sqlalchemy中多线程下,读取数据库信息同时修改数据库信息可能会出现幻读。
创建数据库
from flask_sqlalchemy import SQLAlchemy from flask import Flask app = Flask(__name__) app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:root@localhost:3306/test" # 数据库要先创建 app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False app.config["SECRET_KEY"] = "DsdfFHERsdadad234+J" db = SQLAlchemy(app) class Stu(db.Model): __tablename__ = "student" # 自动生成表,表名为student id = db.Column(db.Integer, primary_key=True) # 创建一个整数型的主键 name = db.Column(db.String(20), nullable=False) select_num = db.Column(db.Integer, nullable=False) if __name__ == '__main__': db.create_all()
创建一个案例
from model import Stu,db stu = Stu() stu.name = "张三" stu.select_num = 0 db.session.add(stu) db.session.commit()
from model import Stu, db from threading import Thread def add_execute_times(): student = Stu.query.filter(Stu.id == 1).one() student.select_num += 1 print(student.select_num) stu = Stu.query.filter(Stu.id == 1).update({"select_num": student.select_num}) db.session.commit() thread_list = [] for i in range(10): t = Thread(target=add_execute_times) thread_list.append(t) for i in thread_list: i.start() for i in thread_list: i.join()
输出结果:
1
11
1
1
1
11
1
2
为了保证读取数据的原子性,可以采用锁来控制数据库的读取。
在sqlalchemy中,with_for_update(self, read=False, nowait=False, of=None)语句可以为数据库添加锁。
- read:是标识加互斥锁还是共享锁. 当为 True 时, 即 for share 的语句, 是共享锁. 多个事务可以获取共享锁, 互斥锁只能一个事务获取. 有"多个地方"都希望是"这段时间我获取的数据不能被修改, 我也不会改", 那么只能使用共享锁.
- nowait:其它事务碰到锁, 是否不等待直接"报错".
- of:指明上锁的表, 如果不指明, 则查询中涉及的所有表(行)都会加锁.
from model import Stu, db
from threading import Thread
def add_execute_times():
student = Stu.query.with_for_update(read=False, nowait=False).filter(Stu.id == 1).one()
#添加锁with_for_update,锁会在commit时打开
student.select_num += 1
print(student.select_num)
stu = Stu.query.filter(Stu.id == 1).update({"select_num": student.select_num})
db.session.commit()
thread_list = []
for i in range(10):
t = Thread(target=add_execute_times)
thread_list.append(t)
for i in thread_list:
i.start()
for i in thread_list:
i.join()
输出结果:
1
2
3
4
5
6
7
8
9
10