注册
通过BUFFER_MODE配置提升DSC集群并发访问能力
专栏/培训园地/ 文章详情 /

通过BUFFER_MODE配置提升DSC集群并发访问能力

Ariamaru 2026/08/06 14 0 0
摘要

在DSC集群配置的时候,有这么一个参数BUFFER_MODE,其默认值为0,=0与=1时分别对应两种淘汰算法,本文将介绍两种算法,并通过测试实验说明,配置BUFFER_MODE=1能有效避免配置BUFFER_MODE=0带来的高并发热点冲突问题,更能提升DSC集群的并发访问能力。

image.png

LRU 淘汰算法

数据缓冲区是 DM Server 在将数据页写入磁盘之前以及从磁盘上读取数据页之后,数据页所存储的地方。
数据缓冲区存在三条链来管理被缓冲的数据页,一条是自由链,用于存放目前尚未使用的内存数据页,一条是 LRU 链,用于存放已被使用的内存数据页(包括未修改和已修改),还有一条即为脏链,用于存放已被修改过的内存数据页。

LRU链对系统当前使用的页其最近是否被使用的顺序进行了排序。

  1. 每次访问,都会对该分片加x锁,该页计数器+1,更新冷热程度;
  2. 淘汰时,在分片内优先淘汰计数器最低(最冷)的页;

自由链用完时,LRU淘汰掉部分最近未使用的数据页,减少IO。

image.png

时钟淘汰算法

缺页时,在该分片内部执行时钟淘汰:

  • 从分片的尾部(当前时钟指针所在位置)开始;
  • 尾页的访问次数设定为 0(给该页一个"第二次机会");
  • 向前查找,找到第一个访问次数已经为 0 的页面;
  • 将该页面淘汰,替换为当前需要加载的新页面;
  • 最后把新页放在页头的位置
    image.png

与LRU不同的地方在于正常的页面命中访问不需要对分片加 X 锁,只有在发生缺页访问(需要将磁盘中的新页面加载到缓冲池,触发页面置换)时,才需要对目标 BUFFER 分片加 X 锁。

LRU 模式下的热点冲突

BUFFER_MODE=0(LRU)模式下,每一次数据页访问都需要更新该分片的 LRU 链表——无论该页面是否在内存中命中,都需要将页面移动至链头或更新其访问计数。这个更新操作需要获取 buffer 分片的 X 互斥锁(mutex),导致同一分片上的并发访问产生热点竞争

当大量并发会话同时访问落在同一个 BUFFER 分片上的不同数据页时,这些会话会排队争抢该分片的互斥锁,大量线程陷入内核态睡眠等待,响应时间急剧上升。

Clock 算法为何能缓解这一问题?

时钟算法将 LRU 模式下"每次访问都要改链表"的频繁加互斥锁操作,收敛为"只有缺页置换时才加 X 锁"。正常页面命中时,线程以无锁或轻量级方式并发访问 buffer 分片;仅在需要淘汰页面时,才通过访问计数器和时钟指针的周期性扫描来决定淘汰对象。

这一设计在高并发场景下显著降低了缓冲池的互斥锁竞争,减少了 CPU 在线程调度与上下文切换上的开销,从而将更多算力释放给业务逻辑。

BUFFER_MODE的两种模式测试

为验证上述理论,我们在一个两节点的 DSC 集群上进行了 JMeter 压测。测试中除 BUFFER_MODE 外,其他数据库与服务端配置均保持一致。我们通过观测不同并发数下的 TPS、平均响应时间、最大响应时间和 99 百分位延迟等指标,对比两种淘汰算法在热点冲突场景下的表现。

建表相关的语句:

image.png

