日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

如何将MySQL去重

發布時間:2023/12/19 综合教程 27 生活家
生活随笔 收集整理的這篇文章主要介紹了 如何将MySQL去重 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這篇文章給大家分享的是有關如何將MySQL去重的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

?問題提出

源表t_source結構如下:

item_idint,
created_timedatetime,
modified_timedatetime,
item_namevarchar(20),
othervarchar(20)

要求:

1.源表中有100萬條數據,其中有50萬created_time和item_name重復。
2.要把去重后的50萬數據寫入到目標表。
3.重復created_time和item_name的多條數據,可以保留任意一條,不做規則限制。

?實驗環境

Linux虛機:CentOS release 6.4;8G物理內存(MySQL配置4G);100G機械硬盤;雙物理CPU雙核,共四個處理器;MySQL 8.0.16。

?建立測試表和數據

--建立源表
createtablet_source
(item_idint,
created_timedatetime,
modified_timedatetime,
item_namevarchar(20),
othervarchar(20)
);
--建立目標表
createtablet_targetliket_source;
--生成100萬測試數據,其中有50萬created_time和item_name重復
delimiter//
createproceduresp_generate_data()
begin
set@i:=1;
while@i<=500000do
set@created_time:=date_add('2017-01-01',interval@isecond);
set@modified_time:=@created_time;
set@item_name:=concat('a',@i);
insertintot_source
values(@i,@created_time,@modified_time,@item_name,'other');
set@i:=@i+1;
endwhile;
commit;
set@last_insert_id:=500000;
insertintot_source
selectitem_id+@last_insert_id,
created_time,
date_add(modified_time,interval@last_insert_idsecond),
item_name,
'other'
fromt_source;
commit;
end
//
delimiter;
callsp_generate_data();

--源表沒有主鍵或唯一性約束,有可能存在兩條完全一樣的數據,所以再插入一條記錄模擬這種情況。
insertintot_sourceselect*fromt_sourcewhereitem_id=1;

源表中有1000001條記錄,去重后的目標表應該有500000條記錄。
mysql>selectcount(*),count(distinctcreated_time,item_name)fromt_source;
+----------+----------------------------------------+
|count(*)|count(distinctcreated_time,item_name)|
+----------+----------------------------------------+
|1000001|500000|
+----------+----------------------------------------+
1rowinset(1.92sec)

一、巧用索引與變量

1. 無索引對比測試

(1)使用相關子查詢

truncatet_target;
insertintot_target
selectdistinctt1.*fromt_sourcet1whereitem_idin
(selectmin(item_id)fromt_sourcet2wheret1.created_time=t2.created_timeandt1.item_name=t2.item_name);

這個語句很長時間都出不來結果,只看一下執行計劃吧。

mysql>explainselectdistinctt1.*fromt_sourcet1whereitem_idin
->(selectmin(item_id)fromt_sourcet2wheret1.created_time=t2.created_timeandt1.item_name=t2.item_name);
+----+--------------------+-------+------------+------+---------------+------+---------+------+--------+----------+------------------------------+
|id|select_type|table|partitions|type|possible_keys|key|key_len|ref|rows|filtered|Extra|
+----+--------------------+-------+------------+------+---------------+------+---------+------+--------+----------+------------------------------+
|1|PRIMARY|t1|NULL|ALL|NULL|NULL|NULL|NULL|997282|100.00|Usingwhere;Usingtemporary|
|2|DEPENDENTSUBQUERY|t2|NULL|ALL|NULL|NULL|NULL|NULL|997282|1.00|Usingwhere|
+----+--------------------+-------+------------+------+---------------+------+---------+------+--------+----------+------------------------------+
2rowsinset,3warnings(0.00sec)

主查詢和相關子查詢都是全表掃描,一共要掃描100萬*100萬數據行,難怪出不來結果。

(2)使用表連接

truncatet_target;
insertintot_target
selectdistinctt1.*fromt_sourcet1,
(selectmin(item_id)item_id,created_time,item_namefromt_sourcegroupbycreated_time,item_name)t2
wheret1.item_id=t2.item_id;

這種方法用時14秒,查詢計劃如下:

