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
