作者: Jayjlchen 原文来源:https://tidb.net/blog/a55941b1
背景
用户在使用 4.0.14 版本进行数据库迁移时,发现通过 dumpling 导出的全量备份体积为 3.5T,而每个 TiKV 节点分别占用 1.36T 的存储空间,总共占用了 4.08T 的空间。用户想了解导出的 CSV 文件的体积应该如何估算,并且为什么导出的单副本 CSV 文件体积接近整个集群的体积。
说明
下面用实验来说明平面文件与数据体积相关的关系:
sysbench 造数 10 张表。
 sysbench --config-file=tidb_config oltp_read_write --tables=10 --table-size=2000000 --db-ps-mode=auto --rand-type=uniform --mysql-ignore-errors=2013 prepare
       复制代码
 
根据官网的“如何预估 TiDB 中的一张表的大小”,我们可以查到表的压缩前和压缩后的体积。
https://docs.pingcap.com/zh/tidb/v6.5/manage-cluster-faq#%E5%A6%82%E4%BD%95%E9%A2%84%E4%BC%B0-tidb-%E4%B8%AD%E4%B8%80%E5%BC%A0%E8%A1%A8%E7%9A%84%E5%A4%A7%E5%B0%8F
 SELECT    db_name,    table_name,    ROUND(SUM(total_size / cnt), 2) Approximate_Size,    ROUND(SUM(total_size / cnt / (SELECT                    ROUND(AVG(value), 2)                FROM                    METRICS_SCHEMA.store_size_amplification                WHERE                    value > 0)),            2) Disk_SizeFROM    (SELECT        db_name,            table_name,            region_id,            SUM(Approximate_Size) total_size,            COUNT(*) cnt    FROM        information_schema.TIKV_REGION_STATUS    WHERE        db_name = 'sbtest'    GROUP BY db_name , table_name , region_id) tabinfoGROUP BY db_name , table_name;
#sum(Approximate_Size)是 6.18 GB#sum(Disk_Size)是 3.93 GB+---------+------------+------------------+-----------+| db_name | table_name | Approximate_Size | Disk_Size |+---------+------------+------------------+-----------+| sbtest  | sbtest5    |           609.00 |    392.90 || sbtest  | sbtest6    |           631.00 |    407.10 || sbtest  | sbtest9    |           699.00 |    450.97 || sbtest  | sbtest4    |           711.00 |    458.71 || sbtest  | sbtest3    |           712.00 |    459.35 || sbtest  | sbtest2    |           597.00 |    385.16 || sbtest  | sbtest8    |           597.00 |    385.16 || sbtest  | sbtest10   |           711.00 |    458.71 || sbtest  | sbtest1    |           593.00 |    382.58 || sbtest  | sbtest7    |           728.00 |    469.68 |+---------+------------+------------------+-----------+10 rows in set (0.01 sec)
       复制代码
 
在 grafana 的 <tidb-cluster>-PD 的 Statistics-balance 面板的 Store used 、Store Region size、Size amplification 也可以观察到。
Store Region size 可以理解为“压缩前的体积”;
Store used 可以理解为“压缩后的体积”;
Size amplification 是“压缩比平均值”。
Store used ≈ Store Region size / Size amplification
注:本实验的数据体积过小,没有多少压缩比可言。TiDB 默认的层级压缩参数为compression-per-level = ["no", "no", "lz4", "lz4", "lz4", "zstd", "zstd"], 在数据量大时,压缩比高。
导出的 CSV 体积是否等于 “Store Region size 总和 / 3 (副本)” 呢?
不是的。CSV 导入到 TiDB 后,转换为 region 的体积会有所放大。此外,还需要考虑索引大小的影响,因此 Store Region size 通常会显得偏大。
导出的 CSV 文件实际上是单一副本,不包含索引数据量,可以理解为略大于 DATA_LENGTH 的总和。
为什么说是大于 DATA_LENGTH 总和呢?
由于 DATA_LENGTH 的计算依赖于数据库的统计信息,而实际情况中,数据库中常常存在统计信息尚未被收集或已经过时,但尚未达到自动触发 auto analyze 操作的标准的情况。
验证过程:
 ################ 1.CSV 体积基本等于 DATA_LENGTH 总和的情况 ######################## #数据是 sysbench insert 而来,会自动触发统计信息收集MySQL [(none)]> select table_schema,sum(DATA_LENGTH)/1024/1024/1024 from information_schema.tables where TABLE_SCHEMA = 'sbtest' group by table_schema;+--------------+---------------------------------+| table_schema | sum(DATA_LENGTH)/1024/1024/1024 |+--------------+---------------------------------+| sbtest       |                  3.764550373890 |+--------------+---------------------------------+1 row in set (0.01 sec)
