对于处理数据库并发问题时,尤其是电商平台,很容易发生商品超卖的问题,因为两次或多次并发购买行为获取的库存时一致的。这导致商品发生超卖的现象,这里通常我们使用锁机制来控制,防止数据库高并发导致数据出现问题的现象。
悲观锁
每次更新数据时可以限制数据库中某个表或者表中某个字段被多个线程修改
primary key和index都是走索引,导致行锁
其余都是表锁
不是全表扫描的都是行锁
select math from zje where math>60 for update
表锁,悲观锁
select math from zje where id>60 for update
行锁,悲观锁
悲观锁就是sql里面带有for update
django悲观锁
1 | # 类视图 (并发,悲观锁) |
sqlalchemy使用悲观锁
1 | db.session.Query(User).with_for_update().first() |
需要注意的是 read、nowait和of参数不支持mysql
功能|支持的数据库
|-|-|
FOR UPDATE NOWAIT |Oracle and Postgresql
FOR SHARE |Postgresql
FOR UPDATE OF |PostgreSQL and Oracle
mysql 不支持这几个参数,转成sql都是:
SELECT users.id AS users_id FROM users FOR UPDATE
乐观锁
乐观锁是在提交更新时判断是否库存和之前的有变化,有变化则失败
1 | django例子 |
mysql默认的是Repeatable read,那么你将不能读取其他事务是否提交了新的数据更新,所以你需要更改为Read committed——读取所有已提交的数据。
修改办法
1 | cd /etc/mysql/mysql.conf.d |
1 | Serializable 串行化,一个事务一个事务的执行 |
悲观锁和乐观锁使用场景
悲观锁|写入频繁
|-|-|
乐观锁|读取频繁
在并发比较少时建议使用乐观锁,减少加锁、释放锁的开销。在并发比较高的时候,建议使用悲观锁。
如果乐观锁多次尝试的代价比较大,也建议使用悲观锁。
对于不是关键的数据读取时不要加锁,不要乱用with_for_update()
使用with_for_update()
如果有事务正在更新,另一个事务只能等待