mysql>explainselectdistinctt1.*fromt_sourcet1,(selectmin(item_id)item_id,created_time,item_namefromt_sourcegroupbycreated_time,item_name)t2wheret1.item_id=t2.item_id;
+----+-------------+------------+------------+------+---------------+-------------+---------+-----------------+--------+----------+------------------------------+
|id|select_type|table|partitions|type|possible_keys|key|key_len|ref|rows|filtered|Extra|
+----+-------------+------------+------------+------+---------------+-------------+---------+-----------------+--------+----------+------------------------------+
|1|PRIMARY|t1|NULL|ALL|NULL|NULL|NULL|NULL|997282|100.00|Usingwhere;Usingtemporary|
|1|PRIMARY|<derived2>|NULL|ref|<auto_key0>|<auto_key0>|5|test.t1.item_id|10|100.00|Distinct|
|2|DERIVED|t_source|NULL|ALL|NULL|NULL|NULL|NULL|997282|100.00|Usingtemporary|
+----+-------------+------------+------------+------+---------------+-------------+---------+-----------------+--------+----------+------------------------------+
3rowsinset,1warning(0.00sec)

?內層查詢掃描t_source表的100萬行,建立臨時表,找出去重后的最小item_id,生成導出表derived2,此導出表有50萬行。
?MySQL會在導出表derived2上自動創建一個item_id字段的索引auto_key0。
?外層查詢也要掃描t_source表的100萬行數據,在與導出表做鏈接時,對t_source表每行的item_id,使用auto_key0索引查找導出表中匹配的行,并在此時優化distinct操作,在找到第一個匹配的行后即停止查找同樣值的動作。

(3)使用變量

set@a:='1000-01-0100:00:00';
set@b:='';
set@f:=0;
truncatet_target;
insertintot_target
selectitem_id,created_time,modified_time,item_name,other
from
(selectt0.*,if(@a=created_timeand@b=item_name,@f:=0,@f:=1)f,@a:=created_time,@b:=item_name
from
(select*fromt_sourceorderbycreated_time,item_name)t0)t1wheref=1;

這種方法用時13秒,查詢計劃如下:

mysql>explainselectitem_id,created_time,modified_time,item_name,other
->from
->(selectt0.*,if(@a=created_timeand@b=item_name,@f:=0,@f:=1)f,@a:=created_time,@b:=item_name
->from
->(select*fromt_sourceorderbycreated_time,item_name)t0)t1wheref=1;
+----+-------------+------------+------------+------+---------------+-------------+---------+-------+--------+----------+----------------+
|id|select_type|table|partitions|type|possible_keys|key|key_len|ref|rows|filtered|Extra|
+----+-------------+------------+------------+------+---------------+-------------+---------+-------+--------+----------+----------------+
|1|PRIMARY|<derived2>|NULL|ref|<auto_key0>|<auto_key0>|4|const|10|100.00|NULL|
|2|DERIVED|<derived3>|NULL|ALL|NULL|NULL|NULL|NULL|997282|100.00|NULL|
|3|DERIVED|t_source|NULL|ALL|NULL|NULL|NULL|NULL|997282|100.00|Usingfilesort|
+----+-------------+------------+------------+------+---------------+-------------+---------+-------+--------+----------+----------------+
3rowsinset,5warnings(0.00sec)

?最內層的查詢掃描t_source表的100萬行,并使用文件排序,生成導出表derived3。
?第二層查詢要掃描derived3的100萬行,生成導出表derived2,完成變量的比較和賦值,并自動創建一個導出列f上的索引auto_key0。
?最外層使用auto_key0索引掃描derived2得到去重的結果行。

與上面方法2比較,總的掃描行數不變,都是200萬行。只存在一點微小的差別,這次自動生成的索引是在常量列 f 上,而表關聯自動生成的索引是在item_id列上,所以查詢時間幾乎相同。

至此,我們還沒有在源表上創建任何索引。無論使用哪種寫法,要查重都需要對created_time和item_name字段進行排序,因此很自然地想到,如果在這兩個字段上建立聯合索引,利用索引本身有序的特性消除額外排序,從而提高查詢性能。

2. 建立created_time和item_name上的聯合索引對比測試

