mysql 并行操作_将MySQL去重操作优化到极致之三弹连发(二):多线程并行执行...

本文介绍了如何通过数据分片和并行执行来优化MySQL的去重操作。首先,通过查询将数据平均分成4份,然后创建4个独立的存储过程以并行执行去重任务,分别对应4个线程。最后,通过shell脚本和MySQL Schedule Event两种方式实现了并行执行,显著提高了处理速度。
摘要由CSDN通过智能技术生成

上一篇已经将单条查重语句调整到最优,但该语句是以单线程方式执行。能否利用多处理器,让去重操作多线程并行执行,从而进一步提高速度呢?比如我的实验环境是4处理器,如果使用4个线程同时执行查重sql,理论上应该接近4倍的性能提升。

一、数据分片

我们生成测试数据时,created_time采用每条记录加一秒的方式,也就是最大和在最小的时间差为50万秒,而且数据均匀分布。因此先把数据平均分成4份。

1. 查询出4份数据的created_time边界值select date_add('2017-01-01',interval 125000 second) dt1,

date_add('2017-01-01',interval 2*125000 second) dt2,

date_add('2017-01-01',interval 3*125000 second) dt3,

max(created_time) dt4

from t_source;        查询结果如图一所示。

ff7b9ae2f71449da2d9f5a4aa54cc1fe.png

图一2. 查看每份数据的记录数,确认数据平均分布select case when created_time >= '2017-01-01'

and created_time < '2017-01-02 10:43:20'

then '2017-01-01'

when created_time >= '2017-01-02 10:43:20'

and created_time < '2017-01-03 21:26:40'

then '2017-01-02 10:43:20'

when created_time >= '2017-01-03 21:26:40'

and created_time < '2017-01-05 08:10:00'

then '2017-01-03 21:26:40'

else '2017-01-05 08:10:00'

end min_dt,

case when created_time >= '2017-01-01'

and created_time < '2017-01-02 10:43:20'

then '2017-01-02 10:43:20'

when created_time >= '2017-01-02 10:43:20'

and created_time < '2017-01-03 21:26:40'

then '2017-01-03 21:26:40'

when created_time >= '2017-01-03 21:26:40'

and created_time < '2017-01-05 08:10:00'

then '2017-01-05 08:10:00'

else '2017-01-06 18:53:20'

end max_dt,

count(*)

from t_source

group by case when created_time >= '2017-01-01'

and created_time < '2017-01-02 10:43:20'

then '2017-01-01'

when created_time >= '2017-01-02 10:43:20'

and created_time < '2017-01-03 21:26:40'

then '2017-01-02 10:43:20'

when created_time >= '2017-01-03 21:26:40'

and created_time < '2017-01-05 08:10:00'

then '2017-01-03 21:26:40'

else '2017-01-05 08:10:00'

end,

case when created_time >= '2017-01-01'

and created_time < '2017-01-02 10:43:20'

then '2017-01-02 10:43:20'

when created_time >= '2017-01-02 10:43:20'

and created_time < '2017-01-03 21:26:40'

then '2017-01-03 21:26:40'

when created_time >= '2017-01-03 21:26:40'

and created_time < '2017-01-05 08:10:00'

then '2017-01-05 08:10:00'

else '2017-01-06 18:53:20'

end;

查询结果如图二所示。

239986c5871bc73f3e9e0cb563551833.png

图二4份数据的并集应该覆盖整个源数据集,并且数据之间是不重复的。也就是说4份数据的created_time要连续且互斥,连续保证处理全部数据,互斥确保了不需要二次查重。实际上这和时间范围分区的概念类似,或许用分区表更好些,只是这里省略了重建表的步骤。

3. 建立查重的存储过程

有了以上信息我们就可以写出4条语句处理全部数据。为了调用接口尽量简单,建立下面的存储过程。delimiter //

create procedure sp_unique(i smallint)

begin

set @a:='0000-00-00 00:00:00';

set @b:=' ';

if (i<4) then

insert into t_target

select * from t_source force index (idx_sort)

where created_time >= date_add('2017-01-01',interval (i-1)*125000 second)

and created_time < date_add('2017-01-01',interval i*125000 second)

and (@a!=created_time or @b!=item_name)

and (@a:=created_time) is not null

and (@b:=item_name) is not null

order by created_time,item_name;

commit;

else

insert into t_target

select * from t_source force index (idx_sort)

where created_time >= date_add('2017-01-01',interval (i-1)*125000 second)

and created_time <= date_add('2017-01-01',interval i*125000 second)

and (@a!=created_time or @b!=item_name)

and (@a:=created_time) is not null

and (@b:=item_name) is not null

order by created_time,item_name;

commit;

end if;

end

//

delimiter ;

查询的执行计划都如图三所示。

b862fc5af9d5933fa6f05679ba4a6824.png

图三mysql优化器进行索引范围扫描,并且使用索引条件下推(ICP)优化查询。

二、并行执行

下面分别使用shell后台进程和MySQL Schedule Event实现并行。

1. shell后台进程(1)建立duplicate_removal.sh文件,内容如下。#!/bin/bash

mysql -vvv -u root -p123456 test -e "truncate t_target" &>/dev/null

date '+%H:%M.%N'

for y in {1..4}

do

sql="call sp_unique($y)"

mysql -vvv -u root -p123456 test -e "$sql" &>par_sql1_$y.log &

done

wait

date '+%H:%M.%N'

(2)执行脚本文件chmod 755 duplicate_removal.sh

./duplicate_removal.sh

执行输出入图四所示。

89dab86990f9fa845e41ce70b9cf566e.png

图四

这种方法用时3.4秒,并行执行的4个过程调用分别用时如图五所示。

74793b0d43bff2b6702ffff8a257f59f.png

