前言
阅读本文需要对OLTP数据库里的行锁(文章所描述的是Mysql InnoDB)有基本的认识。
文章分为上下两篇,上篇讲述热点行的基础信息,下篇我将从多个维度讲述解决(延缓)所造成影响的方法思路。
由于笔者水平有限,欢迎各位大佬批评指正。
什么是热点行
定义
数据库中那些会被频繁执行增删改查操作的数据行称为热点行。当一个事务对一行数据进行更新时,会对目标数据行加锁,直到事务提交或回滚时才释放。同一时段内,对于同一个数据行,只有一个事务能够进行更新,其它事务需要等待。
结合业务场景举例
电商平台中,存在少数大商家(20%的商家占据了平台80%的销量-根据二八原则推测,同理20%商品占据了百分之80的销售额等),拿耐克旗舰店来说,在一个很普通的日子里,没有大促的情况下,账户每秒仍会有大量的资金入账.下图中id为1的耐克旗舰店账户就是热点行.
此例为清结算领域中的账户,当然最常见的场景还有各种库存业务.
如何判定是否热点行
- 业务人员凭借经验判定,比如特惠商品上架,秒杀活动,亦或者是大商家入驻平台。判定后人工手动标记。
- 系统层面做采集,每条sql的执行都要走拦截器,拦截器里判定是否是update以及采集他的修改参数后,添加到预埋内存里维护热点元信息(可以通过LFU等淘汰策略保证内存大小不膨胀),定时上报给一个集中的woker,由woker汇总元信息后根据规则判定。
下图是我参考了几个开源项目,画出的浓缩图- 有兴趣的具体可以看一下京东HotKey以及Sentinel等.
热点行带来的影响
我先简要描述一下执行单条热点行update的总体流程从web-server里的连接池线程到对应mysql线程刷写文件,可以帮助我们更好的理解它所造成的影响.
下图表述的是,在执行update过程中,行有锁的情况下,则会生成锁对象加入到行锁对应的锁等待队列中,等待锁对象被唤醒之后写undo,redo后释放掉锁对象-唤醒队列头节点的全流程.
从上图中我们可以看出来单条热点行update同比非热点行update的耗时,主要在死锁检测(事务越多-DFS需要的时间越多)和等待队列里的等待时间这两点.
并且以上所描述仅是单条sql update的情况,在正常的业务开发中,单个事务都不仅限于执行一条sql,又由于OLTP中大多数采用2PL(两阶段锁),整个事务提交才会释放掉所占用锁。
因此单个事务的执行时间拉长,耗费mysql-server宝贵的线程资源(One-Connection-Per-Thread),进而降低了整个系统的吞吐。
下篇我将会在两周内写出来,内容质量也有保证希望你能持续关注我!
本人公众号:有点小乐观的ben