为了更贴合真实业务场景,并确保热点冲突能够被有效触发,测试用例将查询条件中的 PID 值设定在一个较小的随机范围内(1~10000),使大量并发请求集中在少数 BUFFER 分片上,从而放大 X 锁竞争效应。

        <!-- JSR223 PreProcessor: 每次迭代生成随机 PID (1~10000) -->
        <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="随机 PID 1~10000" enabled="true">
          <stringProp name="scriptLanguage">groovy</stringProp>
          <stringProp name="script">int pid = (int)(Math.random() * 10000) + 1; vars.put(&quot;pid&quot;, String.valueOf(pid));</stringProp>
        </JSR223PreProcessor>
        <hashTree/>

        <!-- SELECT 分区表回表查询 -->
        <JDBCSampler guiclass="TestBeanGUI" testclass="JDBCSampler" testname="分区表回表查询" enabled="true">
          <stringProp name="dataSource">dm_pool</stringProp>
          <stringProp name="queryType">Prepared Select Statement</stringProp>
          <stringProp name="query">SELECT COUNT(*) FROM test_lkup WHERE PID = ? AND C1 &gt; &apos;0&apos;</stringProp>
          <stringProp name="queryArguments">${pid}</stringProp>
          <stringProp name="queryArgumentsTypes">INTEGER</stringProp>
          <stringProp name="variableNames"></stringProp>
          <stringProp name="resultVariable"></stringProp>
          <stringProp name="queryTimeout">10</stringProp>
          <stringProp name="resultSetHandler">Store as String</stringProp>
        </JDBCSampler>
        <hashTree/>

BUFFER_MODE=0

10并发下的压测结果:

image.png

100并发下的压测结果:

image.png

400并发下的压测结果:

image.png

BUFFER_MODE=1

10并发下的压测结果:

image.png

100并发下的压测结果:

image.png

400并发下的压测结果:
image.png

通过对比,整理数据成表格

并发数 模式 平均响应时间 (ms) 最大响应时间 (ms) TPS 99th pct
10 MODE=0 1.27 2489 7158 63
10 MODE=1 1.02 903 8720 34
100 MODE=0 12.34 3683 7100 572.99
100 MODE=1 9.47 1757 9425 369
400 MODE=0 59.94 5693 6120 916
400 MODE=1 46.81 6013 7572 815

从上表数据可以清晰地得出以下结论:

  • 平均响应时间:在所有并发级别下,MODE=1 的平均响应时间均低于 MODE=0。在 400 并发时,MODE=1MODE=0 降低了约 22% 的平均延迟。
  • 尾部延迟(99th pct)MODE=1 的 99 百分位延迟同样显著低于 MODE=0,说明时钟算法在大压力下能更稳定地维持服务质量,减少长尾请求。
  • 吞吐量(TPS)MODE=1 的 TPS 在所有并发级别下均高于 MODE=0。在 100 并发时差距最为显著,MODE=1MODE=0 提升了约 32.7% 的吞吐量。

在测试中,100 并发时 TPS 达到峰值 9425,而当并发提升到 400 时,TPS 却回落至 7572。表面上看,并发翻了 4 倍,吞吐量理应线性增长,但实测结果却背道而驰。通过操作系统对cpu的监控,发现在100并发时cpu的占用率已经高达90多,400并发更是冲到了100,这意味着新增的并发线程并没有被真正并行执行,而是堆积在 CPU 运行队列里空等,所以会出现TPS不升反降的现象。

image.png

热点冲突的间接证据,当我们尝试把查询条件中的 PID 值的范围,从1-10000增加到1-100000,保持其他条件相同,用100的并发压测,结果发现TPS能增长到19000,这说明查询的数据页被打散到更多的 buffer 分片上,本挤在一个分片上的线程,现在被均匀分流到多个分片上,每个分片的并发压力降低,x锁竞争也降低了,TPS有所增加。这间接证明了LRU算法带来的热点冲突,会显著影响集群的性能。
image.png

宏观层面来看,配置BUFFER_MODE=1 能够提供更低的响应延迟、更高的吞吐量,接下来通过抓取堆栈,观察两种模式对于热点冲突发生时的锁竞争的详细情况。

ps -ef | grep dmserver | grep -v grep
gdb -p 8839 -batch -ex "set logging file stack1.log" -ex "set logging on" -ex "thread apply all bt" -ex "set logging off"

image.png

image.png

LRU 模式的堆栈中,通过统计,有大量线程卡在 __lll_lock_waitbuf4_mutex_enterfor_LRU,这是 buffer 分片互斥锁竞争的直接证据;而时钟模式的堆栈中几乎没有这种 mutex 阻塞。

综上所述,在 DSC 集群高并发场景下,将 BUFFER_MODE 配置为 1(时钟淘汰算法),能有效的减少 buffer 分片的 X 锁争用,降低平均响应时间和长尾延迟,提升了集群整体吞吐量。

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服