本文将介绍热点行性能优化功能。

前提条件

PolarDB集群版本需为以下版本之一:

  • PolarDB MySQL 5.6且内核小版本需为20200601及以上。
  • PolarDB MySQL 8.0且内核小版本需为8.0.1.1.10及以上
说明 关于如何升级内核小版本请参见版本升级

使用限制

在如下情场景中,热点行性能优化将不会被使用:

  • 热点行所属的数据表是分区表。
  • 热点行所属的数据表上定义了触发器(Trigger)。

热点行问题和解决方案

  • 问题

    数据库中那些会被频繁执行增删改查操作的数据行称为热点行。当一个事务对一行数据进行更新时,会对目标数据行加锁,直到事务提交或回滚时才释放。同一时段内,对于同一个数据行,只有一个事务能够进行更新,其它事务需要等待。由此可见,对单一热点行的更新请求其实是串行执行的,传统的分库分表策略在性能提升方面并不会有太大帮助。

    在电商平台业务中,限购、秒杀是常用的促销手段。在这些场景下,大量对热点行的更新请求在极短时间间隔内到达后台数据库系统,必然造成严重的行锁竞争和等待,影响系统性能。如果一个更新请求等待执行的时间变长,将会对业务层面产生显著负面影响。

  • 解决方案

    针对上述问题,单纯提高计算机硬件配置已经无法满足这样的低延迟需求。因此PolarDB在数据库内核层进行了创新性的优化,不但能够自动识别热点行更新请求,而且将一定时间间隔内对同一数据行的更新操作进行分组,不同分组采用流水线的方式并行处理,通过这些优化,极大地提升了系统的性能。

原理

  • 串行处理变流水线处理

    为了提升数据库系统的性能,最直接的方法是使用并行处理,但是对同一热点行的更新操作很难做到完全并行,PolarDB创新性地使用了流水线处理方式,最大限度地将热点行更新操作并行化。

    1

    热点行更新操作所使用的SQL语句会用autocommit或者commit_on_success进行标记,优化后的MySQL内核层会自动识别带此类标记的更新操作,在一定的时间间隔内,将收集到的更新操作按照主键或者唯一键进行Hash,对于Hash到同一个桶中的更新操作,会将它们按照到达的先后顺序分组分批地进行处理和提交。

    为了使用流水线方式处理这些更新操作,需要使用两个执行单元对它们进行分组。当第一个分组收集完毕准备提交时,第二个分组立即开始收集更新操作;当第二个分组收集完毕准备提交时,第一个分组已经提交完毕并开始收集新一批的更新操作,两个分组不断切换,并行执行。

    现如今多核心CPU的使用已经非常普遍。这样的流水线式的处理方式能够充分利用硬件资源,提升CPU的使用率,提高数据库系统的并行处理能力,从而最大限度地提升数据库系统的吞吐量。

  • 消除申请行锁时的等待

    为了保证数据的逻辑一致性,对一个数据行进行更新时,首先会对该数据行加锁。如果加锁请求无法立刻满足,则进入等待状态,这样一来,不但增加了处理延迟,而且还会触发死锁检测,导致额外的资源消耗。

    前面提到,我们会按照时间顺序将对同一数据行的更新操作进行分组,组内第一个更新操作为Leader,其读取目标数据行并且加锁,后续更新操作为Follower,对目标数据行加锁时,如果发现Leader已经持有行锁,不需要等待,直接获得行锁。

    通过这个优化,能够减少行锁的加锁次数和时间开销,整个数据库系统的性能有了显著的提升。

  • 减少B-tree索引的遍历

    MySQL是以B-tree索引的方式管理数据的,每次执行查询时,都需要遍历索引才能定位到目标数据行,数据表越大,索引层级越多,遍历时间就越长。

    在前面提到的对更新操作进行分组的机制中,只有每组的Leader遍历索引定位数据行,之后把更新后的数据行缓存(row cache)在内存中,同组的Follower加锁成功后,直接从内存中读取目标数据行,而不需要再次遍历索引。

    这样一来,从整体上减少了索引遍历的次数和时间开销。