--建立created_time和item_name字段的聯合索引
createindexidx_sortont_source(created_time,item_name,item_id);
analyzetablet_source;

(1)使用相關子查詢

truncatet_target;
insertintot_target
selectdistinctt1.*fromt_sourcet1whereitem_idin
(selectmin(item_id)fromt_sourcet2wheret1.created_time=t2.created_timeandt1.item_name=t2.item_name);

本次用時19秒,查詢計劃如下:

mysql>explainselectdistinctt1.*fromt_sourcet1whereitem_idin
->(selectmin(item_id)fromt_sourcet2wheret1.created_time=t2.created_timeandt1.item_name=t2.item_name);
+----+--------------------+-------+------------+------+---------------+----------+---------+----------------------------------------+--------+----------+------------------------------+
|id|select_type|table|partitions|type|possible_keys|key|key_len|ref|rows|filtered|Extra|
+----+--------------------+-------+------------+------+---------------+----------+---------+----------------------------------------+--------+----------+------------------------------+
|1|PRIMARY|t1|NULL|ALL|NULL|NULL|NULL|NULL|997281|100.00|Usingwhere;Usingtemporary|
|2|DEPENDENTSUBQUERY|t2|NULL|ref|idx_sort|idx_sort|89|test.t1.created_time,test.t1.item_name|2|100.00|Usingindex|
+----+--------------------+-------+------------+------+---------------+----------+---------+----------------------------------------+--------+----------+------------------------------+
2rowsinset,3warnings(0.00sec)

?外層查詢的t_source表是驅動表,需要掃描100萬行。

?對于驅動表每行的item_id,通過idx_sort索引查詢出兩行數據。

(2)使用表連接

truncatet_target;
insertintot_target
selectdistinctt1.*fromt_sourcet1,
(selectmin(item_id)item_id,created_time,item_namefromt_sourcegroupbycreated_time,item_name)t2
wheret1.item_id=t2.item_id;

本次用時13秒,查詢計劃如下:

mysql>explainselectdistinctt1.*fromt_sourcet1,
->(selectmin(item_id)item_id,created_time,item_namefromt_sourcegroupbycreated_time,item_name)t2
->wheret1.item_id=t2.item_id;
+----+-------------+------------+------------+-------+---------------+-------------+---------+-----------------+--------+----------+------------------------------+
|id|select_type|table|partitions|type|possible_keys|key|key_len|ref|rows|filtered|Extra|
+----+-------------+------------+------------+-------+---------------+-------------+---------+-----------------+--------+----------+------------------------------+
|1|PRIMARY|t1|NULL|ALL|NULL|NULL|NULL|NULL|997281|100.00|Usingwhere;Usingtemporary|
|1|PRIMARY|<derived2>|NULL|ref|<auto_key0>|<auto_key0>|5|test.t1.item_id|10|100.00|Distinct|
|2|DERIVED|t_source|NULL|index|idx_sort|idx_sort|94|NULL|997281|100.00|Usingindex|
+----+-------------+------------+------------+-------+---------------+-------------+---------+-----------------+--------+----------+------------------------------+
3rowsinset,1warning(0.00sec)

和沒有索引相比,子查詢雖然從全表掃描變為了全索引掃描,但還是需要掃描100萬行記錄。因此查詢性能提升并不是明顯。

(3)使用變量

set@a:='1000-01-0100:00:00';
set@b:='';
set@f:=0;
truncatet_target;
insertintot_target
selectitem_id,created_time,modified_time,item_name,other
from
(selectt0.*,if(@a=created_timeand@b=item_name,@f:=0,@f:=1)f,@a:=created_time,@b:=item_name
from
(select*fromt_sourceorderbycreated_time,item_name)t0)t1wheref=1;

本次用時13秒,查詢計劃與沒有索引時的完全相同??梢娝饕龑@種寫法沒有作用。能不能消除嵌套,只用一層查詢出結果呢?

(4)使用變量,并且消除嵌套查詢

set@a:='1000-01-0100:00:00';
set@b:='';
truncatet_target;
insertintot_target
select*fromt_sourceforceindex(idx_sort)
where(@a!=created_timeor@b!=item_name)and(@a:=created_time)isnotnulland(@b:=item_name)isnotnull
orderbycreated_time,item_name;

