thinkphp6 加锁查询语句阻塞导致应用缓慢,除了看代码外还能从哪里入手排查和解决?

121 天前
 UnrealEngine

//扣减余额前查用户余额 $fundModel = new MemberFund(); $fund = $fundModel->field("available_balance") ->where("memberid", $memberId) ->lock(true) ->find();

看了 php 慢日志是这个加锁的 sql 卡住了

除了看代码外还有哪些排查和解决手段呢?

1137 次点击
所在节点    PHP
9 条回复
akin520
121 天前
把 SQL 输入,去数据库运行 explain 试试,看看是什么问题
bixchen
121 天前
把所有的用户余额操作放入异步队列完成,或者用 redis 锁
kestrelBright
121 天前
innodb 的表更新时如果作为条件的字段没有主键或索引,会引发表锁而不是行锁
rekulas
121 天前
单纯这个查询不大可能导致问题,重点排查该逻辑执行完毕后是否及时释放事务,是否跟其他逻辑有死锁冲突等情况,从数据库日志和事务记录应该能看出来
UnrealEngine
121 天前
@kestrelBright #3 主键这块没问题,应该是其他事务执行太久导致应用阻塞了
UnrealEngine
121 天前
@bixchen #2 这么操作可以解决根源但是要把整个项目中的余额操作相关代码都要全部改一遍……

我在想要么直接加个不加 lock 的查余额方法得了

因为我全局搜索了下整个项目,有的代码查余额时加了 lock 有的没加 lock

摆烂……
coderzhangsan
120 天前
1.你的这个 SQL 加锁属于悲观锁,即 select {fileds} from {table} where {where} for update; 该锁的事务性操作并发能力不高。

2.你的 where 条件如果索引是唯一键,即是行锁,如果是普通索引,则是间隙锁(影响范围是个区间),没有锁的行则是表锁,并发能力从高到低:行锁>间隙锁>表锁。

3.如果是行锁的话,说明你的业务并发值超出悲观锁事务的并发临界值,这个可以查看数据库日志,查询有无死锁或锁超时日志记录,解决方法,个人建议如下:
a.事务不使用悲观锁处理余额,事务直接使用 update 来完成余额更新,即 update fund set available_balance=available_balance-{money} where memberid = {memberId} and available_balance > available_balance-{money},并发能力要高于悲观锁
b.比较笨的方法,提升主库配置,例如提升 CPU 核心数,核数可以提高并发临界值。

参考下 V 站这篇帖子: https://v2ex.com/t/997702
UnrealEngine
120 天前
@coderzhangsan #7 我看项目很多地方的余额查询代码即便没有用到事务也在 select 语句后边加上了 for update ,这样会有什么问题?
coderzhangsan
120 天前
@UnrealEngine #8 select for update 是悲观锁 SQL 语句,目的是为查询条件对应的 SQL 加锁,不声明开启事务,mysql 默认 sql 提交方式为隐式提交,对于非 DQL 的 SQL 语句来讲,例如 insert/update\delete 等 DML 或 DDLSQL 语句来讲,事务跟随 SQL 自动提交,也就是不手动声明开启事务,for update 语句提交结束后,锁就释放了,这样做其实对业务来讲没什么意义,for update 一般伴随业务数据更改,保证一致性,所以需要放在一个完整的事务中执行。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1007182

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX