ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

sysbench原理剖析和实践

2021-10-13 20:59:12  阅读:686  来源: 互联网

标签:-- db 实践 剖析 lua test sb sysbench


sysbench是一款比较流行的测试工具,主要用于测试fileio、cpu、memory、threads、mutex等的性能测试,但是最主要的还是做数据库的性能测试,经常用来测试MySQL、Drizzle、AttachSQL、Oracle、PostgreSQL等相关的数据库,也可以扩展支持其他的数据库以及测试用例,本文主要介绍sysbench的代码逻辑、代码调试、lua脚本以及相关扩展功能。

本文的介绍主要基于sysbench 1.0的代码:
repo:https://github.com/akopytov/sysbench
branch:1.0

1. 代码逻辑

sysbench的代码逻辑如下(已经过滤掉一些不需要额外说明的代码文件):

sysbench/
├── src
│   ├── db_driver.c       // db驱动测试功能接口,主要串联sysbench测试框架和具体的db-driver
│   ├── db_driver.h       // db驱动接口申明,具体的实现见下面的drivers目录
│   ├── drivers           // 具体的driver驱动实现
│   │   ├── attachsql
│   │   ├── drizzle
│   │   ├── mysql
│   │   ├── oracle
│   │   └── pgsql
│   ├── lua               // 内置的oltp测试工具实现
│   │   ├── bulk_insert.lua
│   │   ├── internal
│   │   ├── oltp_common.lua
│   │   ├── oltp_delete.lua
│   │   ├── oltp_insert.lua
│   │   ├── oltp_point_select.lua
│   │   ├── oltp_read_only.lua
│   │   ├── oltp_read_write.lua
│   │   ├── oltp_update_index.lua
│   │   ├── oltp_update_non_index.lua
│   │   ├── oltp_write_only.lua
│   │   ├── select_random_points.lua
│   │   └── select_random_ranges.lua
│   ├── sb_barrier.h      // 线程屏障,用于线程的同步
│   ├── sb_ck_pr.h        // 原子操作,具体的在../third_party/concurrency_kit中实现
│   ├── sb_counter.h      // 计数器程序,由于统计
│   ├── sb_global.h
│   ├── sb_histogram.h    // todo
│   ├── sb_list.h         // List的C语言实现
│   ├── sb_logger.h       // logger实现
│   ├── sb_lua.c
│   ├── sb_lua.h          // 串联C和Lua程序,目标:1. Lua可以调用C中的db-driver-api;2. C可以调用Lua程序
│   ├── sb_options.h      // 参数解析实现
│   ├── sb_rand.h         // 随机数、字符串实现
│   ├── sb_thread.h       // 线程函数实现
│   ├── sb_timer.h        // 定时器实现
│   ├── sb_util.h         // 功能函数,主要是对齐(align)相关的宏
│   ├── sb_win.h          // 跨平台代码,windows支持
│   ├── sysbench.c        // sysbench的main函数
│   ├── sysbench.h
│   ├── tests             // 其他的测试模型程序,包括cpu、fileio等
│   │   ├── cpu
│   │   ├── fileio
│   │   ├── memory
│   │   ├── mutex
│   │   ├── sb_cpu.h
│   │   ├── sb_fileio.h
│   │   ├── sb_memory.h
│   │   ├── sb_mutex.h
│   │   ├── sb_threads.h
│   │   └── threads
│   └── xoroshiro128plus.h
├── tests                 // db-driver的测试用例
│   ├── include
│   │   ├── api_sql_common.sh
│   │   ├── config.sh.in
│   │   ├── drv_common.sh
│   │   ├── inspect.lua
│   │   ├── mysql_common.sh
│   │   ├── oltp_legacy
│   │   ├── pgsql_common.sh
│   │   ├── script_bulk_insert_common.sh
│   │   ├── script_oltp_common.sh
│   │   ├── script_oltp_legacy_common.sh
│   │   ├── script_select_random_common.sh
│   │   └── script_select_random_legacy_common.sh
│   ├── t                 // sysbench单元测试程序
│   └── test_run.sh
└── third_party           // sysbench的第三方依赖库
    ├── concurrency_kit   // 并发控制模块
    ├── cram              // 命令后程序测试框架
    └── luajit            // lua的jit编译器

阅读代码从sysbench.c中的main函数开始,流程逻辑还是比较清晰的:

