Use of Python MySQL database connection driver aiomysql

Keywords: Python

In the last blog post, I introduced the use of MySQL database to obtain pymysql. For reference: https://www.cnblogs.com/minseo/p/15597428.html

This article introduces the use of asynchronous MySQL asynchronous driver aiomysql

1. Install asynchronous module

If there is no module, install the module using pip first

pip3 install asyncio
pip3 install aiomysql

2. Create MySQL database connection pool

Unlike the synchronous method, you cannot directly create a database connection conn using asynchronous. You need to create a database connection pool object first__ Pool creates database connections through this database connection pool object

The database configuration information is the same as the database used for pymysql synchronization

import asyncio,aiomysql,time
# Database configuration dict
db_config = {
    'host': 'localhost',
    'user': 'www-data',
    'password': 'www-data',
    'db': 'awesome'
}

# Create database connection pool coprocessor function
async def create_pool(**kw):
    global __pool
    __pool = await aiomysql.create_pool(
        host=kw.get('host', 'localhost'),
        port=kw.get('port', 3306),
        user=kw['user'],
        password=kw['password'],
        db=kw['db']
    )

loop=asyncio.get_event_loop()
loop.run_until_complete(create_pool(**db_config))
# The global variable is generated when the coprocessor function is run in the event loop__ Pool is a connection pool object < aiomysql. Pool. Pool object at 0x00000244ad1724c8 >
print(__pool)
# <aiomysql.pool.Pool object at 0x00000244AD1724C8>

3. Create a coprocessor function to execute sql statements

Because it is an asynchronous module, it can only be called through the await keyword in the event loop, using the coprocessor function that needs to be created to execute sql statements

Use the connection pool object created in the previous step to create the connection conn and the buoy object cur in the coroutine function. Execute the sql statement through the buoy object. The execution method is the same as that of the pymysql module

cursor.execute(sql,args)
sql # sql statements to be executed, such as' select * from table '_ name'
args # Replace the formatted string of the sql statement, that is, the sql statement can use% s to represent a string, and then replace it with the corresponding variable or parameter in args. Args is a list or tuple, that is, an ordered sequence, which needs to be one-to-one corresponding to% s in sql
# For example, sql='select * from table_name where id=%s'  args=['12345']
# It is equivalent to replacing% s in sql with parameters in args
# select * from table_name where id='12345'

Next, create two coroutine functions select execute, one to perform search operations and the other to perform insert, update, delete and other modification operations

# Execute the select function
async def select(sql,args,size=None):
    with await __pool as conn:
        cur = await conn.cursor(aiomysql.DictCursor)
        await cur.execute(sql.replace('?','?s'),args or ())
        if size:
            rs = await cur.fetchmany(size)
        else:
            rs = await cur.fetchall()
        await cur.close()
        return rs


# Execute the insert update delete function
async def execute(sql,args):
    with await __pool as conn:
        try:
            cur = await conn.cursor()
            await cur.execute(sql.replace('?','%s'),args)
            affetced = cur.rowcount
            await conn.commit()
            await cur.close()
        except BaseException as e:
            raise
        return affetced

4. Execute sql statements in practice

Before implementing sql statements, we first create a database and corresponding tables on the machine for testing

The host, user name, password, database name and table name corresponding to the database are as follows

host: localhost
user: www-data
password: www-data
db: awesome
table_name: users

The sql statement to create the table name is as follows. You need to create the corresponding table in the database