本次用時12秒,查詢計劃如下:

mysql>explainselect*fromt_sourceforceindex(idx_sort)
->where(@a!=created_timeor@b!=item_name)and(@a:=created_time)isnotnulland(@b:=item_name)isnotnull
->orderbycreated_time,item_name;
+----+-------------+----------+------------+-------+---------------+----------+---------+------+--------+----------+-------------+
|id|select_type|table|partitions|type|possible_keys|key|key_len|ref|rows|filtered|Extra|
+----+-------------+----------+------------+-------+---------------+----------+---------+------+--------+----------+-------------+
|1|SIMPLE|t_source|NULL|index|NULL|idx_sort|94|NULL|997281|99.00|Usingwhere|
+----+-------------+----------+------------+-------+---------------+----------+---------+------+--------+----------+-------------+
1rowinset,3warnings(0.00sec)

該語句具有以下特點:

?消除了嵌套子查詢,只需要對t_source表進行一次全索引掃描,查詢計劃已達最優。
?無需distinct二次查重。
?變量判斷與賦值只出現在where子句中。
?利用索引消除了filesort。

在MySQL 8之前,該語句是單線程去重的最佳解決方案。仔細分析這條語句,發現它巧妙地利用了SQL語句的邏輯查詢處理步驟和索引特性。一條SQL查詢的邏輯步驟為:

1.執行笛卡爾乘積(交叉連接)
2.應用ON篩選器(連接條件)
3.添加外部行(outer join)
4.應用where篩選器
5.分組
6.應用cube或rollup
7.應用having篩選器
8.處理select列表
9.應用distinct子句
10.應用order by子句
11.應用limit子句

每條查詢語句的邏輯執行步驟都是這11步的子集。拿這條查詢語句來說,其執行順序為:強制通過索引idx_sort查找數據行 -> 應用where篩選器 -> 處理select列表 -> 應用order by子句。

為了使變量能夠按照created_time和item_name的排序順序進行賦值和比較,必須按照索引順序查找數據行。這里的force index (idx_sort)提示就起到了這個作用,必須這樣寫才能使整條查重語句成立。否則,因為先掃描表才處理排序,因此不能保證變量賦值的順序,也就不能確保查詢結果的正確性。order by子句同樣不可忽略,否則即使有force index提示,MySQL也會使用全表掃描而不是全索引掃描,從而使結果錯誤。索引同時保證了created_time,item_name的順序,避免了文件排序。force index (idx_sort)提示和order by子句缺一不可,索引idx_sort在這里可謂恰到好處、一舉兩得。

查詢語句開始前,先給變量初始化為數據中不可能出現的值,然后進入where子句從左向右判斷。先比較變量和字段的值,再將本行created_time和item_name的值賦給變量,按created_time、item_name的順序逐行處理。item_name是字符串類型,(@b:=item_name)不是有效的布爾表達式,因此要寫成(@b:=item_name) is not null。

最后補充一句,這里忽略了“insert into t_target select * from t_source group by created_time,item_name;”的寫法,因為它受“sql_mode='ONLY_FULL_GROUP_BY'”的限制。

二、利用窗口函數

MySQL 8中新增的窗口函數使得原來麻煩的去重操作變得很簡單。

truncatet_target;
insertintot_target
selectitem_id,created_time,modified_time,item_name,other
from(select*,row_number()over(partitionbycreated_time,item_name)asrn
fromt_source)twherern=1;

這個語句執行只需要12秒,而且寫法清晰易懂,其查詢計劃如下:

mysql>explainselectitem_id,created_time,modified_time,item_name,other
->from(select*,row_number()over(partitionbycreated_time,item_name)asrn
->fromt_source)twherern=1;
+----+-------------+------------+------------+------+---------------+-------------+---------+-------+--------+----------+----------------+
|id|select_type|table|partitions|type|possible_keys|key|key_len|ref|rows|filtered|Extra|
+----+-------------+------------+------------+------+---------------+-------------+---------+-------+--------+----------+----------------+
|1|PRIMARY|<derived2>|NULL|ref|<auto_key0>|<auto_key0>|8|const|10|100.00|NULL|
|2|DERIVED|t_source|NULL|ALL|NULL|NULL|NULL|NULL|997281|100.00|Usingfilesort|
+----+-------------+------------+------------+------+---------------+-------------+---------+-------+--------+----------+----------------+
2rowsinset,2warnings(0.00sec)