// source file: src/sysbench.c
int main(int argc, char *argv[])
{
  sb_test_t *test = NULL;
  int rc;

  sb_globals.argc = argc;
  sb_globals.argv = malloc(argc * sizeof(char *));
  memcpy(sb_globals.argv, argv, argc * sizeof(char *));

  /* Initialize options library */
  sb_options_init();

  /* First register the logger */
  if (log_register())
    return EXIT_FAILURE;

  /* Register available tests */
  if (register_tests())
  {
    fprintf(stderr, "Failed to register tests.\n");
    return EXIT_FAILURE;
  }

  /* Parse command line arguments */
  if (parse_general_arguments(argc, argv))
    return EXIT_FAILURE;
  
  /* Initialize global variables and logger */
  if (init() || log_init() || sb_counters_init())
    return EXIT_FAILURE;

  print_header();

  test = sb_load_lua(NULL);
  current_test = test;

  /* Load and parse test-specific options */
  if (parse_test_arguments(test, argc, argv))
    return EXIT_FAILURE;

  /* prepare, cleanup, run. */
  if (!strcmp(sb_globals.cmdname, "prepare"))
  {
    rc = test->builtin_cmds.prepare();
  }
  else if (!strcmp(sb_globals.cmdname, "cleanup"))
  {
    rc = test->builtin_cmds.cleanup();
  }
  else if (!strcmp(sb_globals.cmdname, "run"))
  {
    rc = run_test(test) ? EXIT_FAILURE : EXIT_SUCCESS;
  }

  /* clean up. */
end:
  if (sb_lua_loaded())
    sb_lua_done();
  db_done();
  sb_counters_done();
  log_done();
  sb_options_done();
  sb_rand_done();
  sb_thread_done();
  free(timers);
  free(timers_copy);
  free(sb_globals.argv);
  return rc;
}

2. Lua脚本

Lua和C的亲和性非常好,被称为最好的胶水语言,Lua可以调用C中定义的方法,C也可以非常方便的调用Lua,为什么要C和Lua结合使用呢:

  • C的优势是高性能,但它是编译语言,程序发生变动,就需要重新进行编译,相对来说不太灵活;
  • Lua的优势是灵活,它是解释语言,无需编译,就可以执行,一般用来写一些经常变动的规则性代码。

那问题来了:Lua不会拖慢C的性能吗?那就不得不提LuaJIT了,它是Lua的虚拟机,主要功能就是将Lua程序编译成字节码程序去执行,LuaJIT比较轻量,在C程序里面很容易就能集成进去,这就使得Lua的性能并不会有大的影响,并且支持动态的加载Lua程序。
下面是一个Lua的简单的代码:

-- file: lua_test.lua
function domain(num)
    -- call C function.
    local tab = gettab()
    
    -- show key and value
    for k, v in pairs(tab) do
        print("key: ".. k)
        print("val: ".. v)
				print()
    end
end

下面是和C交互的代码:

// file: c_test.c
int get_tab(lua_State *L) {
    // create table.
    lua_newtable(L);
 
    // push (key, value).
    int i;
    char value[10] = {0};
    for(i=0; i<5; ++i) {
        sprintf(value, "value%d", i+1);
        lua_pushnumber(L, i+1);    //key
        lua_pushstring(L, value);  //value
        lua_settable(L, -3);       //push key,value
    }
 
    // deal return.
    return 1;
}
 
int main()
{
    // create a state and load standard library.
    lua_State* L = luaL_newstate();
    luaL_openlibs(L);
 
    // register function be called by lua.
    lua_register(L, "gettab", get_tab);
 
    // load and exec the specified lua file.
    int error = luaL_dofile(L, "lua_test.lua");
    if(error) {
        perror("luaL_dofile error");
        exit(1);
    }
 
    // get the domain function from lua file. 
    lua_getglobal(L, "domain");
 
    // exec the domain function.
    error = lua_pcall(L, 0, 0, 0);
    if (error) {
        fprintf(stderr, "%s\n",lua_tostring(L, -1));
        lua_pop(L, 1);
    }
 
    // close lua state.
    lua_close(L);
 
    return 0;
}

这种通过lua_State交互的方式对C代码的侵入较大,还有一种就是ffi的方式,可以参考这个链接:
https://moonbingbing.gitbooks.io/openresty-best-practices/content/lua/FFI.html
sysbench以上两种方式都用了:

  • 通过lua_State进行交互:主要用在Lua调用C中定义好的db-driver api;
  • ffi主要用在Lua使用C中定义好的结构体、字段类型、常规函数(sleep、随机函数)等。

3. 扩展功能

sysbench测试db场景比较简单,表结构如下:

CREATE TABLE `sbtest1` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `k` int(10) unsigned NOT NULL DEFAULT '0',
  `c` char(120) NOT NULL DEFAULT '',
  `pad` char(60) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`) BLOCK_SIZE 16384 GLOBAL
);

在我们测试其他场景,比如宽表、大字段、小字段等,这种场景用原生的sysbench测试脚本就力不从心了。
这个时候就需要修改代码的实现了,实现也比较简单,就是在Lua脚本中实现这几个函数就可以了:

// src/sb_lua.c
#define EVENT_FUNC       "event"       // worker线程执行内容
#define PREPARE_FUNC     "prepare"     // prepare阶段:创建表、预加载数据
#define CLEANUP_FUNC     "cleanup"     // cleanup阶段:清理数据等
#define HELP_FUNC        "help"        // help函数,展示测试用例的参数用法
#define THREAD_INIT_FUNC "thread_init" // worker线程初始化:设置运行参数、生成标名称等
#define THREAD_DONE_FUNC "thread_done" // worker线程执行完:清理线程中的资源
#define THREAD_RUN_FUNC  "thread_run"  // 一般无需实现,只需要关注event
#define INIT_FUNC        "init"        // 全局init:一般无需实现
#define DONE_FUNC        "done"        // 全局done:一般无需实现

sysbench自带的oltp的测试case就是这样实现的,主要包含两个文件:

  • oltp.lua:定义了thread_init、event函数;
  • common.lua:定义了prepare、cleanup函数。

下面写一个我们自己的测试case脚本:

-- file: test_cjf.lua
-- A simple test case.

function prepare()
  db_connect()
  -- create table.
  local query = [[
    CREATE TABLE `sbtest_cjf` (
      `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
      `k` int(10) unsigned NOT NULL DEFAULT '0',
      `c` char(120) NOT NULL DEFAULT '',
      `pad` char(60) NOT NULL DEFAULT '',
      PRIMARY KEY (`id`)
    )
  ]]
  db_query(query)
  print("create table sbtest_cjf succ.")

  -- insert 1000 records.
  local i
  for i = 1, 1000 do
    query = [[
      insert into sbtest_cjf(`id`, `k`, `c`, `pad`) values(
    ]] .. i .. ',' .. i + 1000 .. ", 'c', 'pad')"
    db_query(query)
  end
  print("insert 1000 record to sbtest_cjf succ.")
  return 0
end

function cleanup()
  db_query('DROP TABLE IF EXISTS sbtest_cjf')
  print('drop table sbtest_cjf succ.')
  return 0
end

function event()
  db_query('select * from sbtest_cjf where id = 1')
end

使用sysbench对上述test_cjf.lua进行测试:

$./src/sysbench ./tests/include/oltp_legacy/test_cjf.lua --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=root --mysql-password=xxx --mysql-db=dbtest --db-driver=mysql --report-interval=2 --threads=1 --time=10 prepare
sysbench 1.0.20-6cb07f3 (using bundled LuaJIT 2.1.0-beta2)

create table sbtest_cjf succ.
insert 1000 record to sbtest_cjf succ.

$./src/sysbench ./tests/include/oltp_legacy/test_cjf.lua --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=root --mysql-password=xxx --mysql-db=dbtest --db-driver=mysql --report-interval=2 --threads=1 --time=10 run
sysbench 1.0.20-6cb07f3 (using bundled LuaJIT 2.1.0-beta2)

Running the test with following options:
Number of threads: 1
Report intermediate results every 2 second(s)
Initializing random number generator from current time


Initializing worker threads...

Threads started!

[ 2s ] thds: 1 tps: 2182.39 qps: 2182.39 (r/w/o: 2182.39/0.00/0.00) lat (ms,95%): 0.55 err/s: 0.00 reconn/s: 0.00
[ 4s ] thds: 1 tps: 2323.41 qps: 2323.41 (r/w/o: 2323.41/0.00/0.00) lat (ms,95%): 0.47 err/s: 0.00 reconn/s: 0.00
[ 6s ] thds: 1 tps: 2299.49 qps: 2299.49 (r/w/o: 2299.49/0.00/0.00) lat (ms,95%): 0.47 err/s: 0.00 reconn/s: 0.00
[ 8s ] thds: 1 tps: 2304.50 qps: 2304.50 (r/w/o: 2304.50/0.00/0.00) lat (ms,95%): 0.48 err/s: 0.00 reconn/s: 0.00
SQL statistics:
    queries performed:
        read:                            22804
        write:                           0
        other:                           0
        total:                           22804
    transactions:                        22804  (2279.41 per sec.)
    queries:                             22804  (2279.41 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          10.0024s
    total number of events:              22804

Latency (ms):
         min:                                    0.40
         avg:                                    0.44
         max:                                   14.68
         95th percentile:                        0.53
         sum:                                 9974.14

Threads fairness:
    events (avg/stddev):           22804.0000/0.00
    execution time (avg/stddev):   9.9741/0.00


$./src/sysbench ./tests/include/oltp_legacy/test_cjf.lua --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=root --mysql-password=xxx --mysql-db=dbtest --db-driver=mysql --report-interval=2 --threads=1 --time=10 cleanup
sysbench 1.0.20-6cb07f3 (using bundled LuaJIT 2.1.0-beta2)

drop table sbtest_cjf succ.

4. 参考文档

Lua教程:https://www.w3xue.com/manual/lua/Lua-Chinese.pdf
GDB入门:https://zhuanlan.zhihu.com/p/74897601
GDB命令:https://developer.aliyun.com/article/232348

标签:--,db,实践,剖析,lua,test,sb,sysbench
来源: https://blog.csdn.net/cjfeii/article/details/120751934

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有