本文作者:啦啦啦啦啦,TiDB 老粉,现在上任于京东物流,社区资深用户,asktug 主页
一、背景
一般情况下运用 TiDB 单表巨细为千万级别以上在事务中功用最优,可是在实践事务中总是会存在小表。例如装备表对写恳求很少,而对读恳求的功用的要求更高。TiDB 作为一个分布式数据库,大表的负载很容易运用分布式的特性涣散到多台机器上,但当表的数据量不大,拜访又特别频频的情况下,数据一般会集中在 TiKV 的一个 Region 上,构成读热门,更容易形成功用瓶颈。
TiDB v6.0.0(DMR) 版别推出了缓存表的功用,第一次看到这个词的时分让我想到了 MySQL 的内存表。MySQL 内存表的表结构创建在磁盘上,数据存放在内存中。内存表的缺陷很明显。当 MySQL 发动着的时分,表和数据都存在,当 MySQL 重启后,表结构存在,数据消失。TiDB 的缓存表不存在这个问题。从 asktug 论坛中看到许多小伙伴都很等待缓存表的体现,个人也对它的功用很等待,因而在测验环境中实践看看缓存表的功用怎么。
二、缓存表的运用场景
以下部分内容来自官方文档,概况见 缓存表
TiDB 缓存表功用适用于以下特点的表:
- 表的数据量不大
- 只读表,或许简直很少修正
- 表的拜访很频频,期望有更好的读功用
关于第一点官方文档中说到缓存表的巨细限制为包括索引在内的一切 key-value 记载的总巨细不能超越 64 MB。实践测验运用 Sysbench 生成下文中表结构的表从 20w 提高到 30w 数据量时无法将一般表转换为缓存表,因而出产环境中实践运用缓存表的场景应该最多不超越几十万级别的数据量。关于缓存表对包括读写操作方面的功用,运用多种不同的读写恳求份额进行了测验,相较一般表均没有达到更好的功用体现。这是因为为了读取数据的一致性,在缓存表上履行修正操作后,租约时间内写操作会被阻塞,最长或许呈现 tidb_table_cache_lease 变量值时长的等待,会导致QPS下降。因而缓存表更适宜只读表,或许简直很少修正的场景。
缓存表把整张表的数据从 TiKV 加载到 TiDB Server 中,查询时能够不通过拜访 TiKV 直接从 TiDB Server 的缓存中读取,节省了磁盘 IO 和网络带宽。运用一般表查询时,返回的数据量越多索引的效率或许越低,直到和全表扫描的代价接近优化器或许会直接选择全表扫描。缓存表本身数据都在 TiDB Server 的内存中,能够防止磁盘 IO,因而查询效率也会更高。以装备表为例,当事务重启的瞬间,悉数事务连接一同加载装备,会形成较高的数据库读推迟。假如运用了缓存表,读恳求能够直接从内存中读取数据,能够有效下降读推迟。在金融场景中,事务一般会一起涉及订单表和汇率表。汇率表一般不大,表结构很少发生变化因而简直不会有 DDL,加上每天只更新一次,也非常适宜运用缓存表。其他事务场景例如银行分行或许网点信息表,物流职业的城市、仓号库房号表,电商职业的区域、品类相关的字典表等等,关于这种很少新增记载项的表都是缓存表的典型运用场景。
三、测验环境
1.硬件装备及集群拓扑规划
运用 2 台云主机,硬件装备为 4C 16G 100G 一般 SSD 硬盘。
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. 软件装备
软件名称 | 软件用处 | 版别 |
---|---|---|
CentOS | 操作系统 | 7.6 |
TiDB 集群 | 开源分布式 NewSQL 数据库 | v6.0.0 DMR |
Sysbench | 压力测验东西 | 1.0.20 |
3.参数装备
server_configs:
tidb:
log.slow-threshold: 300
new_collations_enabled_on_first_bootstrap: true
tikv:
readpool.coprocessor.use-unified-pool: true
readpool.storage.use-unified-pool: false
pd:
replication.enable-placement-rules: true
replication.location-labels:
- host
因为硬件条件受限,只要 2 台一般功用的云主机混合布置的集群(实践上和单机布置也差不多了)。单机 CPU 核数较少且 TiDB Server 没有做负载均衡所以并发无法调整太高。以下测验均运用一个 TiDB Server 节点进行压测,因而不必特别关注本次测验的测验数据,或许会跟其他测验成果有所收支,不代表最佳功用实践和布置,测验成果仅限参阅。
四、功用测验
Sysbench 生成的表结构
CREATE TABLE sbtest1 (
id int(11) NOT NULL AUTO_INCREMENT,
k int(11) NOT NULL DEFAULT '0',
c char(120) NOT NULL DEFAULT '',
pad char(60) NOT NULL DEFAULT '',
PRIMARY KEY (id),
KEY k_1 (k)
) ENGINE = InnoDB CHARSET = utf8mb4 COLLATE = utf8mb4_bin AUTO_INCREMENT = 1
读功用测验
测验首要参数 oltp_point_select 主键查询测验(点查,条件为唯一索引列)
首要 SQL 句子:
SELECT c FROM sbtest1 WHERE id=?
select_random_points 随机多个查询(主键列的 selete in 操作)
首要 SQL 句子:
SELECT id, k, c, pad FROM sbtest1 WHERE k IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
select_random_ranges 随机范围查询(主键列的 selete between and 操作)
首要 SQL 句子:
SELECT count(k) FROM sbtest1 WHERE k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ? OR k BETWEEN ? AND ?
oltp_read_only 只读操作(包括聚合、去重等)
首要 SQL 句子:
SELECT c FROM sbtest1 WHERE id=?
SELECT SUM(k) FROM sbtest1 WHERE id BETWEEN ? AND ?
SELECT c FROM sbtest1 WHERE id BETWEEN ? AND ? ORDER BY c
SELECT DISTINCT c FROM sbtest1 WHERE id BETWEEN ? AND ? ORDER BY c
Sysbench 测验指令示例
sysbench --mysql-host=10.0.0.1 --mysql-port=4000 --mysql-db=sbtest --mysql-user=root --time=600 \
--threads=8 --report-interval=10 --db-driver=mysql oltp_point_select --tables=1 --table-size=5000 run
sysbench --mysql-host=10.0.0.1 --mysql-port=4000 --mysql-db=sbtest --mysql-user=root --time=600 \
--threads=8 --report-interval=10 --db-driver=mysql oltp_read_only --tables=1 --table-size=5000 run
sysbench --mysql-host=10.0.0.1 --mysql-port=4000 --mysql-db=sbtest --mysql-user=root --time=600 \
--threads=8 --report-interval=10 --db-driver=mysql select_random_points --tables=1 --table-size=5000 run
sysbench --mysql-host=10.0.0.1 --mysql-port=4000 --mysql-db=sbtest --mysql-user=root --time=600 \
--threads=8 --report-interval=10 --db-driver=mysql select_random_ranges --tables=1 --table-size=5000 run
一、运用一般表
1.单表数据量 5000,测验 QPS
threads/request type | oltp_point_select | oltp_read_only | select_random_points | select_random_ranges |
---|---|---|---|---|
8 | 2214 | 1985 | 3190 | 2263 |
16 | 3199 | 2414 | 3412 | 2491 |
32 | 4454 | 2867 | 3898 | 2743 |
64 | 5792 | 3712 | 4321 | 2981 |
128 | 7639 | 4964 | 4474 | 2965 |
2.单表数据量 50000,测验 QPS
threads/request type | oltp_point_select | oltp_read_only | select_random_points | select_random_ranges |
---|---|---|---|---|
8 | 4874 | 2808 | 2841 | 2207 |
16 | 5042 | 3429 | 3172 | 2448 |
32 | 6754 | 4290 | 3405 | 2651 |
64 | 8989 | 5282 | 3831 | 2818 |
128 | 12565 | 6470 | 3996 | 2811 |
二、运用缓存表
1.单表数据量 5000,测验 QPS
threads/request type | oltp_point_select | oltp_read_only | select_random_points | select_random_ranges |
---|---|---|---|---|
8 | 15780 | 10811 | 5666 | 2716 |
16 | 23296 | 11399 | 6417 | 2948 |
32 | 28038 | 11313 | 6907 | 3050 |
64 | 32924 | 11377 | 7217 | 3200 |
128 | 33962 | 11413 | 7199 | 3232 |
2.单表数据量 50000,测验 QPS
threads/request type | oltp_point_select | oltp_read_only | select_random_points | select_random_ranges |
---|---|---|---|---|
8 | 15910 | 16540 | 5359 | 2646 |
16 | 21945 | 17022 | 5999 | 2915 |
32 | 25614 | 17356 | 6355 | 3065 |
64 | 31782 | 17410 | 6690 | 3088 |
128 | 35009 | 17584 | 6713 | 3161 |
三、功用比照
读写混合功用测验
测验首要场景参数 oltp_read_write 表明混合读写。
point_selects(每个事务里点查的数量)
delete_inserts(每个事务里插入/删去组合的数量)
首要 SQL 句子:
INSERT INTO sbtest1 (id, k, c, pad) VALUES (?, ?, ?, ?)
DELETE FROM sbtest1 WHERE id=?
SELECT c FROM sbtest1 WHERE id=?
本次测验通过单个事务中恳求类型的数量 –delete_inserts 固定为 10 且调整 –point_selects 参数的值来模仿不同读写份额下的功用差异,其余恳求参数运用默许值,详细指令可参阅下面 Sysbench 测验指令示例。
Sysbench 测验指令示例
sysbench --mysql-host=10.0.0.1 --mysql-port=4000 --mysql-db=sbtest --mysql-user=root --time=600 --threads=8 --report-interval=10 --db-driver=mysql oltp_read_write --tables=1 --table-size=5000 --point_selects=10 --non_index_updates=0 --delete_inserts=10 --index_updates=0 run
一.运用一般表
1.单表数据量 5000,测验 QPS
threads/–point_selects | 10 | 40 | 160 | 640 |
---|---|---|---|---|
8 | 869 | 2289 | 3852 | 5090 |
16 | 1014 | 2139 | 4354 | 6094 |
32 | 1075 | 2205 | 5089 | 6944 |
64 | 605 | 1861 | 5160 | 8395 |
128 | 877 | 2127 | 4332 | 9257 |
2.单表数据量 50000,测验 QPS
threads/–point_selects | 10 | 40 | 160 | 640 |
---|---|---|---|---|
8 | 1107 | 2144 | 3312 | 4439 |
16 | 1108 | 2103 | 3738 | 5702 |
32 | 1055 | 2228 | 4325 | 6770 |
64 | 1062 | 1397 | 5367 | 8209 |
128 | 981 | 1838 | 7235 | 17472 |
二、运用缓存表
1.单表数据量 5000,测验 QPS
threads/–point_selects | 10 | 40 | 160 | 640 |
---|---|---|---|---|
8 | 711 | 1322 | 2123 | 2787 |
16 | 361 | 665 | 1274 | 2870 |
32 | 400 | 627 | 1394 | 2997 |
64 | 323 | 804 | 1853 | 4100 |
128 | 372 | 680 | 1847 | 4704 |
2.单表数据量 50000,测验 QPS
threads/–point_selects | 10 | 40 | 160 | 640 |
---|---|---|---|---|
8 | 974 | 2726 | 3716 | 1804 |
16 | 787 | 1366 | 1736 | 2176 |
32 | 673 | 1231 | 2338 | 4627 |
64 | 572 | 1384 | 3120 | 7755 |
128 | 557 | 1104 | 2907 | 7486 |
三、功用比照
五、遇到的问题
- 测验将 30w 数据的表改为缓存表时报错
ERROR 8242 (HY000): 'table too large' is unsupported on cache tables
。
现在 TiDB 关于每张缓存表的巨细限制为 64 MB,因而太大的表无法缓存在内存中。另外,缓存表无法履行一般的 DDL 句子。若要对缓存表履行 DDL 句子,需求先运用 ALTER TABLE xxx NOCACHE
句子去掉缓存特点,将缓存表设回一般表后,才能对其履行其他 DDL 句子。
- 测验过程中缓存表功用呈现了不稳定的情况,有些时分缓存表反而比一般表读取功用差,运用 trace 句子(
TRACE SELECT * FROM sbtest1;
)检查发现返回成果中呈现了regionRequest.SendReqCtx
,阐明 TiDB 尚未将一切数据加载到内存中,屡次测验均未加载完结。把tidb_table_cache_lease
调整为 10 后没有呈现该问题。
在 asktug 中向研制大佬提出了这个问题得到了回答。依据 github.com/pingcap/tid… 中的描述,当机器负载较重时,load table 需求 3s 以上 ,可是默许的 tidb_table_cache_lease
是 3s, 表明加载的数据是立即过时的,因而需求重新加载,而且该过程永远重复。导致了浪费了很多的 CPU 资源而且下降了 QPS。现在能够将 tidb_table_cache_lease
的值调大来处理,该问题在 master 分支中现已处理,后续版别应该不会呈现。
依据测验成果,写入较为频频的情况下缓存表的功用是比较差的。在包括写恳求的测验中,缓存表相较于一般表的功用简直都大幅下降。
在 lease 过期之前,无法对数据履行修正操作。为了保证数据一致性,修正操作必须等待 lease 过期,所以会呈现写入推迟。例如 1tidb_table_cache_lease1 为 10 时,写入或许会呈现较大的推迟。因而写入比较频频或许对写入推迟要求很高的事务不建议运用缓存表。
六、测验总结
读功用
单表 5000,缓存表比较一般表提高的百分比
threads/request type | oltp_point_select | oltp_read_only | select_random_points | select_random_ranges |
---|---|---|---|---|
8 | 612.73% | 444.63% | 77.61% | 20.01% |
16 | 628.22% | 372.20% | 88.01% | 18.34% |
32 | 529.50% | 294.59% | 77.19% | 10.38% |
64 | 468.43% | 206.49% | 67.02% | 7.34% |
128 | 344.58% | 129.91% | 60.90% | 9.00% |
单表 50000,缓存表比较一般表提高的百分比
threads/request type | oltp_point_select | oltp_read_only | select_random_points | select_random_ranges |
---|---|---|---|---|
8 | 226.42% | 489.03% | 88.63% | 19.89% |
16 | 335.24% | 396.41% | 89.12% | 19.07% |
32 | 279.24% | 304.56% | 86.63% | 15.61% |
64 | 253.56% | 229.60% | 74.62% | 9.58% |
128 | 178.62% | 171.77% | 67.99% | 12.45% |
### 读写混合 | ||||
单表 5000,缓存表比较一般表提高的百分比(负增长契合预期) |
threads/–point_selects | 10 | 40 | 160 | 640 |
---|---|---|---|---|
8 | -35.77% | -42.24% | -44.88% | -45.24% |
16 | -64.39% | -68.91% | -70.73 | -52.90% |
32 | -62.79% | -71.56% | -72.60% | -56.84% |
64 | -46.61% | -42.44% | -64.08% | -50.05% |
128 | -57.58% | -68.03% | -57.36% | -49.18% |
单表 50000,缓存表比较一般表提高的百分比(负增长契合预期)
threads/–point_selects | 10 | 40 | 160 | 640 |
---|---|---|---|---|
8 | -12.01% | 27.14% | 12.19% | -59.36% |
16 | -28.97% | -35.04% | -53.55% | -61.83% |
32 | -36.20% | -44.74% | -45.94% | -31.65% |
64 | -46.13% | -0.93% | -41.86% | -5.53% |
128 | -43.21% | -39.93% | -59.82% | -57.15% |
成果显示,比较于一般表,缓存表在 oltp_point_select、oltp_read_only、select_random_points、select_random_ranges 几种只读的场景下功用有非常大的提高,但在包括写恳求的测验中无法供给更好的功用。它的机制决议了运用场景现在仅限于表的数据量不大的只读表,或许简直很少修正的小表。综上,尽管缓存表现在的运用场景比较照较单一,可是在适宜的场景下确实是一个处理了事务痛点的好功用,也等待在后续的版别中能有更高的稳定性和更优秀的功用体现。