該查詢對t_source表進行了一次全表掃描,同時用filesort對表按分區字段created_time、item_name進行了排序。外層查詢從每個分區中保留一條數據。因為重復created_timeitem_name的多條數據中可以保留任意一條,所以oevr中不需要使用order by子句。

從執行計劃看,窗口函數去重語句似乎沒有消除嵌套查詢的變量去重好,但此方法實際執行是最快的。

MySQL窗口函數說明參見“https://dev.mysql.com/doc/refman/8.0/en/window-functions.html”。

三、多線程并行執行

前面已經將單條查重語句調整到最優,但還是以單線程方式執行。能否利用多處理器,讓去重操作多線程并行執行,從而進一步提高速度呢?比如我的實驗環境是4處理器,如果使用4個線程同時執行查重SQL,理論上應該接近4倍的性能提升。

1. 數據分片

在生成測試數據時,created_time采用每條記錄加一秒的方式,也就是最大和在最小的時間差為50萬秒,而且數據均勻分布,因此先把數據平均分成4份。

(1)查詢出4份數據的created_time邊界值

mysql>selectdate_add('2017-01-01',interval125000second)dt1,
->date_add('2017-01-01',interval2*125000second)dt2,
->date_add('2017-01-01',interval3*125000second)dt3,
->max(created_time)dt4
->fromt_source;
+---------------------+---------------------+---------------------+---------------------+
|dt1|dt2|dt3|dt4|
+---------------------+---------------------+---------------------+---------------------+
|2017-01-0210:43:20|2017-01-0321:26:40|2017-01-0508:10:00|2017-01-0618:53:20|
+---------------------+---------------------+---------------------+---------------------+
1rowinset(0.00sec)

(2)查看每份數據的記錄數,確認數據平均分布

mysql>selectcasewhencreated_time>='2017-01-01'
->andcreated_time<'2017-01-0210:43:20'
->then'2017-01-01'
->whencreated_time>='2017-01-0210:43:20'
->andcreated_time<'2017-01-0321:26:40'
->then'2017-01-0210:43:20'
->whencreated_time>='2017-01-0321:26:40'
->andcreated_time<'2017-01-0508:10:00'
->then'2017-01-0321:26:40'
->else'2017-01-0508:10:00'
->endmin_dt,
->casewhencreated_time>='2017-01-01'
->andcreated_time<'2017-01-0210:43:20'
->then'2017-01-0210:43:20'
->whencreated_time>='2017-01-0210:43:20'
->andcreated_time<'2017-01-0321:26:40'
->then'2017-01-0321:26:40'
->whencreated_time>='2017-01-0321:26:40'
->andcreated_time<'2017-01-0508:10:00'
->then'2017-01-0508:10:00'
->else'2017-01-0618:53:20'
->endmax_dt,
->count(*)
->fromt_source
->groupbycasewhencreated_time>='2017-01-01'
->andcreated_time<'2017-01-0210:43:20'
->then'2017-01-01'
->whencreated_time>='2017-01-0210:43:20'
->andcreated_time<'2017-01-0321:26:40'
->then'2017-01-0210:43:20'
->whencreated_time>='2017-01-0321:26:40'
->andcreated_time<'2017-01-0508:10:00'
->then'2017-01-0321:26:40'
->else'2017-01-0508:10:00'
->end,
->casewhencreated_time>='2017-01-01'
->andcreated_time<'2017-01-0210:43:20'
->then'2017-01-0210:43:20'
->whencreated_time>='2017-01-0210:43:20'
->andcreated_time<'2017-01-0321:26:40'
->then'2017-01-0321:26:40'
->whencreated_time>='2017-01-0321:26:40'
->andcreated_time<'2017-01-0508:10:00'
->then'2017-01-0508:10:00'
->else'2017-01-0618:53:20'
->end;
+---------------------+---------------------+----------+
|min_dt|max_dt|count(*)|
+---------------------+---------------------+----------+
|2017-01-01|2017-01-0210:43:20|249999|
|2017-01-0210:43:20|2017-01-0321:26:40|250000|
|2017-01-0321:26:40|2017-01-0508:10:00|250000|
|2017-01-0508:10:00|2017-01-0618:53:20|250002|
+---------------------+---------------------+----------+
4rowsinset(4.86sec)

