写点什么

手把手教你改 sysbench 代码

  • 2023-03-10
    北京
  • 本文字数:4105 字

    阅读完需:约 13 分钟

作者: GangShen 原文来源:https://tidb.net/blog/a8c2981d

sysbench 原始代码介绍

   安装完 sysbench 之后,可以在 /usr/share/sysbench 目录下看到一些 .lua 的脚本,这些脚本就是 sysbench 程序在进行 oltp 压测时用到的 lua 脚本代码,修改目录下的脚本即可修改 sysbench oltp 压测的行为。
复制代码


[tidb@172-16-120-12 sysbench]$ ll /usr/share/sysbench/总用量 64-rwxr-xr-x 1 root root  1452 3月  16 2019 bulk_insert.lua-rw-r--r-- 1 root root 14659 6月  28 2022 oltp_common.lua-rwxr-xr-x 1 root root  1290 3月  16 2019 oltp_delete.lua-rwxr-xr-x 1 root root  2415 3月  16 2019 oltp_insert.lua-rwxr-xr-x 1 root root  1265 3月  16 2019 oltp_point_select.lua-rwxr-xr-x 1 root root  1649 3月  16 2019 oltp_read_only.lua-rwxr-xr-x 1 root root  1824 3月  16 2019 oltp_read_write.lua-rwxr-xr-x 1 root root  1118 3月  16 2019 oltp_update_index.lua-rwxr-xr-x 1 root root  1127 3月  16 2019 oltp_update_non_index.lua-rwxr-xr-x 1 root root  1440 3月  16 2019 oltp_write_only.lua-rwxr-xr-x 1 root root  1919 3月  16 2019 select_random_points.lua-rwxr-xr-x 1 root root  2118 3月  16 2019 select_random_ranges.luadrwxr-xr-x 4 root root  4096 6月  28 2022 tests
复制代码


  • oltp_point_select.lua/oltp_read_write.lua/oltp_update_index.lua 等脚本是使用 sysbench 进行 OLTP 各项测试的入口

  • oltp_common.lua 包含了一些常见的 OLTP 基准测试场景和数据模型,包括创建表、插入数据、查询数据等

  • sysbench prepare 建表以及生成数据的实现逻辑在 `function create_table(drv, con, table_num)` 实现。如果要自定义的表结构或者并发生成数据,主要是通过修改 create_table 函数的逻辑来实现。

修改 1 :修改表结构提高单表记录最大数量

   默认 sysbench 创建的表结构为
复制代码


