TiDB v6.0.0(DMR)缓存表初试

2023-10-11 01:59
文章标签 tidb 缓存 初试 v6.0 dmr

本文主要是介绍TiDB v6.0.0(DMR)缓存表初试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者:陈超

原文来源: https://tidb.net/blog/452fe625

【是否原创】是

【首发渠道】TiDB 社区

【首发渠道链接】其他平台首发请附上对应链接

【正文】

一、背景

一般情况下使用TiDB单表大小为千万级别以上在业务中性能最优,但是在实际业务中总是会存在小表。例如配置表对写请求很少,而对读请求的性能的要求更高。当表的数据量不大,访问又特别频繁的情况下,数据会集中在 TiKV的 一个 Region 上,形成读热点,从而影响性能,在分布式数据库中一直以来是一个痛点问题。

TiDB v6.0.0(DMR)版本推出了缓存表的功能,第一次看到这个词的时候让我想到了MySQL的内存表。MySQL内存表的表结构创建在磁盘上,数据存放在内存中。内存表的缺点很明显。当MySQL启动着的时候,表和数据都存在,当MySQL重启后,表结构存在,数据消失。TiDB的缓存表不存在这个问题。从asktug论坛中看到很多小伙伴都很期待缓存表的表现,个人也对它的性能很期待,因此在测试环境中实际看看缓存表的性能如何。

二、缓存表的使用场景

以下部分内容来自官方文档

TiDB 缓存表功能适用于以下特点的表:

  • 表的数据量不大
  • 只读表,或者几乎很少修改
  • 表的访问很频繁,期望有更好的读性能

TiDB作为一个分布式数据库,大表的负载很容易利用分布式的特性分散到多台机器上,但当表的数据量不大,访问又特别频繁的情况下,数据通常会集中在 TiKV 的一个 Region 上,形成读热点,更容易造成性能瓶颈。因此,TiDB 缓存表的典型使用场景如下:

  • 配置表,业务通过该表读取配置信息
  • 金融场景中的存储汇率的表,该表不会实时更新,每天只更新一次
  • 银行分行或者网点信息表,该表很少新增记录项

缓存表把整张表的数据从 TiKV 加载到 TiDB Server 中,查询时可以不通过访问TiKV直接从 TiDB Server 的缓存中读取。使用普通表查询时,返回的数据量越多索引的效率可能越低,直到和全表扫描的代价接近优化器可能会直接选择全表扫描。缓存表本身数据都在TiDB Server 的内存中,可以避免磁盘IO,因此查询效率也会更高。以配置表为例,当业务重启的瞬间,全部业务连接一起加载配置,会造成较高的数据库读延迟。如果使用了缓存表,读请求可以直接从内存中读取数据,可以有效降低读延迟。在金融场景中,业务通常会同时涉及订单表和汇率表。汇率表通常不大,表结构很少发生变化因此几乎不会有DDL,加上每天只更新一次,也非常适合使用缓存表。其他业务场景例如银行分行或者网点信息表,物流行业的城市、仓号库房号表,电商行业的地区、品类相关的字典表等等,对于这种很少新增记录项的表都是缓存表的典型使用场景。

三、集群拓扑规划

Role Host Ports
alertmanager 10.0.0.1 9093/9094
grafana 10.0.0.1 3000
pd 10.0.0.1 2379/2380
pd 10.0.0.2 2379/2380
pd 10.0.0.1 3379/3380
prometheus 10.0.0.1 9090/12020
tidb 10.0.0.1 4000/10080
tidb 10.0.0.2 4000/10080
tikv 10.0.0.1 20162/20182
tikv 10.0.0.1 20160/20180
tikv 10.0.0.2 20161/20181

