python flask 数据库乐观锁

flask 乐观锁和悲观锁

对于处理数据库并发问题时,尤其是电商平台,很容易发生商品超卖的问题,因为两次或多次并发购买行为获取的库存时一致的。这导致商品发生超卖的现象,这里通常我们使用锁机制来控制,防止数据库高并发导致数据出现问题的现象。

悲观锁

每次更新数据时可以限制数据库中某个表或者表中某个字段被多个线程修改

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
2
3
4
5
6
7
8
9
10
11
12
# 类视图 (并发,悲观锁)
class MyView(View):

@transaction.atomic
def post(self, request):
# select * from 表名 where id=1 for update;
# for update 就表示锁,只有获取到锁才会执行查询,否则阻塞等待。
obj = 模型类名.objects.select_for_update().get(id=1)

# 等事务提交后,会自动释放锁。

return HttpResponse('ok')

sqlalchemy使用悲观锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
db.session.Query(User).with_for_update().first()

可用参数
read

是标识加互斥锁还是共享锁. 当为 True 时, 即 for share 的语句, 是共享锁. 多个事务可以获取共享锁, 互斥锁只能一个事务获取. 有"多个地方"都希望是"这段时间我获取的数据不能被修改, 我也不会改", 那么只能使用共享锁.


nowait

其它事务碰到锁, 是否不等待直接”报错”.

of

指明上锁的表, 如果不指明, 则查询中涉及的所有表(行)都会加锁.

需要注意的是 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
2
3
django例子

GoodsSKU.objects.filter(id=1, stock=orgin_stock).update(stock=new_stock, sales=new_sales)

mysql默认的是Repeatable read,那么你将不能读取其他事务是否提交了新的数据更新,所以你需要更改为Read committed——读取所有已提交的数据。

修改办法

1
2
3
cd /etc/mysql/mysql.conf.d  
vim mysqld.cnf
transaction-isolation=READ-COMMITED
1
2
3
4
5
6
7
Serializable 串行化,一个事务一个事务的执行

Repeatable read 可重复读,无论其他事务是否修改并提交了数据,在这个事务中看到的数据值始终不受其他事务影响

Read committed 读取已提交,其他事务提交了对数据的修改后,本事务就能读取到修改后的数据值

Read uncommitted 读取为提交,其他事务只要修改了数据,即使未提交,本事务也能看到修改后的数据值。

悲观锁和乐观锁使用场景

悲观锁|写入频繁
|-|-|
乐观锁|读取频繁

在并发比较少时建议使用乐观锁,减少加锁、释放锁的开销。在并发比较高的时候,建议使用悲观锁。
如果乐观锁多次尝试的代价比较大,也建议使用悲观锁。

对于不是关键的数据读取时不要加锁,不要乱用with_for_update()

使用with_for_update()如果有事务正在更新,另一个事务只能等待

小伟科技 wechat
python爱好者公众号—每日学习python必备
欢迎打赏支持!