Oracle sqlload脚本,osdba's blog : sysbench测试Oracle时写一个lua脚本调用sqlload快速造数据...

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

#include

#include

#include

#include

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 \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

{

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. 此脚本的下载及其它

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值