Chapter5_非阻塞数据库驱动程序

1、如何理解如下代码,尤其是协程部分【详细解释 insert_brands 函数】。[example5_5.py]

import asyncpg
import asyncio
from typing import List, Tuple, Union
from random import sample

def load_common_words() -> List[str]: with open('common_words.txt') as common_words: return common_words.readlines() def generate_brand_names(words: List[str]) -> List[Tuple[Union[str, ]]]: return [(words[index],) for index in sample(range(100), 100)] async def insert_brands(common_words, connection) -> int: brands = generate_brand_names(common_words) insert_brands = "INSERT INTO brand VALUES(DEFAULT, $1)" return await connection.executemany(insert_brands, brands) async def main(): common_words = load_common_words() connection = await asyncpg.connect(host='127.0.0.1', port=5432, user='postgres', database='products', password='password') await insert_brands(common_words, connection) asyncio.run(main())

这个函数的作用是将生成的品牌名称列表插入到数据库中的 brand 表中。下面是逐行解释,尤其关注最后两行,特别是 executemany 函数的 API 含义。

函数签名

async def insert_brands(common_words, connection) -> int:
  • async def: 这是一个异步函数,意味着它可以在执行中等待其他异步操作完成。
  • common_words: 一个单词列表,用于生成品牌名称。
  • connection: 一个异步 PostgreSQL 数据库连接对象。
  • -> int: 指定函数返回一个整数。

生成品牌名称

brands = generate_brand_names(common_words)
  • 调用 generate_brand_names 函数,将 common_words 列表传递给它。
  • generate_brand_names 返回一个包含品牌名称的列表 brands,每个品牌名称是一个单元素元组。

SQL 插入语句

insert_brands = "INSERT INTO brand VALUES(DEFAULT, $1)"
  • 定义 SQL 插入语句 insert_brands
  • DEFAULT: 用于自动生成 ID 或其他默认值。
  • $1: 占位符,表示将传递的值插入到该位置。

异步执行插入操作

return await connection.executemany(insert_brands, brands)
  • await: 等待异步操作完成。在等待期间,事件循环可以执行其他任务。
  • connection.executemany: 异步执行多条 SQL 插入语句。
    • insert_brands: SQL 插入语句。
    • brands: 要插入的品牌名称列表,每个元素都是一个单元素元组,对应 SQL 语句中的 $1 占位符。

executemany 函数的 API 含义

executemany 是 asyncpg 库中的一个方法,用于执行多条 SQL 语句。它的签名如下:

asyncpg.Connection.executemany(command: str, args: Iterable[Iterable[Any]]) -> str
  • command: str: 要执行的 SQL 命令字符串,可以包含一个或多个占位符(如 $1, $2)。
  • args: Iterable[Iterable[Any]]: 一个可迭代对象,其中每个元素是一个包含参数的可迭代对象,对应 SQL 命令中的占位符
  • 返回值: str,表示已执行的命令(通常是执行的 SQL 语句)。

在你的代码中,executemany 方法执行如下:

  • insert_brands 是 SQL 插入语句 "INSERT INTO brand VALUES(DEFAULT, $1)"
  • brands 是包含品牌名称的列表,每个品牌名称是一个单元素元组 [(brand1,), (brand2,), ..., (brand100,)]

executemany 方法会将 brands 中的每个元组的值插入到 insert_brands 语句中的 $1 位置,并异步地将这些语句发送到数据库执行。这种方法可以高效地批量插入数据,而不是一次一条地插入。

完整的执行过程

  1. 生成品牌名称:

brands = generate_brand_names(common_words)
  1. 生成一个包含品牌名称的列表 brands

  2. 定义 SQL 插入语句:

insert_brands = "INSERT INTO brand VALUES(DEFAULT, $1)"
  1. SQL 插入语句,使用 $1 作为占位符。

  2. 异步批量插入:

return await connection.executemany(insert_brands, brands)
  1. 异步执行 executemany 方法,将 brands 中的每个品牌名称插入到 brand 表中。

这种设计允许在插入大量数据时更高效,因为它减少了数据库交互的次数,通过批量插入提高了性能。

 

2、如何理解SQL的注入攻击?

 

 

3、理解如下代码,以及不能并发执行的原因。然后优化它,使其能够并行。[from example5_14.py]

import asyncio
from util import delay, async_timed


async def positive_integers_async(until: int):
    for integer in range(1, until):
        """That's the key reason which example5_14.py running as serial execution, instead of concurrent execution"""
        await delay(integer)
        yield integer


@async_timed()
async def main():
    async_generator = positive_integers_async(3)
    print(type(async_generator))
    async for number in async_generator:
        print(f'Got number {number}')


asyncio.run(main())

优化后:

import asyncio
from util import delay, async_timed

async def positive_integers_async(until: int):
    tasks = []
    for integer in range(1, until):
        task = asyncio.create_task(delay_and_yield(integer))
        tasks.append(task)
    results = await asyncio.gather(*tasks)  # result: list
    print(type(results))
    for result in results:  # can't use async for, because result is a list, not async_generator.
        yield result  # yield -> generator


async def delay_and_yield(integer):
    await delay(integer)
    return integer


@async_timed()
async def main():
    async_generator = positive_integers_async(4)
    print(type(async_generator))
    async for number in async_generator:  # async can use here, because async_generator is <class 'async_generator'>
        print(f'Got number {number}')

asyncio.run(main())

 

2

 

posted @ 2024-05-18 22:21  AlphaGeek  阅读(23)  评论(0)    收藏  举报