4份數據的并集應該覆蓋整個源數據集,并且數據之間是不重復的。也就是說4份數據的created_time要連續且互斥,連續保證處理全部數據,互斥確保了不需要二次查重。實際上這和時間范圍分區的概念類似,或許用分區表更好些,只是這里省略了重建表的步驟。

2. 建立查重的存儲過程

有了以上信息我們就可以寫出4條語句處理全部數據。為了調用接口盡量簡單,建立下面的存儲過程。

delimiter//
createproceduresp_unique(ismallint)
begin
set@a:='1000-01-0100:00:00';
set@b:='';
if(i<4)then
insertintot_target
select*fromt_sourceforceindex(idx_sort)
wherecreated_time>=date_add('2017-01-01',interval(i-1)*125000second)
andcreated_time<date_add('2017-01-01',intervali*125000second)
and(@a!=created_timeor@b!=item_name)
and(@a:=created_time)isnotnull
and(@b:=item_name)isnotnull
orderbycreated_time,item_name;
else
insertintot_target
select*fromt_sourceforceindex(idx_sort)
wherecreated_time>=date_add('2017-01-01',interval(i-1)*125000second)
andcreated_time<=date_add('2017-01-01',intervali*125000second)
and(@a!=created_timeor@b!=item_name)
and(@a:=created_time)isnotnull
and(@b:=item_name)isnotnull
orderbycreated_time,item_name;
endif;
end
//

查詢語句的執行計劃如下:

mysql>explainselect*fromt_sourceforceindex(idx_sort)
->wherecreated_time>=date_add('2017-01-01',interval(1-1)*125000second)
->andcreated_time<date_add('2017-01-01',interval1*125000second)
->and(@a!=created_timeor@b!=item_name)
->and(@a:=created_time)isnotnull
->and(@b:=item_name)isnotnull
->orderbycreated_time,item_name;
+----+-------------+----------+------------+-------+---------------+----------+---------+------+--------+----------+-----------------------+
|id|select_type|table|partitions|type|possible_keys|key|key_len|ref|rows|filtered|Extra|
+----+-------------+----------+------------+-------+---------------+----------+---------+------+--------+----------+-----------------------+
|1|SIMPLE|t_source|NULL|range|idx_sort|idx_sort|6|NULL|498640|100.00|Usingindexcondition|
+----+-------------+----------+------------+-------+---------------+----------+---------+------+--------+----------+-----------------------+
1rowinset,3warnings(0.00sec)

MySQL優化器進行索引范圍掃描,并且使用索引條件下推(ICP)優化查詢。

3. 并行執行

下面分別使用shell后臺進程和MySQL Schedule Event實現并行。

(1)shell后臺進程

?建立duplicate_removal.sh文件,內容如下:

#!/bin/bash
mysql-vvv-uroot-p123456test-e"truncatet_target"&>/dev/null
date'+%H:%M:%S'
foryin{1..4}
do
sql="callsp_unique($y)"
mysql-vvv-uroot-p123456test-e"$sql"&>par_sql1_$y.log&
done
wait
date'+%H:%M:%S'

?執行腳本文件

./duplicate_removal.sh

執行輸出如下:

[mysql@hdp2~]$./duplicate_removal.sh
14:27:30
14:27:35

這種方法用時5秒,并行執行的4個過程調用分別用時為4.87秒、4.88秒、4.91秒、4.73秒:

[mysql@hdp2~]$catpar_sql1_1.log|sed'/^$/d'
mysql:[Warning]Usingapasswordonthecommandlineinterfacecanbeinsecure.
--------------
callsp_unique(1)
--------------
QueryOK,124999rowsaffected(4.87sec)
Bye
[mysql@hdp2~]$catpar_sql1_2.log|sed'/^$/d'
mysql:[Warning]Usingapasswordonthecommandlineinterfacecanbeinsecure.
--------------
callsp_unique(2)
--------------
QueryOK,125000rowsaffected(4.88sec)
Bye
[mysql@hdp2~]$catpar_sql1_3.log|sed'/^$/d'
mysql:[Warning]Usingapasswordonthecommandlineinterfacecanbeinsecure.
--------------
callsp_unique(3)
--------------
QueryOK,125000rowsaffected(4.91sec)
Bye
[mysql@hdp2~]$catpar_sql1_4.log|sed'/^$/d'
mysql:[Warning]Usingapasswordonthecommandlineinterfacecanbeinsecure.
--------------
callsp_unique(4)
--------------
QueryOK,125001rowsaffected(4.73sec)
Bye
[mysql@hdp2~]$

可以看到,每個過程的執行時間均4.85,因為是并行執行,總的過程執行時間為最慢的4.91秒,比單線程速度提高了2.5倍。

(2)MySQL Schedule Event

?建立事件歷史日志表

--用于查看事件執行時間等信息
createtablet_event_history(
dbnamevarchar(128)notnulldefault'',
eventnamevarchar(128)notnulldefault'',
starttimedatetime(3)notnulldefault'1000-01-0100:00:00',
endtimedatetime(3)defaultnull,
issuccessint(11)defaultnull,
durationint(11)defaultnull,
errormessagevarchar(512)defaultnull,
randnoint(11)defaultnull
);

?為每個并發線程創建一個事件

delimiter//
createeventev1onscheduleatcurrent_timestamp+interval1houroncompletionpreservedisabledo
begin
declarer_codechar(5)default'00000';
declarer_msgtext;
declarev_errorinteger;
declarev_starttimedatetimedefaultnow(3);
declarev_randnointegerdefaultfloor(rand()*100001);
insertintot_event_history(dbname,eventname,starttime,randno)
#作業名
values(database(),'ev1',v_starttime,v_randno);
begin
#異常處理段
declarecontinuehandlerforsqlexception
begin
setv_error=1;
getdiagnosticscondition1r_code=returned_sqlstate,r_msg=message_text;
end;
#此處為實際調用的用戶程序過程
callsp_unique(1);
end;
updatet_event_historysetendtime=now(3),issuccess=isnull(v_error),duration=timestampdiff(microsecond,starttime,now(3)),errormessage=concat('error=',r_code,',message=',r_msg),randno=nullwherestarttime=v_starttimeandrandno=v_randno;
end
//
createeventev2onscheduleatcurrent_timestamp+interval1houroncompletionpreservedisabledo
begin
declarer_codechar(5)default'00000';
declarer_msgtext;
declarev_errorinteger;
declarev_starttimedatetimedefaultnow(3);
declarev_randnointegerdefaultfloor(rand()*100001);
insertintot_event_history(dbname,eventname,starttime,randno)
#作業名
values(database(),'ev2',v_starttime,v_randno);
begin
#異常處理段
declarecontinuehandlerforsqlexception
begin
setv_error=1;
getdiagnosticscondition1r_code=returned_sqlstate,r_msg=message_text;
end;
#此處為實際調用的用戶程序過程
callsp_unique(2);
end;
updatet_event_historysetendtime=now(3),issuccess=isnull(v_error),duration=timestampdiff(microsecond,starttime,now(3)),errormessage=concat('error=',r_code,',message=',r_msg),randno=nullwherestarttime=v_starttimeandrandno=v_randno;
end
//
createeventev3onscheduleatcurrent_timestamp+interval1houroncompletionpreservedisabledo
begin
declarer_codechar(5)default'00000';
declarer_msgtext;
declarev_errorinteger;
declarev_starttimedatetimedefaultnow(3);
declarev_randnointegerdefaultfloor(rand()*100001);
insertintot_event_history(dbname,eventname,starttime,randno)
#作業名
values(database(),'ev3',v_starttime,v_randno);
begin
#異常處理段
declarecontinuehandlerforsqlexception
begin
setv_error=1;
getdiagnosticscondition1r_code=returned_sqlstate,r_msg=message_text;
end;
#此處為實際調用的用戶程序過程
callsp_unique(3);
end;
updatet_event_historysetendtime=now(3),issuccess=isnull(v_error),duration=timestampdiff(microsecond,starttime,now(3)),errormessage=concat('error=',r_code,',message=',r_msg),randno=nullwherestarttime=v_starttimeandrandno=v_randno;
end
//
createeventev4onscheduleatcurrent_timestamp+interval1houroncompletionpreservedisabledo
begin
declarer_codechar(5)default'00000';
declarer_msgtext;
declarev_errorinteger;
declarev_starttimedatetimedefaultnow(3);
declarev_randnointegerdefaultfloor(rand()*100001);
insertintot_event_history(dbname,eventname,starttime,randno)
#作業名
values(database(),'ev4',v_starttime,v_randno);
begin
#異常處理段
declarecontinuehandlerforsqlexception
begin
setv_error=1;
getdiagnosticscondition1r_code=returned_sqlstate,r_msg=message_text;
end;
#此處為實際調用的用戶程序過程
callsp_unique(4);
end;
updatet_event_historysetendtime=now(3),issuccess=isnull(v_error),duration=timestampdiff(microsecond,starttime,now(3)),errormessage=concat('error=',r_code,',message=',r_msg),randno=nullwherestarttime=v_starttimeandrandno=v_randno;
end
//

