高并发下的mysql问题
mysql 重复插入问题
业务需要先根据where条件查询,如有数据命中对其进行修改否则创建一条新的记录。实践中发现:该操作在遇到并发处理时会发生重复创建
解决方式
添加唯一索引或多列唯一索引,这样当有重复创建时数据库报错,交由程序处理。这种处理方式为乐观锁,适合冲突发生率比较低的情况。
mysql set update 并发脏读问题
对一条数据进行累加时常常先使用select语句查询,然后在查询的数值上加上一个数值,在update 更新回数据库。 这样操作在并发处理中会出现脏读问题,例如:两个进程同时处理相同一条数据,同时读取了当前的数值为200,进程1处理的比较快在2000的基础上加上了3000(2000 + 3000),更新回数据库(此时数据为5000),进程2也在原2000基础上加上了2000(2000 + 2000),也更新会数据库。最终数据库数值为4000,正确的应为2000+3000+2000=7000.
解决方法
对数据加独占锁:
共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。
排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE。
在这个问题中,我们采用排他锁对需要修改的数据加锁,这样在整个查询、修改的事务执行过程中,其他事务将无法读取数据,直到该事务处理完毕。这种操作方式为悲观锁。 将并行处理改为串行处理。
SQLAlchemy 的 Query 支持 select ... for update / share .
session.Query(User).with_for_update().first()
session.Query(User).with_for_update(read=True).first()
完整形式是:
with_for_update(read=False, nowait=False, of=None)
read
是标识加互斥锁还是共享锁. 当为 True 时, 即 for share 的语句, 是共享锁. 多个事务可以获取共享锁, 互斥锁只能一个事务获取. 有"多个地方"都希望是"这段时间我获取的数据不能被修改, 我也不会改", 那么只能使用共享锁.
nowait
其它事务碰到锁, 是否不等待直接"报错".
of
指明上锁的表, 如果不指明, 则查询中涉及的所有表(行)都会加锁.