thinkphp6 加锁查询语句阻塞导致应用缓慢,除了看代码外还能从哪里入手排查和解决? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
UnrealEngine
V2EX    PHP

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

  •  
  •   UnrealEngine 2024-01-09 15:01:28 +08:00 1809 次点击
    这是一个创建于 641 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

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

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

    摆烂……
    coderzhangsan
        7
    coderzhangsan  
       2024-01-10 11:40:20 +08:00   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
    UnrealEngine
        8
    UnrealEngine  
    OP
       2024-01-10 12:27:50 +08:00
    @coderzhangsan #7 我看项目很多地方的余额查询代码即便没有用到事务也在 select 语句后边加上了 for update ,这样会有什么问题?
    coderzhangsan
        9
    coderzhangsan  
       2024-01-10 14:12:29 +08:00   1
    @UnrealEngine #8 select for update 是悲观锁 SQL 语句,目的是为查询条件对应的 SQL 加锁,不声明开启事务,mysql 默认 sql 提交方式为隐式提交,对于非 DQL 的 SQL 语句来讲,例如 insert/update\delete 等 DML 或 DDLSQL 语句来讲,事务跟随 SQL 自动提交,也就是不手动声明开启事务,for update 语句提交结束后,锁就释放了,这样做其实对业务来讲没什么意义,for update 一般伴随业务数据更改,保证一致性,所以需要放在一个完整的事务中执行。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2674 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 13:47 PVG 21:47 LAX 06:47 JFK 09:47
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86