為了記錄每個事件執行的時間,在事件定義中增加了操作日志表的邏輯,因為每個事件中只多執行了一條insert,一條update,4個事件總共多執行8條很簡單的語句,對測試的影響可以忽略不計。執行時間精確到毫秒。

?觸發事件執行

mysql-vvv-uroot-p123456test-e"truncatet_target;altereventev1onscheduleatcurrent_timestampenable;altereventev2onscheduleatcurrent_timestampenable;altereventev3onscheduleatcurrent_timestampenable;altereventev4onscheduleatcurrent_timestampenable;"

該命令行順序觸發了4個事件,但不會等前一個執行完才執行下一個,而是立即向下執行。這可從命令的輸出可以清除看到:

[mysql@hdp2~]$mysql-vvv-uroot-p123456test-e"truncatet_target;altereventev1onscheduleatcurrent_timestampenable;altereventev2onscheduleatcurrent_timestampenable;altereventev3onscheduleatcurrent_timestampenable;altereventev4onscheduleatcurrent_timestampenable;"
mysql:[Warning]Usingapasswordonthecommandlineinterfacecanbeinsecure.
--------------
truncatet_target
--------------
QueryOK,0rowsaffected(0.06sec)
--------------
altereventev1onscheduleatcurrent_timestampenable
--------------
QueryOK,0rowsaffected(0.02sec)
--------------
altereventev2onscheduleatcurrent_timestampenable
--------------
QueryOK,0rowsaffected(0.00sec)
--------------
altereventev3onscheduleatcurrent_timestampenable
--------------
QueryOK,0rowsaffected(0.02sec)
--------------
altereventev4onscheduleatcurrent_timestampenable
--------------
QueryOK,0rowsaffected(0.00sec)
Bye
[mysql@hdp2~]$

?查看事件執行日志

mysql>select*fromtest.t_event_history;
+--------+-----------+-------------------------+-------------------------+-----------+----------+--------------+--------+
|dbname|eventname|starttime|endtime|issuccess|duration|errormessage|randno|
+--------+-----------+-------------------------+-------------------------+-----------+----------+--------------+--------+
|test|ev1|2019-07-3114:38:04.000|2019-07-3114:38:09.389|1|5389000|NULL|NULL|
|test|ev2|2019-07-3114:38:04.000|2019-07-3114:38:09.344|1|5344000|NULL|NULL|
|test|ev3|2019-07-3114:38:05.000|2019-07-3114:38:09.230|1|4230000|NULL|NULL|
|test|ev4|2019-07-3114:38:05.000|2019-07-3114:38:09.344|1|4344000|NULL|NULL|
+--------+-----------+-------------------------+-------------------------+-----------+----------+--------------+--------+
4rowsinset(0.00sec)

可以看到,每個過程的執行均為4.83秒,又因為是并行執行的,因此總的執行之間為最慢的5.3秒,優化效果和shell后臺進程方式幾乎相同。

總結

以上是生活随笔為你收集整理的如何将MySQL去重的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。