由于硬件条件受限,只有2台普通性能的云主机混合部署的集群(实际上和单机部署也差不多了)。单机CPU核数较少且tidb server没有做负载均衡所以并发无法调整太高。以下测试均使用一个tidb server节点进行压测(贫穷限制了我的想象;´༎ຶД༎ຶ`)。因此不用特别关注本次测试的测试数据,测试结果也仅限参考。

四、性能测试

测试工具:sysbench 1.0

一.使用普通表

1.单表数据量5000,测试QPS

| 线程数 | oltp_point_select | oltp_read_only | select_random_points | select_random_ranges | oltp_read_write | | --: | ------------------: | ---------------: | ---------------------: | ---------------------: | ----------------: | | 8 | 2214 | 1985 | 3190 | 2263 | 1702 | | 16 | 3199 | 2414 | 3412 | 2491 | 1132 | | 32 | 4454 | 2867 | 3898 | 2763 | 1101 | | 64 | 5792 | 3712 | 4321 | 2981 | 831 | | 128 | 7639 | 4964 | 4474 | 2965 | 686 |

2.单表数据量50000,测试QPS

| 线程数 | oltp_point_select | oltp_read_only | select_random_points | select_random_ranges | oltp_read_write | | --: | ------------------: | ---------------: | ---------------------: | ---------------------: | ----------------: | | 8 | 4874 | 2808 | 2841 | 2207 | 2940 | | 16 | 5042 | 3429 | 3172 | 2448 | 3053 | | 32 | 6754 | 4290 | 3405 | 2651 | 3133 | | 64 | 8989 | 5282 | 3831 | 2818 | 3294 | | 128 | 12565 | 6470 | 3996 | 2811 | 2695 |

| |

二.使用缓存表

1.单表数据量5000,测试QPS

| 线程数 | oltp_point_select | oltp_read_only | select_random_points | select_random_ranges | oltp_read_write | | --: | ------------------: | ---------------: | ---------------------: | ---------------------: | ----------------: | | 8 | 15780 | 10811 | 5666 | 2716 | 274 | | 16 | 23296 | 11399 | 6417 | 2948 | 330 | | 32 | 28038 | 11313 | 6907 | 3050 | 566 | | 64 | 32924 | 11377 | 7217 | 3200 | 435 | | 128 | 33962 | 11413 | 7199 | 3232 | 371 |

2.单表数据量50000,测试QPS

| 线程数 | oltp_point_select | oltp_read_only | select_random_points | select_random_ranges | oltp_read_write | | --: | ------------------: | ---------------: | ---------------------: | ---------------------: | ----------------: | | 8 | 15910 | 16540 | 5359 | 2646 | 443 | | 16 | 21945 | 17022 | 5999 | 2915 | 668 | | 32 | 25614 | 17356 | 6355 | 3065 | 857 | | 64 | 31782 | 17410 | 6690 | 3088 | 1085 | | 128 | 35009 | 17584 | 6713 | 3161 | 1561 |

三.性能对比

image.png

image.png

image.png

image.png

image.png

五、遇到的问题

1.目前 TiDB 对于每张缓存表的大小限制为 64 MB(所有 key-value 记录的总大小),因此太大的表无法缓存在内存中。另外,缓存表无法执行普通的 DDL 语句。若要对缓存表执行 DDL 语句,需要先使用 ALTER TABLE xxx NOCACHE 语句去掉缓存属性,将缓存表设回普通表后,才能对其执行其他 DDL 语句。尝试将50w数据的表改为缓存表时报错ERROR 8242 (HY000): 'table too large' is unsupported on cache tables。当然这本身不是什么问题,不过想把一些相对大一些的表也缓存起来达到加速读写的目的可能暂时无法实现。

2.测试过程中缓存表性能出现了不稳定的情况,有些时候缓存表反而比普通表读取性能差,使用 trace 语句(TRACE SELECT * FROM sbtest1;)查看发现返回结果中出现了regionRequest.SendReqCtx,说明 TiDB 尚未将所有数据加载到内存中,多次尝试均未加载完成。把tidb_table_cache_lease调整为10后没有出现该问题。在asktug中向研发大佬提出了这个问题得到了解答。根据 https://github.com/pingcap/tidb/issues/33167 中的描述,当机器负载较重时,load table需要3s以上 ,但是默认的tidb_table_cache_lease是3s, 表示加载的数据是立即过时,因此需要重新加载,并且该过程永远重复。导致了浪费了大量的 CPU 资源并降低了 QPS。目前可以将tidb_table_cache_lease的值调大来解决,该问题在master分支中已经解决,后续版本应该不会出现。

3.根据测试结果,写入较为频繁的情况下缓存表的性能是比较差的。在 lease 过期之前,无法对数据执行修改操作。为了保证数据一致性,修改操作必须等待 lease 过期,所以会出现写入延迟。例如tidb_table_cache_lease为10时,写入可能会出现较大的延迟。因此写入比较频繁或者对写入延迟要求很高的业务不建议使用缓存表。

五、总结

在6.0版本之前,对于小表热点问题我们可以通过load split、follower read、stale read等方式去缓解,6.0之后提供了一种全新的功能缓存表去解决小表读热点问题。缓存表剑走偏锋,它的机制决定了使用场景目前仅限于表的数据量不大 的只读表,或者几乎很少修改的小表,但在它的使用场景下相较于其他方式能够提供更好的性能。

本次测试除了各种读性能测试外有写操作的只测试了oltp_read_write(读写混合操作)的场景。由于缓存表使用了lease机制,为了保证缓存的一致性,写操作可能会产生较大的延迟,因此在读写操作都比较频繁的oltp_read_write的场景下测试性能低于普通表符合预期。类似这种写操作较为频繁的业务则不建议使用缓存表,也许更适合放到nosql例如redis中去。在oltp_point_select(点查,条件为唯一索引列),oltp_read_only(只读操作,包含聚合、去重等),select_random_points(随机点查,主键列的selete in操作)和select_random_ranges(随机范围查询,主键列的selete between操作)的性能测试中相较于普通表都大幅提高了访问性能。综上,虽然缓存表目前的使用场景相对比较单一,但是在合适的场景下确实是一个解决了业务痛点的好功能,也期待在后续的版本中能有更高的稳定性和更优秀的性能表现。

这篇关于TiDB v6.0.0(DMR)缓存表初试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/184719

相关文章

Linux修改pip和conda缓存路径的几种方法

《Linux修改pip和conda缓存路径的几种方法》在Python生态中,pip和conda是两种常见的软件包管理工具,它们在安装、更新和卸载软件包时都会使用缓存来提高效率,适当地修改它们的缓存路径... 目录一、pip 和 conda 的缓存机制1. pip 的缓存机制默认缓存路径2. conda 的缓

Redis解决缓存击穿问题的两种方法

《Redis解决缓存击穿问题的两种方法》缓存击穿问题也叫热点Key问题,就是⼀个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击,本文给大家介绍了Re... 目录引言解决办法互斥锁(强一致,性能差)逻辑过期(高可用,性能优)设计逻辑过期时间引言缓存击穿:给

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

一文详解Nginx的强缓存和协商缓存

《一文详解Nginx的强缓存和协商缓存》这篇文章主要为大家详细介绍了Nginx中强缓存和协商缓存的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、强缓存(Strong Cache)1. 定义2. 响应头3. Nginx 配置示例4. 行为5. 适用场景二、协商缓存(协

Golang基于内存的键值存储缓存库go-cache

《Golang基于内存的键值存储缓存库go-cache》go-cache是一个内存中的key:valuestore/cache库,适用于单机应用程序,本文主要介绍了Golang基于内存的键值存储缓存库... 目录文档安装方法示例1示例2使用注意点优点缺点go-cache 和 Redis 缓存对比1)功能特性

MySQL8.0设置redo缓存大小的实现

《MySQL8.0设置redo缓存大小的实现》本文主要在MySQL8.0.30及之后版本中使用innodb_redo_log_capacity参数在线更改redo缓存文件大小,下面就来介绍一下,具有一... mysql 8.0.30及之后版本可以使用innodb_redo_log_capacity参数来更改

MySQL 缓存机制与架构解析(最新推荐)

《MySQL缓存机制与架构解析(最新推荐)》本文详细介绍了MySQL的缓存机制和整体架构,包括一级缓存(InnoDBBufferPool)和二级缓存(QueryCache),文章还探讨了SQL... 目录一、mysql缓存机制概述二、MySQL整体架构三、SQL查询执行全流程四、MySQL 8.0为何移除查

Redis缓存问题与缓存更新机制详解

《Redis缓存问题与缓存更新机制详解》本文主要介绍了缓存问题及其解决方案,包括缓存穿透、缓存击穿、缓存雪崩等问题的成因以及相应的预防和解决方法,同时,还详细探讨了缓存更新机制,包括不同情况下的缓存更... 目录一、缓存问题1.1 缓存穿透1.1.1 问题来源1.1.2 解决方案1.2 缓存击穿1.2.1

Redis与缓存解读

《Redis与缓存解读》文章介绍了Redis作为缓存层的优势和缺点,并分析了六种缓存更新策略,包括超时剔除、先删缓存再更新数据库、旁路缓存、先更新数据库再删缓存、先更新数据库再更新缓存、读写穿透和异步... 目录缓存缓存优缺点缓存更新策略超时剔除先删缓存再更新数据库旁路缓存(先更新数据库,再删缓存)先更新数

el-select下拉选择缓存的实现

《el-select下拉选择缓存的实现》本文主要介绍了在使用el-select实现下拉选择缓存时遇到的问题及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录项目场景:问题描述解决方案:项目场景:从左侧列表中选取字段填入右侧下拉多选框,用户可以对右侧