//扣减余额前查用户余额 $fundModel = new MemberFund(); $fund = $fundModel->field("available_balance") ->where("memberid", $memberId) ->lock(true) ->find();
看了 php 慢日志是这个加锁的 sql 卡住了
除了看代码外还有哪些排查和解决手段呢?
1
akin520 324 天前
把 SQL 输入,去数据库运行 explain 试试,看看是什么问题
|
2
bixchen 324 天前
把所有的用户余额操作放入异步队列完成,或者用 redis 锁
|
3
kestrelBright 324 天前
innodb 的表更新时如果作为条件的字段没有主键或索引,会引发表锁而不是行锁
|
4
rekulas 324 天前
单纯这个查询不大可能导致问题,重点排查该逻辑执行完毕后是否及时释放事务,是否跟其他逻辑有死锁冲突等情况,从数据库日志和事务记录应该能看出来
|
5
UnrealEngine OP @kestrelBright #3 主键这块没问题,应该是其他事务执行太久导致应用阻塞了
|
6
UnrealEngine OP @bixchen #2 这么操作可以解决根源但是要把整个项目中的余额操作相关代码都要全部改一遍……
我在想要么直接加个不加 lock 的查余额方法得了 因为我全局搜索了下整个项目,有的代码查余额时加了 lock 有的没加 lock 摆烂…… |
7
coderzhangsan 323 天前 1
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 |
8
UnrealEngine OP @coderzhangsan #7 我看项目很多地方的余额查询代码即便没有用到事务也在 select 语句后边加上了 for update ,这样会有什么问题?
|
9
coderzhangsan 323 天前 1
@UnrealEngine #8 select for update 是悲观锁 SQL 语句,目的是为查询条件对应的 SQL 加锁,不声明开启事务,mysql 默认 sql 提交方式为隐式提交,对于非 DQL 的 SQL 语句来讲,例如 insert/update\delete 等 DML 或 DDLSQL 语句来讲,事务跟随 SQL 自动提交,也就是不手动声明开启事务,for update 语句提交结束后,锁就释放了,这样做其实对业务来讲没什么意义,for update 一般伴随业务数据更改,保证一致性,所以需要放在一个完整的事务中执行。
|