我们知道在asbench中可以在命令行中指定测试时启动的并行线程数,这个测试过程是使用run命令,而且是多线程并发的,所以我们可以使用asbench的run命令来造数据,而不再使用其提供的prepare命令的方法来造数据。run命令会根据命令行参数—num-threads来指定并发线程数的多少。
在asbench中自定义的lua脚本中要求实现以下几个函数:
function thread_init(thread_id): 此函数在线程创建后只被执行一次
function event(thread_id): 每执行一次就会被调用一次。
由上可以知道,本次造数据的脚本我们只需要实现thread_init()函数就可以了,在此函数内调用sqlload装载数据即可。
我们知道sqlload工具是把一个文件中的数据快速装载到Oracle数据库中,但如果我们生成的数据是几百G以上,那么生成一个放在头盘上的文本文件不仅会占用空间,在读写过程中也会产生IO导致慢,所以最好的方案是程序直接生成数据,然后直接就通过sqlload装载到数据库中,这样的性能是最高的,这可以通过管道文件的方式来实现。造数据的程序把造的数据往管道中写,而sqlload从管道中读,这样就避免了数据需要落磁盘产生IO导致慢的问题。
虽然通过lua脚本也能生成asbench要的测试数据,但其中的asbench在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);
memset((void*)&r1, 0, sizeof(r1));
memset((void*)&r2, 0, sizeof(r1));
memset((void*)rand_state1, 0, sizeof(rand_state1));
memset((void*)rand_state2, 0, sizeof(rand_state2));
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
为了使用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
假设sqlload_prepare2.lua此脚本放在当前目录下的子目录lua下,我们需要把生成数据的程序也拷贝到当前目录下,然后运行下面的命令就可以并行装载数据了:
./asbench_ora11 --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并发的装载数据。
asbench下载: https://gitee.com/csudata/asbench/releases/download/v0.1/asbench0.1.tar.xz