CREATE TABLE `sbtest1` (  `id` int(11) NOT NULL,  `k` int(11) NOT NULL DEFAULT '0',  `c` char(120) NOT NULL DEFAULT '',  `pad` char(60) NOT NULL DEFAULT '',  PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,  KEY `k_1` (`k`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
复制代码


   由于 int 类型最大值为 2147483647 ,所以当 id 从 1 开始自增的时候,单表最多只能存 2147483647 条记录,为了测试更大容量的单表性能情况,需要修改建表定义。
复制代码


修改主要内容


修改 2:修改非 AUTO_INCREMENT 主键起始值

   sysbench 程序在建完表结构之后,开始往表结构通过 INSERT 方式造数。如果 sysbench 命令设置了 --auto-inc 为 true (默认值为 true),则生成 INSERT INTO sbtestN (k,c,pad) VALUES()()().... 的语句写入,id 字段的值依赖于 auto\_increment 自动生成;如果 sysbench 命令设置了 --auto-inc 为 false 则生成 INSERT INTO sbtestN(id,k,c,pad) VALUES()()().... 的语句写入,id 字段的值由下面这个 for 循环迭代生成,显式指定。
复制代码


   for i = 1, sysbench.opt.table_size do
c_val = get_c_value() pad_val = get_pad_value()
if (sysbench.opt.auto_inc) then query = string.format("(%d, '%s', '%s')", sb_rand(1, sysbench.opt.table_size), c_val, pad_val) else query = string.format("(%d, %d, '%s', '%s')", i, sb_rand(1, sysbench.opt.table_size), c_val, pad_val) end
con:bulk_insert_next(query) end
复制代码


   原始的 sysbench 程序,在造数过程中如果遇到错误中断,则无法通过重新运行程序继续造数,需要将之前的数据清理干净从头开始造,对于大表造数的中断,影响比较大。所以需要修改造数脚本,让 sysbench 在错误中断之后,继续造数。默认每次重新运行 sysbench 程序 id 都是从 1 开始生成,直到 table-size 值,如果直接重新运行 sysbench 会在两个地方产生错误:
复制代码


  1. 表结构已经存在,重复建表会导致 DDL 错误

  2. id 主键从 1 开始生成,会产生主键冲突的错误


修改方式:


  1. 对于重复建表的问题,可以在建表语句末尾加上 if not exists 定义,或者将执行建表语句行直接注释,跳过建表,con:query(query) 这一行就是在执行建表语句,通过 – 将代码注释。

  2. 对于 id 主键冲突的问题,比较简单的方式就是直接修改 for 循环的起始值和结束值,查询出中断时候,表内已有数据的最大 id 值,for 循环从 max(id)+1 开始,到 table-size 结束。

  3.  但是这种修改方式主要是针对单表造数的情况,如果有多张表的造数,sysbench 遇到错误中断退出后,每个表的 max(id) 值可能是不同的,如果对数据数量以及准确度要求完全跟原生 sysbench 产生的一样,可以将 INSERT 语句修改为 REPLACE 语句,并且 for 循环起始位置取多张表的 max(id) 中的最小值来完成。

修改 3:提升单表造数并发度

   sysbench 在 Prepare 造数时,每个表只有 1 个线程进行造数,当要造的单表数量比较大的情况下,造数效率比较低。看如何修改 sysbench 脚本,在 Prepare 造数阶段实现单表并发写入数据,提升造数效率。正常应该按照 lua 语言的协程方式去实现并发写入,但是对 lua 学艺不精,看了 Chatgpt 给出的关于 lua 协程的 demo 程序也比较复杂,放弃让 sysbench 脚本直接支持并发。
考虑到前面修改 for 循环起始位置可以控制生成的记录数量以及主键范围,想到一种比较粗糙的实现并发的方式:
通过 for 循环起始值和结束值控制生成的数据范围,同时启动多个 sysbench 程序同时写入数据,就能达到并发写入的效果。
要实现同时启动多个 sysbench 程序同时写入,通过直接修改代码写入数值的方式就比较麻烦,并且 lua 是脚本语言,在前面 sysbench 运行过程中,修改 lua 脚本,可能会导致不预期的行为。所以考虑通过命令行参数的方式将起始值和结束值传入。
复制代码


for i = 1, sysbench.opt.table_size do
复制代码


   原始 for 循环中的起始值固定为 1 ,结束值是 sysbench 命令行中传入的 --table-size 值,所以添加一个参数的传入即可。
复制代码




   修改之后 sysbench prepare 可以通过在命令行修改 --start-id 和 --table-size 参数来控制生成的记录数量及范围
复制代码


sysbench --config-file=tidbconfig oltp_read_write_tidb --mysql-db=sbdb  --tables=1 --table-size=2200000000 --mysql-ignore-errors=all --auto-inc=false  --start-id=2100000001 prepare
复制代码


   实现了传参控制 sysbench 生成数据之后,还是需要手动进行分批,为了解决这一部分工作量的问题,可以考虑配合一个简单的 shell 脚本生成 sysbench 造数命令。
这个脚本由 ChatGPT 生成,执行脚本时需要传入三个参数:id 起始值, id 结束值,batch 值,每个 sysbench 进程负责完成 batch 数量的记录写入。\`sh parall\_prapare.sh 1 5000000000 100000000\` 表示总共需要完成 id 从 1 到 5000000000 行记录的写入,每个 sysbench 进程负责 100000000 行记录的写入,也就是会生成 50 个 sysbench 进程同时进行造数,达到 50 个并发的同时写入的效果。
复制代码


#!/bin/bash
# Check if start, end and batch values are providedif [ $# -ne 3 ]; then echo "Usage: $0 start end batch" exit 1fi
# Parse the argumentsstart=$1end=$2batch=$3
# Set the initial value of tmp to starttmps=$starttmpe=$batch# Loop until tmp is greater than endwhile [ $tmps -le $end ]do echo "Start from $tmps to $tmpe" sysbench_cmd="sysbench --config-file=tidbconfig oltp_read_write_tidb --mysql-db=sbdb --tables=1 --start-id=$tmps --table-size=$tmpe --mysql-ignore-errors=all --auto-inc=false prepare" #echo "execute $sysbench_cmd" nohup sysbench --config-file=tidbconfig oltp_read_write_tidb --mysql-db=sbdb --tables=1 --start-id=$tmps --table-size=$tmpe --mysql-ignore-errors=all --auto-inc=false prepare > nohup_$tmps.log & #sleep 3 tmps=$((tmps + batch)) tmpe=$((tmpe + batch))done
echo "Done!"
复制代码

修改 4:控制每个 INSERT 语句记录行数

   前面看 sysbench 造数的时候,主要是调用了 bulk\_insert\_init() ,bulk\_insert\_next(),bulk\_insert\_done() 三个接口来完成初始数据的写入,具体实现是通过 C++ 实现的:
复制代码


  • bulk_insert_init() 初始化 INSERT INTO VALUES 语句,并开启事务

  • bulk_insert_next() 拼接生成的每一行记录值 (),(),()… ,通过代码看到当拼接的字符串长度大于 BULK_PACKET_SIZE = 524288 之后,就会执行当前 SQL 语句,在 autocommit 模式下等于提交事务

  • bulk_insert_done() 表示提交事务

  • 通过 bulk_insert_next() 拼接的 INSERT 语句提交的时机是通过字符串长度进行控制的,所以单个 INSERT 提交多少行记录是不固定的,当我们需要控制 sysbench 按照 500 行记录一次提交的时候,需要修改 sysbench 相关代码。相关接口已经实现,只需要修改一下调用接口的逻辑即可,在 for 循环中记录已经拼接的记录行数,当记录行数达到 500 行,调用 bulk_insert_done() 提交事务,然后重新初始化字符串。


独立修改的测试代码

   为了避免上述我们对于 oltp\_common.lua 的修改影响 sysbench 压测 MySQL 的逻辑,我们可以将 oltp\_common.lua 拷贝一份 oltp\_common\_tidb.lua 进行修改,并且拷贝一份 oltp\_read\_write.lua 命名为 oltp\_read\_write\_tidb.lua 。将 oltp\_read\_write\_tidb.lua 文件中的 require 修改为 require("oltp\_common\_tidb") 就可以正常引用到 oltp\_common\_tidb.lua 的代码。
复制代码


调用命令方式


sysbench --config-file=tidbconfig oltp_read_write_tidb --mysql-db=sbdb  --tables=1 --start-id=1 --table-size=1000 --mysql-ignore-errors=all --auto-inc=false  prepare
复制代码

附完整修改代码

  • oltp_common_tidb.lua

  • oltp_read_write_tidb.lua

  • 使用 10 并发进行造数例子


发布于: 刚刚阅读数: 2
用户头像

TiDB 社区官网:https://tidb.net/ 2021-12-15 加入

TiDB 社区干货传送门是由 TiDB 社区中布道师组委会自发组织的 TiDB 社区优质内容对外宣布的栏目,旨在加深 TiDBer 之间的交流和学习。一起构建有爱、互助、共创共建的 TiDB 社区 https://tidb.net/

评论

发布
暂无评论
手把手教你改 sysbench 代码_开发语言_TiDB 社区干货传送门_InfoQ写作社区