使用方法

  • 使用热点行性能优化功能前,您需要对如下参数进行配置。如何配置参数,详细操作请参见设置集群参数
    参数 说明
    hotspot 热点行性能优化总开关,取值为ON或OFF,默认值为OFF。
    hotspot_for_autocommit Auto-commit模式下的UPDATE语句能否使用热点行性能优化,取值为ON或OFF,默认值为OFF。
    说明 只有当hotspot为ON时,才支持配置该参数。
    hotspot_update_max_wait_time 行数据分组批量更新(即Group update)过程中Leader等待Follower加入该分组的等待时间,单位为微秒(us),默认值为100us。
    hotspot_lock_type 行数据分组批量更新过程中是否使用新类型的行锁,取值为ON或OFF,默认值为OFF。
    说明 该参数打开时,对相同热点行的更新操作申请行锁时不需要等待,从而提升性能。
    说明
    • hotspot_for_autocommithotspot_update_max_wait_timehotspot_lock_type参数暂未开放,如需使用请提交工单联系技术支持。
    • 热点行性能优化需要依赖Binlog,所以,当Binlog功能处于关闭状态时,试图打开参数hotspot时会失败并报错。
      当Binlog处于关闭状态时,试图使用MySQL [(none)]> set global hotspot=ON;命令打开参数hotspot时会失败,并出现如下报错:
      ERROR 3042 (HY000):
      Variable 'hotspot' cannot be enabled because 'bin logging' is enabled/disabled

      如何开启Binlog,详细操作步骤请参见开启Binlog

    • 当全局Binlog开启,但是会话级别的Binlog关闭时,可以打开参数hotspot,但是执行UPDATE语句时并不会使用热点行性能优化。
    • 当参数rds_ic_reduce_hint_enable处于打开状态时,试图使用MySQL [(none)]> set global hotspot=ON;命令打开参数hotspot时会失败,并出现如下报错:
      ERROR 3042 (HY000): Variable 'hotspot' cannot be enabled because 'rds_ic_reduce_hint_enable' is enabled/disabled
  • 您可以使用如下命令查看参数配置:
    show variables like "hotspot%";

    返回结果示例

    +------------------------------+-------+
    |
    Variable_name                | Value |
    +------------------------------+-------+
    |
    hotspot                      | OFF   |
    |
    hotspot_for_autocommit       | OFF   |
    |
    hotspot_lock_type            | OFF   |
    |
    hotspot_update_max_wait_time | 100   |
    +------------------------------+-------+
  • 您可以使用如下命令查看热点行性能优化使用情况:
    show global status like 'Group_update%';

性能测试

  • 测试所需如下数据表定义和测试语句:
    • 数据表定义
      CREATE TABLE sbtest(id INT UNSIGNED NOT NULL, c BIGINT UNSIGNED NOT NULL)PRIMARY KEY (id)
    • 测试语句
      UPDATE COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW 1 sbtest SET c=c+1 WHERE id = 1
  • 测试工具:Sysbench
  • 测试场景和测试结果
    • 测试场景1:单个热点行加8内核CPU。

      测试结果:在单热点行加8内核CPU的场景下,引入热点行优化后,库存热点性能在高并发时提升近64倍。

      2
    • 测试场景2:单个热点行加32内核CPU

      测试结果:在单热点行加32核CPU的场景下,引入热点行优化后,峰值QPS提升了63倍;高并发为8000时,性能提升了46倍。

      2
    • 测试场景3:8个热点行加32核CPU

      测试结果:在多热点行(8个)加32核CPU的场景下,引入热点行优化后,峰值QPS提升了20倍。

      且当高并发达到16000时,在未使用热点行优化功能的情况下,更新操作会导致数据库出现故障无法返回更新操作结果;但使用热点行优化后,更新操作可以正常返回,且QPS维持稳定。

      4