图五可以看到,每个过程的执行时间均不到3.4秒,因为是并行执行,总的过程执行时间也小于3.4秒,比单线程sql速度提高了近3倍。

2. MySQL Schedule Event

吴老师也用到了并行,但他是利用MySQL自带的Schedule Event功能实现的,代码应该和下面的类似。

(1)建立事件历史日志表-- 用于查看事件执行时间等信息

create table t_event_history (

dbname varchar(128) not null default '',

eventname varchar(128) not null default '',

starttime datetime(3) not null default '0000-00-00 00:00:00',

endtime datetime(3) default null,

issuccess int(11) default null,

duration int(11) default null,

errormessage varchar(512) default null,

randno int(11) default null

);

(2)修改event_scheduler参数set global event_scheduler = 1;

(3)为每个并发线程创建一个事件delimiter //

create event ev1 on schedule at current_timestamp + interval 1 hour on completion preserve disable do

begin

declare r_code char(5) default '00000';

declare r_msg text;

declare v_error integer;

declare v_starttime datetime default now(3);

declare v_randno integer default floor(rand()*100001);

insert into t_event_history (dbname,eventname,starttime,randno)

#作业名

values(database(),'ev1', v_starttime,v_randno);

begin

#异常处理段

declare continue handler for sqlexception

begin

set v_error = 1;

get diagnostics condition 1 r_code = returned_sqlstate , r_msg = message_text;

end;

#此处为实际调用的用户程序过程

call sp_unique(1);

end;

update t_event_history set endtime=now(3),issuccess=isnull(v_error),duration=timestampdiff(microsecond,starttime,now(3)), errormessage=concat('error=',r_code,', message=',r_msg),randno=null where starttime=v_starttime and randno=v_randno;

end

//

create event ev2 on schedule at current_timestamp + interval 1 hour on completion preserve disable do

begin

declare r_code char(5) default '00000';

declare r_msg text;

declare v_error integer;

declare v_starttime datetime default now(3);

declare v_randno integer default floor(rand()*100001);

insert into t_event_history (dbname,eventname,starttime,randno)

#作业名

values(database(),'ev2', v_starttime,v_randno);

begin

#异常处理段

declare continue handler for sqlexception

begin

set v_error = 1;

get diagnostics condition 1 r_code = returned_sqlstate , r_msg = message_text;

end;

#此处为实际调用的用户程序过程

call sp_unique(2);

end;

update t_event_history set endtime=now(3),issuccess=isnull(v_error),duration=timestampdiff(microsecond,starttime,now(3)), errormessage=concat('error=',r_code,', message=',r_msg),randno=null where starttime=v_starttime and randno=v_randno;

end

//

create event ev3 on schedule at current_timestamp + interval 1 hour on completion preserve disable do

begin

declare r_code char(5) default '00000';

declare r_msg text;

declare v_error integer;

declare v_starttime datetime default now(3);

declare v_randno integer default floor(rand()*100001);

insert into t_event_history (dbname,eventname,starttime,randno)

#作业名

values(database(),'ev3', v_starttime,v_randno);

begin

#异常处理段

declare continue handler for sqlexception

begin

set v_error = 1;

get diagnostics condition 1 r_code = returned_sqlstate , r_msg = message_text;

end;

#此处为实际调用的用户程序过程

call sp_unique(3);

end;

update t_event_history set endtime=now(3),issuccess=isnull(v_error),duration=timestampdiff(microsecond,starttime,now(3)), errormessage=concat('error=',r_code,', message=',r_msg),randno=null where starttime=v_starttime and randno=v_randno;

end

//

create event ev4 on schedule at current_timestamp + interval 1 hour on completion preserve disable do

begin

declare r_code char(5) default '00000';

declare r_msg text;

declare v_error integer;

declare v_starttime datetime default now(3);

declare v_randno integer default floor(rand()*100001);

insert into t_event_history (dbname,eventname,starttime,randno)

#作业名

values(database(),'ev4', v_starttime,v_randno);

begin

#异常处理段

declare continue handler for sqlexception

begin

set v_error = 1;

get diagnostics condition 1 r_code = returned_sqlstate , r_msg = message_text;

end;

#此处为实际调用的用户程序过程

call sp_unique(4);

end;

update t_event_history set endtime=now(3),issuccess=isnull(v_error),duration=timestampdiff(microsecond,starttime,now(3)), errormessage=concat('error=',r_code,', message=',r_msg),randno=null where starttime=v_starttime and randno=v_randno;

end

//

delimiter ;

说明:为了记录每个事件执行的时间,在事件定义中增加了操作日志表的逻辑,因为每个事件中只多执行了一条insert,一条update,4个事件总共多执行8条很简单的语句,对测试的影响可以忽略不计。执行时间精确到毫秒。

(4)触发事件执行mysql -vvv -u root -p123456 test -e "truncate t_target;alter event ev1 on schedule at current_timestamp enable;alter event ev2 on schedule at current_timestamp enable;alter event ev3 on schedule at current_timestamp enable;alter event ev4 on schedule at current_timestamp enable;"

说明:该命令行顺序触发了4个事件,但不会等前一个执行完才执行下一个,而是立即向下执行。从图六的输出也可以清楚地看到这一点。因此四次过程调用是并行执行的。

ebf8f980523ba412409e32d3a32290a1.png图六

(5)查看事件执行日志

select * from t_event_history;

查询结果如图7所示。

5cedee9a00900f9d9b5b759c9c2da16c.png

图七可以看到,每个过程的执行均为3.5秒,又因为是并行执行的,因此总的执行之间也是3.5秒,优化效果和shell后台进程方式几乎相同。

参考:

Increasing slow query performance with the parallel query execution

Mysql Event 调度历史记录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值