内容简介:sysbench测试Oracle时写一个lua脚本调用sqlload快速造数据
Posted on 2016-03-13 10:20:48 by osdba
1. 背景
如果用sysbench默认的功能来造数据,其原理是一条一条insert进数据库,同时其使用prepare命令是不能并行执行的,所以当要造几百G以上的数据时,会很慢,为此我写了一个sysbench的 lua 脚本,此脚本会启动多个线程并行调用sqlload快速造出大量的数据。
2. 实现过程
2.1 如何实现并行
我们知道在sysbench0.5中可以在命令行中指定测试时启动的并行线程数,这个测试过程是使用run命令,而且是多线程并发的,所以我们可以使用sysbench的run命令来造数据,而不再使用其提供的prepare命令的方法来造数据。run命令会根据命令行参数--num-threads来指定并发线程数的多少。
在sysbench中自定义的lua脚本中要求实现以下几个函数:
- function thread_init(thread_id): 此函数在线程创建后只被执行一次
- function event(thread_id): 每执行一次就会被调用一次。
由上可以知道,本次造数据的脚本我们只需要实现thread_init()函数就可以了,在此函数内调用sqlload装载数据即可。
2.2 生成数据的效率问题
我们知道sqlload工具是把一个文件中的数据快速装载到Oracle数据库中,但如果我们生成的数据是几百G以上,那么生成一个放在头盘上的文本文件不仅会占用空间,在读写过程中也会产生IO导致慢,所以最好的方案是程序直接生成数据,然后直接就通过sqlload装载到数据库中,这样的性能是最高的,这可以通过管道文件的方式来实现。造数据的程序把造的数据往管道中写,而sqlload从管道中读,这样就避免了数据需要落磁盘产生IO导致慢的问题。
虽然通过lua脚本也能生成sysbench要的测试数据,但其中的sysbench在lua脚本中提供的随机函数sb_rand及sb_rand_uniform还是太够快,这会影响数据的装载速度,为了达到一种极致的性能,我们可以写一个 C语言 的程序来生成所需要的数据。 此C程序为gendata.c,内容如下:
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <stdint.h> #include <sys/time.h> uint64_t my_rand(struct random_data * r1, struct random_data * r2) { uint64_t rand_max = 100000000000LL; uint64_t result; uint32_t u1, u2; random_r(r1, &u1); random_r(r2, &u2); result = (int64_t)u1 * (int64_t)u2; result = result % rand_max; return result; } int main(int argc, char *argv[]) { struct timeval tpstart; struct random_data r1, r2; int i; int r; int max_value; char rand_state1[128]; char rand_state2[128]; if (argc !=2) { printf("Usage: %s <rownums>\n", argv[0]); return 1; } max_value = atoi(argv[1]); gettimeofday(&tpstart,NULL); initstate_r(tpstart.tv_usec,rand_state1,sizeof(rand_state1),&r1); srandom_r(tpstart.tv_usec, &r1); gettimeofday(&tpstart,NULL); initstate_r(tpstart.tv_usec,rand_state2,sizeof(rand_state1),&r2); srandom_r(tpstart.tv_usec, &r2); for (i=1; i<max_value+1; i++) { r = my_rand(&r1, &r2) % max_value; printf("%d,%d,%011llu-%011llu-%011llu-%011llu-%011llu-%011llu-%011llu-%011llu-%011llu-%011llu,%011llu-%011llu-%011llu-%011llu-%011llu\n", i, r, my_rand(&r1, &r2), my_rand(&r1, &r2), my_rand(&r1, &r2), my_rand(&r1, &r2), my_rand(&r1, &r2), my_rand(&r1, &r2), my_rand(&r1, &r2), my_rand(&r1, &r2), my_rand(&r1, &r2), my_rand(&r1, &r2), my_rand(&r1, &r2), my_rand(&r1, &r2), my_rand(&r1, &r2), my_rand(&r1, &r2), my_rand(&r1, &r2) ); } return 0; }
编译此C语言程序的方法如下:
gcc gendata.c -o gendata
2.3 装载数据的lua脚本的设计
为了使用sqlload,需要生成一个控制文件,这个控制文件可由lua脚本自动生成,格式类似如下:
unrecoverable load data infile 'sbtest9.dat' append into table sbtest9 fields terminated by "," (id,k,c,pad)
其中的“infile 'sbtest9.dat'”中的sbtest9.dat文件是一个管道文件,这个管道文件也由lua脚本自动生成。
实现以上功能的lua脚本sqlload_prepare2.lua的内容如下:
pathtest = string.match(test, "(.*/)") or "" dofile(pathtest .. "common.lua") function sqlload(table_id) local ctl local f local i local c_val local pad_val local content local query query = [[ CREATE TABLE sbtest]] .. table_id .. [[ ( id INTEGER NOT NULL, k INTEGER, c CHAR(120) DEFAULT '' NOT NULL, pad CHAR(60) DEFAULT '' NOT NULL, PRIMARY KEY (id) ) ]] db_query(query) content = [[ unrecoverable load data infile 'sbtest]] .. table_id .. [[.dat' append into table sbtest]]..table_id..[[ fields terminated by "," (id,k,c,pad) ]] f = assert(io.open('sbtest'..table_id .. '.ctl', 'w')) f:write(content) f:close() os.execute('mknod sbtest'..table_id..'.dat p') os.execute ('./gendata ' .. oltp_table_size .. ' >> sbtest'..table_id ..'.dat &') os.execute ('sqlldr -skip_unusable_indexes userid='..oracle_user.. '/'..oracle_password .. ' control=sbtest'..table_id ..'.ctl direct=true') end function create_index_and_seq(table_id) db_query("CREATE SEQUENCE sbtest" .. table_id .. "_seq CACHE 10 START WITH ".. (oltp_table_size+1) ) db_query([[CREATE TRIGGER sbtest]] .. table_id .. [[_trig BEFORE INSERT ON sbtest]] .. table_id .. [[ FOR EACH ROW BEGIN SELECT sbtest]] .. table_id .. [[_seq.nextval INTO :new.id FROM DUAL; END;]]) db_query("COMMIT") db_query("CREATE INDEX k_" .. table_id .. " on sbtest" .. table_id .. "(k)") end function thread_init(thread_id) local index_name local i set_vars() print("thread prepare"..thread_id) if (oltp_secondary) then index_name = "KEY xid" else index_name = "PRIMARY KEY" end for i=thread_id+1, oltp_tables_count, num_threads do sqlload(i) create_index_and_seq(i) end end function event(thread_id) os.exit() end
3. 使用此脚本造数据
假设sqlload_prepare2.lua此脚本放在当前目录下的子目录lua下,我们需要把生成数据的程序也拷贝到当前目录下,然后运行下面的命令就可以并行装载数据了:
./sysbench_ora --test=lua/sqlload_prepare2.lua \ --oltp-table-name=sysbench \ --oltp-table-size=10000000 \ --oltp-tables-count=320 \ --oracle-db=bcache \ --oracle-user=sysbench \ --oracle-password=sysbench \ --max-time=7200 \ --max-requests=0 \ --num-threads=32 \ --db-driver=oracle \ --report-interval=1 \ run
上面的命令会开32个sqlload并发的装载数据。
4. 此脚本的下载及其它
- sysbench程序可以从github上下载: https://github.com/osdba/sysbench_bin
- gendata.c下载:gendata.c
- sqlload_prepare2.lua下载:sqlload_prepare2.lua
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- MySQL使用UDF调用shell脚本
- 在新的,干净的PowerShell实例中调用PowerShell脚本(在另一个脚本中)
- Nodejs 调用 R 脚本 / Nodejs Call R Script
- Go调用企业微信API发送zabbix告警信息脚本
- 新姿势!Redis中调用Lua脚本以实现原子性操作
- 使用Python和Java调用Shell脚本时的死锁陷阱
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。