V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
MySQL 5.5 Community Server
MySQL 5.6 Community Server
Percona Configuration Wizard
XtraBackup 搭建主从复制
Great Sites on MySQL
Percona
MySQL Performance Blog
Severalnines
推荐管理工具
Sequel Pro
phpMyAdmin
推荐书目
MySQL Cookbook
MySQL 相关项目
MariaDB
Drizzle
参考文档
http://mysql-python.sourceforge.net/MySQLdb.html
455035319
V2EX  ›  MySQL

数据库高频更新问题 谁遇到过没 有啥好的解决方案部

  •  
  •   455035319 · 1 天前 · 3459 次点击

    一个字段金额,一秒可能要更新上百次,就会卡顿死锁,应该怎么处理比较好

    51 条回复    2025-02-07 11:22:55 +08:00
    iyiluo
        1
    iyiluo  
       1 天前
    放内存或者 redis ,定期写入数据库
    GPLer
        2
    GPLer  
       1 天前 via Android
    时序数据库或不可变数据库?反正用插入替代修改应该就能解决。
    mark2025
        3
    mark2025  
       1 天前   ❤️ 1
    1. 放内存或者 redis 。不过保持数据一致性需要额外开销
    2. 数据库存储用本地高速磁盘,比如 NVME
    3. 如果是 pgsql 数据库
    3.1 字段 FILLFACTOR 设定很小值(比如 1 )提高写入性能
    3.2 关闭 full page write 开关(需要磁盘磁层有相应的安全措施,比如 ZFS 文件系统)
    3.3 关闭 sync 同步写入(需要磁盘磁层有相应的安全措施,比如 UPS 、阵列卡电池)
    godall
        4
    godall  
       1 天前   ❤️ 2
    我建议是修改业务逻辑,标准的读写分离:
    1 、update 改为 insert ,插入数据同时更新 redis 。
    2 、增加 redis ,提供读服务

    这样不会有问题,即使突然挂了,数据还在数据库里,重启初始化 redis 就行了。
    455035319
        5
    455035319  
    OP
       1 天前
    @godall
    @mark2025
    @GPLer
    @iyiluo
    感谢大佬们 我试着调整下
    21231sv
        6
    21231sv  
       1 天前   ❤️ 1
    有没有大佬解释下为什么 1 秒更新上百次就会卡顿死锁
    coderzhangsan
        7
    coderzhangsan  
       1 天前
    现在的问题是数据库更新并发太高,数据库负载扛不住;楼上已经给出了问题相关解决方案,我抛砖引玉总结下楼上解决方案思路。

    1. 从降低并发负载角度来看,常规是采取削锋,控制数据库并发流量,例如可以使用消息队列处理,控制好消费流量即可。
    2.从提高并发负载角度来看,使用内存缓存替代传统关系型数据库或内存缓存作为中间层.
    3.从提高数据库行锁并发角度来看,业务策略调整,用写入替代更新,数据量会飙升,后续是否考虑分表或者定时清理数据。
    4. 提升机器配置。
    CEBBCAT
        8
    CEBBCAT  
       1 天前
    可不可以请假下是什么业务场景,怎么压力都堆到 DB 来了?
    CEBBCAT
        9
    CEBBCAT  
       1 天前
    @CEBBCAT #8 oops 粗心打错,“请教下”
    455035319
        10
    455035319  
    OP
       1 天前
    一个购票系统
    @CEBBCAT
    CEBBCAT
        11
    CEBBCAT  
       1 天前
    @455035319 #10 购票系统,单行,RPS 数百,类型为金额,这是动态定价吗家人😂

    但价格频繁变动,用户需要支付的金额也会变化,这场景太抓马了
    skallz
        12
    skallz  
       1 天前
    @CEBBCAT 确实没见过这种,这种高频数据写入场景只在物联网中遇见过,用的也是时序数据库。。。
    lasuar
        13
    lasuar  
       1 天前
    这不跟交易系统一样了,已经不适合用 mysql 实时存储了,都是放内存数据库,再延时落地 mysql 。
    lessMonologue
        14
    lessMonologue  
       1 天前
    @21231sv 抢锁等待更新,占用数据库链接
    vveexx
        15
    vveexx  
       1 天前
    楼上大佬给的方案已经足够多,但是刚又想到一种。如果价格是可预知的,提前生成出来所有可能的值应该也能解决。具体的最好能把场景贴的太详细一点
    qyvlik
        16
    qyvlik  
       1 天前
    热点账号的问题。
    rds 范畴内: 分散更新,合并读取;追加记录,合并更新;如下:
    1. 多个账户,增加资金时哪个空闲用哪个;扣除资金如果不够,还是要锁全部账户。
    2. insert 记录,作为待结算数据,然后再批次更新回主记录。

    非 rds 范畴:
    1. 不考虑用 rds 了,上 redis 等非关系数据库。
    2. 如果怕 redis 丢数据,用 in-memory + kafka ,in-memory 就是直接 java ( rust 也许更快)内存维护最新状态(本地内存可比 redis 快多了),kafka 记录变动流水( kafka 容量可比 redis 大多了)。
    pony2335
        17
    pony2335  
       1 天前
    @godall 这个靠谱
    pony2335
        18
    pony2335  
       1 天前
    @lasuar 写内存不好保持一致性吧。 如果内存突然挂了,交易丢失是很严重的事故。 不是很懂,望讨论。
    zpfhbyx
        19
    zpfhbyx  
       1 天前
    可以维护一个 token 池子 这个池子的 token 可以一次性生成, 可以随时补充, 每个 token 对应一个价格, 然后预扣, 最后统计池子中没用的 token 余额加回就好了..
    lasuar
        20
    lasuar  
       1 天前   ❤️ 1
    @pony2335 #18 既然是写内存,那这种数据肯定是允许丢失的,比如 1s 中变化上百次的字段,丢失其中第 99 次的数据并不影响什么,因为系统恢复后,马上又会收到最新的数据。
    如果发生交易,那交易记录是从内存中获取实时价格,然后记录本身要入持久化数据库的(而不是写内存)。
    miaomiaotu
        21
    miaomiaotu  
       1 天前   ❤️ 3
    设计的就有问题,为啥都要并发修改在数据库层面,正常不是缓存层面加锁控制数量金额等,业务交互在数据库之前走完了,数据库只是记录最终结果。然后再改数据库,而不是直接操作数据库,任何性能问题都是架构层面的问题,不是改个啥代码能解决的,除非屎山代码
    nicoley
        22
    nicoley  
       1 天前
    @miaomiaotu Redis 不是单线程执行的吗,为什么要在缓存层面加锁控制?如果确实要做业务流程编排,感觉可以把这部分的控制放到 Lua 脚本里面去控制。
    nicoley
        23
    nicoley  
       1 天前
    @miaomiaotu 再就是 Redis 和 数据库数据的一致性该如何保证咧 ? 就是存在 Redis 挂了的情况?
    daybreakfangyang
        24
    daybreakfangyang  
       1 天前 via Android
    好奇是什么场景
    mark2025
        25
    mark2025  
       1 天前
    @godall 更新改为插入新记录无法解决并发写入的一致性问题吧:UPDATE SET val+10 ,这个 val 怎么确保是最新记录呢?
    mark2025
        26
    mark2025  
       1 天前
    @coderzhangsan 如果实时读取需求不强,用消息队列是个好办法。
    silentsky
        27
    silentsky  
       1 天前
    用 hazelcast 或 redis
    guanhui07
        28
    guanhui07  
       1 天前
    用 redis 然后 定期刷入 db
    angryfish
        29
    angryfish  
       1 天前
    更新上百次,也是不同的记录吧?理论上是不会卡死的啊?
    是不是更新字段没走索引啊
    angryfish
        30
    angryfish  
       1 天前
    #29 更新的 where 条件没走索引,造成锁表了
    zmal
        31
    zmal  
       1 天前
    把你的问题拿去问 deepseek R1 , 给的答案比评论区大部分方案靠谱。
    v2er119
        32
    v2er119  
       1 天前
    同一个字段没关系,不是同一行的就没问题,降低锁的粒,表到行到页。前提能准确定位,又不产生共享锁。
    linora
        33
    linora  
       1 天前
    业务场景描述太过有限

    说白了就是避免行锁,那只能参考数据库的 latch 和 children latch 设计

    即将一行尽量拆分成多行

    比如 100 块资源池,拆分成 4 行,每行 25 块

    不考虑 delete/insert 的话,纯 update 随机一行

    如果只是一味的累加,风险不大

    否则可能遭遇负数风险,就要引入全局锁定读

    所以还得看场景!看场景!看场景!
    areless
        34
    areless  
       1 天前 via Android
    肯定是股票~期货~外汇~数字货币呗,报价随买卖单对挂单的撮合而动,mysql 做不到的,直接用缓存之类的放内存里面。
    Asheet
        35
    Asheet  
       1 天前
    将多次 update 的行锁请求合并到一次上。比如启动一个异步线程和引入一个时间窗口,对这个窗口内的所有请求扣减金额进行相加,进行一次 update 。然后进行多次 insert 操作记录扣减日志,发生异常根据日志情况进行回滚
    nicoley
        36
    nicoley  
       1 天前
    @Asheet 如果合并请求后预计总共扣减的金额和超过了表里面这个字段上的值,那该怎么处理
    Asheet
        37
    Asheet  
       1 天前
    @nicoley #36 这种情况下退化成串行扣减
    realpg
        38
    realpg  
       1 天前
    换一个技术牛逼点的架构师
    lqw3030
        39
    lqw3030  
       1 天前
    @mark2025 #25 楼主想表达的可能是引入“账单”概念,以此把并发 update 写转化为并发 insert ,从根源上规避了高并发操作同一行的场景,计算余额对所有操作行做累加即可
    lqw3030
        40
    lqw3030  
       1 天前
    @21231sv 更新同一行,行级别的排他锁
    nicoley
        41
    nicoley  
       1 天前
    要是设计表的时候,主键字段设置的是数据库自增,那还能提升写入的并发吗
    sagaxu
        42
    sagaxu  
       1 天前
    确定有死锁,那就排查一下死锁原因,正常情况每秒几百次的单行 update 是吃得消的
    hd7771
        43
    hd7771  
       21 小时 37 分钟前
    我之前在阿里做过 MySQL 内核,这种场景阿里是通过修改 MySQL 源码解决的。具体原理其实就是在 MySQL Server 层排队批量提交多个事务,InnoDB 只用拿一次行锁,提交成功之后在 Server 层构造结果。
    https://github.com/alibaba/AliSQL/wiki/AliSQL-Performance-benchmark-for-inventory
    hd7771
        44
    hd7771  
       21 小时 34 分钟前
    AliSQL 这个源码不建议用了,没人维护的,一定要用,可以用 PolarDB-X Engine 这个代码,我走的时候还有团队在负责。
    miaomiaotu
        45
    miaomiaotu  
       20 小时 46 分钟前
    @nicoley 加分布式锁,先改缓存,缓存成功再改数据库,一致性保证就行,查询什么的走缓存,然后数据库做主主复制或者主从,再加读写分离,这样方案扩展性强,前提是技术到位再加上服务器到位,完美方案,最主要的是把数据库的读压力和写压力分开,数据库在并发读时候出现锁表问题很低,但是加上并发写就会出现,所以最终目的还是数据库读写分离加上压力在缓存层解决,完美,服务器要给力,不然是累赘这个方案
    miaomiaotu
        46
    miaomiaotu  
       20 小时 39 分钟前
    @nicoley 如果架构层次解决不了,只能改业务代码那块,那么就使用乐观锁机制,把事务内部代码减少,尽量一个事务内就是单独修改哪个金额数字,减少事务时间,再加上修改时候尽量走索引这样行锁,避免锁表,这样也可以,但是都是拆东墙补西墙方案,不是最终的,最终的还是之前架构方案
    junwind
        47
    junwind  
       20 小时 11 分钟前
    @21231sv 数据库 io 瓶颈
    whahuzhihao
        48
    whahuzhihao  
       20 小时 4 分钟前
    问题本质是热点数据写

    一种思路是改用 redis 这种能抗热点修改的组件,异步落盘到 DB 。但是针对金额这种敏感字段,不建议这么做,除了需要额外维护一致性,还需要将所有读数据的场景都迁移到 redis 。

    另一种思路是在数据库层面进行合并提交,类似 43 楼的的做法,需要魔改 MySQL 。热点行更新能抗几百一两千并发问题不大。
    areless
        49
    areless  
       20 小时 3 分钟前
    1 秒上百次的写,肯定也得 1 秒上百次读出来,纯 update 写本身毫无意义的。纯数据库方案即便各种方式写进去了,也是读不出来。比如 1 2 3 4 5 6 7 ,读出来就可能是 1 1 1 6 6 。读写都不落数据库是最好的方案,然后再搞个消息队列往数据库里塞海量的价格变动日志,供离线分析。
    xmdbb
        50
    xmdbb  
       20 小时 1 分钟前   ❤️ 1
    @CEBBCAT 感觉是和景区对接,并不是单行过百,而是该字段更新频率过百。国内这个体系的盲猜长隆集团对接
    MoYi123
        51
    MoYi123  
       19 小时 51 分钟前
    只是高频更新不会导致死锁, 建议先把死锁的问题修好再看.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1079 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 23:14 · PVG 07:14 · LAX 15:14 · JFK 18:14
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.