nohup ./dumpling \  -u root \  -P 4000 \  -h 172.16.201.22 \  -t 8 \  -o /data/backup \  -r 200000 \  -F 256MiB \  --filetype csv \  -B sbtest > nohup.out &  [tidb@vm172-16-201-22 backup]$ du -sh3.8G
# 表的平均行长为 199 bytes,抽样验证导出的 CSV 文件也是每行 199 bytes 左右。MySQL [(none)]> select TABLE_NAME,AVG_ROW_LENGTH from information_schema.tables where table_schema='sbtest' AND table_name='sbtest1';+------------+----------------+| TABLE_NAME | AVG_ROW_LENGTH |+------------+----------------+| sbtest1    |            199 |+------------+----------------+1 row in set (0.01 sec)
[tidb@vm172-16-201-22 backup]$ lines=$(wc -l < sbtest.sbtest1.0000000000000.csv)[tidb@vm172-16-201-22 backup]$ bytes=$(wc -c < sbtest.sbtest1.0000000000000.csv)[tidb@vm172-16-201-22 backup]$ avg_line_length=$(echo "scale=2; $bytes / $lines" | bc)[tidb@vm172-16-201-22 backup]$ echo "Average line length: $avg_line_length bytes"Average line length: 198.84 bytes
################ 2.CSV 体积大于 DATA_LENGTH 总和的情况 ######################## #数据是 lightning 设置 analyze = "off" 导入的,缺失统计信息mysql> select DATA_LENGTH from information_schema.tables where table_name='sbtest1';+-------------+| DATA_LENGTH |+-------------+|           0 |+-------------+1 row in set (0.01 sec)
#lightning local 模式导入,Modify_count 和 Row_count 会为 0,所以也不会自动触发统计信息收集 mysql> show stats_meta where table_name='sbtest1';+---------+------------+----------------+---------------------+--------------+-----------+| Db_name | Table_name | Partition_name | Update_time         | Modify_count | Row_count |+---------+------------+----------------+---------------------+--------------+-----------+| sbtest  | sbtest1    |                | 2024-05-21 20:43:12 |            0 |         0 |+---------+------------+----------------+---------------------+--------------+-----------+1 row in set (0.00 sec)
#不能用健康度判断统计信息是否准确,即使是新建一张空表,没收集统计信息时,也显示 100mysql> show stats_healthy where table_name='sbtest1';+---------+------------+----------------+---------+| Db_name | Table_name | Partition_name | Healthy |+---------+------------+----------------+---------+| sbtest  | sbtest1    |                |     100 |+---------+------------+----------------+---------+1 row in set (0.00 sec)
mysql> select count(*) from sbtest1;+----------+| count(*) |+----------+|  2000000 |+----------+1 row in set (0.51 sec)
       复制代码
 用户验证
导出的 CSV 大小等于 DATA_LENGTH 的总和。
后话
估算导出的 CSV 平面文件的体积相对容易。但如果要估算 CSV 文件导入后的体积,这就比较困难了。因为每个行业的数据特点不同,压缩率也会有所差异。举个不严谨的例子,一亿条相同的数据,压缩后可能只占用一条的空间。较为精确的估算方法是,先导入 10% 的 CSV 文件,然后根据观察到的体积来估算最终的导入体积。
如果一定要给出一个经验比例值,那么大致可以认为 3T 的 CSV 文件导入后会占用 4.5 至 6T 的存储空间(包括三副本),就算宽裕估算比值也是 1 : 2 的关系。相比于 MySQL 三副本使用的数据体积,这个压缩率已经相当可观。
评论