CREATE TABLE `users` (
  `id` varchar(50) NOT NULL,
  `email` varchar(50) NOT NULL,
  `passwd` varchar(50) NOT NULL,
  `admin` tinyint(1) NOT NULL,
  `name` varchar(50) NOT NULL,
  `image` varchar(500) NOT NULL,
  `created_at` double NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_email` (`email`),
  KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

The corresponding structure of the created table is as follows

mysql> desc users;
+------------+--------------+------+-----+---------+-------+
| Field      | Type         | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| id         | varchar(50)  | NO   | PRI | NULL    |       |
| email      | varchar(50)  | NO   | UNI | NULL    |       |
| passwd     | varchar(50)  | NO   |     | NULL    |       |
| admin      | tinyint(1)   | NO   |     | NULL    |       |
| name       | varchar(50)  | NO   |     | NULL    |       |
| image      | varchar(500) | NO   |     | NULL    |       |
| created_at | double       | NO   | MUL | NULL    |       |
+------------+--------------+------+-----+---------+-------+
7 rows in set (2.68 sec)

① execute the insert operation

# insert start
import time
sql = 'insert into `users` (`email`, `passwd`, `admin`, `name`, `image`, `created_at`, `id`) values (?, ?, ?, ?, ?, ?, ?)'
args = ['test@qq.com','password',1,'test','about:blank',time.time(),'111111']
async def insert():
    await execute(sql,args)
loop.run_until_complete(insert())
# insert end

The execution method is no different from that of pymysql. The difference is that you need to use the keyword await call in the event loop

After execution, view the inserted data in MySQL

mysql> select * from users;
+--------+-------------+----------+-------+------+-------------+------------------+
| id     | email       | passwd   | admin | name | image       | created_at       |
+--------+-------------+----------+-------+------+-------------+------------------+
| 111111 | test@qq.com | password |     1 | test | about:blank | 1637738541.48629 |
+--------+-------------+----------+-------+------+-------------+------------------+
1 row in set (0.00 sec)

② execute the update operation

You can also execute the execute function directly in the loop event loop

# update start
import time
sql = 'update `users` set `email`=?, `passwd`=?, `admin`=?, `name`=?, `image`=?, `created_at`=? where `id`=?'
args = ['test2@qq.com','password',1,'test2','about:blank',time.time(),'111111']
loop.run_until_complete(execute(sql,args))
# update end

After execution, both email and name were modified

③ execute the delete operation

# delete start
sql = 'delete from `users` where `id`=?'
args = ['111111'] 
loop.run_until_complete(execute(sql,args))
# delete end

This data is also deleted according to the value specified by the keyword id

④ execute selete operation

Before performing the select operation, we ensure that there is at least one piece of data in the database

# select start
sql = 'select * from users'
args = []
rs = loop.run_until_complete(select(sql,args))
print(rs)
# select end

Here, the coroutine function select that directly performs the search returns the list of all results according to the definition of the function, and the element is the dictionary of the query results

Output as

[{'id': '111111', 'email': 'test@qq.com', 'passwd': 'password', 'admin': 1, 'name': 'test', 'image': 'about:blank', 'created_at': 1637739212.74493}]

If there are multiple results, use the subscript of the list to get them

  

Supplement

Comparison of execution speed between synchronous module pymysql and asynchronous module aiomysql

If we need to insert 20000 pieces of data into the database, we use synchronous mode and asynchronous mode respectively

First, delete all test data in the database

delete from users;

Synchronized code

d:/learn-python3 / learning script / pymysql/use_pymysql.py

import pymysql
db_config = {
    'host': 'localhost',
    'user': 'www-data',
    'password': 'www-data',
    'db': 'awesome'
}
# Creating a connection is equivalent to passing key value pairs in the dictionary
# It is equivalent to executing pymysql.connect(host='localhost',user='www-data',password='www-data',db='awesome')
conn = pymysql.connect(**db_config)
# Create cursor
cursor = conn.cursor(pymysql.cursors.DictCursor)
sql = 'select * from users'
args = []
# Number of results returned by executing query
# Execute query
rs=cursor.execute(sql,args)
# Get query results
# Get the first result of the query and return a dict. The dict element is the key value pair corresponding to the query
# If there are multiple query results, execute once, move the cursor to the next data, and return another data after execution
# print(cursor.fetchone())
# print(cursor.fetchone())
# print(cursor.fetchall())
# print(cursor.fetchmany())
# {'id': '111111', 'email': 'test@qq.com', 'passwd': 'password', 'admin': 1, 'name': 'test', 'image': 'about:blank', 'created_at': 1637723578.5734}
# Get all the results of the query and return a list. The list element is dict, and the dict element is the key value pair corresponding to the query
# print(cursor.fetchall())
# [{'id': '111111', 'email': 'test@qq.com', 'passwd': 'password', 'admin': 1, 'name': 'test', 'image': 'about:blank', 'created_at': 1637723578.5734}]
# Get the first few results of the query and return a dict. The dict element is the key value pair corresponding to the query
# print(cursor.fetchmany(1))
# [{'id': '111111', 'email': 'test@qq.com', 'passwd': 'password', 'admin': 1, 'name': 'test', 'image': 'about:blank', 'created_at': 1637723578.5734}]
# Perform modification
import time
# # insert start
sql = 'insert into `users` (`email`, `passwd`, `admin`, `name`, `image`, `created_at`, `id`) values (?, ?, ?, ?, ?, ?, ?)'
args = ['test1@qq.com','password',1,'test','about:blank',time.time(),'1111121']
# Use replace to replace '?' Replace with '% s'
# rs = cursor.execute(sql.replace('?','%s'),args)
# print(cursor.rowcount)
# conn.commit()
# print(rs)
# insert end

# update start
# sql = 'update `users` set `email`=?, `passwd`=?, `admin`=?, `name`=?, `image`=?, `created_at`=? where `id`=?'
# args = ['test2@qq.com','password',1,'test2','about:blank',time.time(),'111111'] 
# print(cursor.execute(sql.replace('?','%s'),args))
# conn.commit()
# update end

# delete start
# sql = 'delete from `users` where `id`=?'
# args = ['111111'] 
# print(cursor.execute(sql.replace('?','%s'),args))
# conn.commit()
# delete end


# Written as a function call, the database connection object conn is used inside the function
# It can be defined as global variable first
def select(sql,args,size=None):
    
    cursor =  conn.cursor(pymysql.cursors.DictCursor)
    cursor.execute(sql.replace('?','%s'),args or ())
    if size:
        rs = cursor.fetchmany(size)
    else:
        rs = cursor.fetchall()
    cursor.close
    # logging.info('rows returned: %s' % len(rs))
    return rs
 
def execute(sql,args):
     
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    try:
        cursor.execute(sql.replace('?','%s'),args)
        # The rowcount method returns the influence function
        rs = cursor.rowcount
        cursor.close()
        conn.commit()
    except:
        raise
    return rs

start_time = time.time()
for n in range(20000):
    sql = 'insert into `users` (`email`, `passwd`, `admin`, `name`, `image`, `created_at`, `id`) values (?, ?, ?, ?, ?, ?, ?)'
    email = 'test%s@qq.com' %n
    args = [email,'password',1,'test','about:blank',time.time(),n] 
    execute(sql,args)
end_time = time.time()
# Difference between print start and end times
print(end_time - start_time)

We use a cycle of 20000 times to insert data into the database

Execute. There are too many inserted data and you need to wait for a period of time for output

D:\learn-python3\Functional programming>C:/Python37/python.exe d:/learn-python3/Learning script/pymysql/use_pymysql.py
77.46903562545776

The 20000 pieces of data can be queried in the database, and the field of the table is created_at stores the time stamp of creating this data. We can see that the later the id, the later the time stamp, indicating that the data is inserted one by one synchronously

We created according to the field_ At sort query

 

 

Let's delete all data and insert it asynchronously

The asynchronous code is as follows

D: / learn-python 3 / learning script / aiomysql/use_aiomysql.py

import asyncio,aiomysql,time
# Database configuration dict
db_config = {
    'host': 'localhost',
    'user': 'www-data',
    'password': 'www-data',
    'db': 'awesome'
}

# Create database connection pool coprocessor function
async def create_pool(**kw):
    global __pool
    __pool = await aiomysql.create_pool(
        host=kw.get('host', 'localhost'),
        port=kw.get('port', 3306),
        user=kw['user'],
        password=kw['password'],
        db=kw['db']
    )

loop=asyncio.get_event_loop()
loop.run_until_complete(create_pool(**db_config))
# The global variable is generated when the coprocessor function is run in the event loop__ Pool is a connection pool object < aiomysql. Pool. Pool object at 0x00000244ad1724c8 >
print(__pool)
# <aiomysql.pool.Pool object at 0x00000244AD1724C8>

# Execute the select function
async def select(sql,args,size=None):
    with await __pool as conn:
        cur = await conn.cursor(aiomysql.DictCursor)
        await cur.execute(sql.replace('?','?s'),args or ())
        if size:
            rs = await cur.fetchmany(size)
        else:
            rs = await cur.fetchall()
        await cur.close()
        return rs


# Execute the insert update delete function
async def execute(sql,args):
    with await __pool as conn:
        try:
            cur = await conn.cursor()
            await cur.execute(sql.replace('?','%s'),args)
            affetced = cur.rowcount
            await conn.commit()
            await cur.close()
        except BaseException as e:
            raise
        return affetced

# insert start
# import time
# sql = 'insert into `users` (`email`, `passwd`, `admin`, `name`, `image`, `created_at`, `id`) values (?, ?, ?, ?, ?, ?, ?)'
# args = ['test@qq.com','password',1,'test','about:blank',time.time(),'111111']
# async def insert():
#     await execute(sql,args)
# loop.run_until_complete(insert())
# insert end

# update start
# import time
# sql = 'update `users` set `email`=?, `passwd`=?, `admin`=?, `name`=?, `image`=?, `created_at`=? where `id`=?'
# args = ['test2@qq.com','password',1,'test2','about:blank',time.time(),'111111']
# loop.run_until_complete(execute(sql,args))

# update end

# delete start
# sql = 'delete from `users` where `id`=?'
# args = ['111111'] 
# loop.run_until_complete(execute(sql,args))
# delete end

# select start
# sql = 'select * from users'
# args = []
# rs = loop.run_until_complete(select(sql,args))
# print(rs)
# select end

async def insert1():
     for n in range(10000):
        sql = 'insert into `users` (`email`, `passwd`, `admin`, `name`, `image`, `created_at`, `id`) values (?, ?, ?, ?, ?, ?, ?)'
        email = 'test%s@qq.com' %n
        args = [email,'password',1,'test','about:blank',time.time(),n] 
        await execute(sql,args)

async def insert2():
     for n in range(10001,20001):
        sql = 'insert into `users` (`email`, `passwd`, `admin`, `name`, `image`, `created_at`, `id`) values (?, ?, ?, ?, ?, ?, ?)'
        email = 'test%s@qq.com' %n
        args = [email,'password',1,'test','about:blank',time.time(),n] 
        await execute(sql,args)

async def main():
    # It needs to be combined into one event to execute asynchronously, that is, insert2 is executed synchronously when insert1 is executed
    await asyncio.gather(insert1(),insert2())

start_time = time.time()
loop.run_until_complete(main())
end_time = time.time()
print(end_time - start_time)

Here, we define two coprocessor functions, which are used to insert the first 10000 data and the last 10000 data respectively. The two coprocessor functions are combined into an event loop in main()

After waiting for a period of time, the execution output is as follows. Ignoring this warning, you can see that the execution time is significantly shorter than the synchronization time

d:/learn-python3/Learning script/aiomysql/use_aiomysql.py:42: DeprecationWarning: with await pool as conn deprecated, useasync with pool.acquire() as conn instead
  with await __pool as conn:
39.794615507125854

Let's go to the database to query the data. We can also see that the id starts from 0 and the id starts from 10001 almost at the same time

 

 

  

Posted by Soumen on Wed, 24 Nov 2021 08:10:33 -0800