1. 程式人生 > >MySQL基礎系列之 記一次利用儲存過程實現2600萬資料水平分表

MySQL基礎系列之 記一次利用儲存過程實現2600萬資料水平分表

日常開發中我們經常會遇到大表的情況,所謂的大表是指儲存了百萬級乃至千萬級條記錄的表。這樣的表過於龐大,導致資料庫在查詢和插入的時候耗時太長,效能低下,如果涉及聯合查詢的情況,效能會更加糟糕。分表的目的就是減少資料庫的負擔,提高資料庫的效率,通常點來講就是提高表的增刪改查效率,本文將介紹我的一次利用儲存過程給一張2600萬資料的地址庫大表(資料網上下載也可以自己造)進行水平分表處理。對於MySQL儲存過程大家都瞭解,我就不介紹概念了,本文主要介紹分表的過程。我的上一篇文章也簡單介紹了儲存過程的一些語法、建立語句等,參考文章:https://blog.csdn.net/caiqing116/article/details/84843908

開門見山,進入正文。

1.建立IP地址庫總資料表

CREATE TABLE `tb_data_ipaddrlib_free` (
	`id` INT (11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'id',
	`minip` INT (11) UNSIGNED DEFAULT NULL COMMENT 'IP 塊最小端 IP,整數形式',
	`maxip` INT (11) UNSIGNED DEFAULT NULL COMMENT 'IP 塊最大端 IP,整數形式',
	`continent` VARCHAR (16) DEFAULT NULL COMMENT '大洲',
	`areacode` VARCHAR (4) DEFAULT NULL COMMENT 'IP 塊所在國家的國家編碼',
	`country` VARCHAR (50) DEFAULT NULL COMMENT 'IP 塊所在國家',
	`multiarea` text COMMENT 'IP 塊定位資訊,是單或多區域',
	`user` VARCHAR (200) DEFAULT NULL COMMENT 'IP 使用者名稱',
	PRIMARY KEY (`id`)
	KEY `index_minip_maxip` (`minip`, `maxip`)
) AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;

2.建立IP地址區間和分表字尾關係表
在後面我將IP地址庫總表每100萬條IP地址記錄作為一條關聯記錄儲存,id值在這裡從1001開始累加(字尾都4位,這個可以自己定義2位3位都行),作為分表的區分字尾,minip為這100萬條資料的開始ip,maxip為這100萬條資料的結束ip。以上這些定義是可以靈活變動的,根據個人需要定義,後續根據儲存過程插入記錄結果形如:

1000,minip1,maxip2
1001,minip3,maxip4
1002,minip5,maxip6

建表語句如下

DROP TABLE IF EXISTS tb_data_ipaddrlib_tables;
CREATE TABLE `tb_data_ipaddrlib_tables` (
	`id` INT (11) UNSIGNED  NOT NULL AUTO_INCREMENT COMMENT 'id',
	`minip` INT (11) UNSIGNED  DEFAULT NULL COMMENT 'IP 塊最小端 IP,整數形式',
	`maxip` INT (11) UNSIGNED  DEFAULT NULL COMMENT 'IP 塊最大端 IP,整數形式',
	PRIMARY KEY (`id`)
) DEFAULT CHARSET = utf8;

3.匯入IP地址庫總資料作為分表依據
Navicat for MySQL提供了匯入資料的功能,我們也可以根據tb_data_ipaddrlib_free表結構自己手動造資料,這裡就不詳細介紹怎麼導資料了。如果需要可以找我(評論區)。匯入結果如下,可以看到我這裡匯入了26407540條資料

在這裡插入圖片描述
資料格式如下
在這裡插入圖片描述
4.定義儲存過程為IP地址區間和動分表字尾關係表插入資料
每張表100萬資料,冗餘的插入在最後一張表,分表字尾從1001開始累加

DELIMITER //
create PROCEDURE proc_ip_split_tables()
begin
#定義變數 i 迴圈起始值,init分表字尾起始值,datanum每張表最大資料量, count分表個數
declare i int default  0; 
declare init int default  1001;
declare datanum int default 1000000;
declare count int ;
#計算出分表個數並賦值給count
select FLOOR(count(id) / datanum) into count from tb_data_ipaddrlib_free;
truncate tb_data_ipaddrlib_tables;
while i<= count do
    IF i = count THEN 
	insert into  tb_data_ipaddrlib_tables set 
		id = init + i,
		#查詢開始ip賦值
		minip = (select minip from tb_data_ipaddrlib_free where id = (1+datanum*i) ),
		#查詢結束ip賦值,最後一條記錄
		maxip = (select maxip from tb_data_ipaddrlib_free ORDER BY id desc limit 1 );
    ELSE
	insert into  tb_data_ipaddrlib_tables set
		id = init + i,
		#查詢開始ip賦值
		minip = (select minip from tb_data_ipaddrlib_free where id = (1+datanum*i) ),
		#查詢結束ip賦值
		maxip = (select maxip from tb_data_ipaddrlib_free where id = (datanum+datanum*i) );
    END IF;
set i = i+1;
end while;
end//
DELIMITER ;

執行此儲存過程 call proc_ip_split_tables();
根據2600萬+的資料量,每張表100萬,我們可以斷定會生成27張分表。結果如下:
在這裡插入圖片描述

5.定義儲存過程建立所有的分表

DELIMITER //
create PROCEDURE proc_ip_tables_create()
begin
#定義變數 i 迴圈起始值,init分表字尾起始值,datanum每張表最大資料量, count分表個數
declare i int default  0;
declare init int default  1001;
declare datanum int default 1000000;
declare count int;
#計算出分表個數並賦值給count
select FLOOR(count(id) / datanum) into count from tb_data_ipaddrlib_free;
#開始建立表
while i<= count do
	set @sql_create_table = concat(  
	'CREATE TABLE IF NOT EXISTS tb_data_ipaddrlib_free_',  init+i,  
	"(  
		`id` INT (11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'id',
		`minip` INT (11) UNSIGNED DEFAULT NULL COMMENT 'IP 塊最小端 IP,整數形式',
		`maxip` INT (11) UNSIGNED DEFAULT NULL COMMENT 'IP 塊最大端 IP,整數形式',
		`continent` VARCHAR (16) DEFAULT NULL COMMENT '大洲',
		`areacode` VARCHAR (4) DEFAULT NULL COMMENT 'IP 塊所在國家的國家編碼',
		`country` VARCHAR (50) DEFAULT NULL COMMENT 'IP 塊所在國家',
		`multiarea` text COMMENT 'IP 塊定位資訊,是單或多區域',
		`user` VARCHAR (200) DEFAULT NULL COMMENT 'IP 使用者名稱',
		PRIMARY KEY (`id`),
		KEY `index_minip_maxip` (`minip`, `maxip`)
	) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8" );  
	PREPARE sql_create_table FROM @sql_create_table;		
	EXECUTE sql_create_table;
	set i = i+1;
end while; 
end//
DELIMITER ;

執行此儲存過程 call proc_ip_tables_create();
根據2600萬+的資料量,每張表100萬,我們可以斷定會生成27張分表。結果如下:
在這裡插入圖片描述

6.定義儲存過程為所有的分表插入資料

DELIMITER //
create PROCEDURE proc_ip_inserttotables()
BEGIN
#定義變數 i 迴圈起始值,init分表字尾起始值,datanum每張表最大資料量, count分表個數
declare i int default  0;
declare datanum int default 1000000;
declare init int default 1001;
declare count int ;
#計算出分表個數並賦值給count
select FLOOR(count(id) / datanum) into count from tb_data_ipaddrlib_free;
#開始插入資料
WHILE i<= count DO
    set @sql_insert_ip = CONCAT(
	'insert into  tb_data_ipaddrlib_free_',
	init+i,
	' select * from tb_data_ipaddrlib_free where minip BETWEEN (select minip from tb_data_ipaddrlib_tables where id =',
	init+i,
	' ) AND (select minip from tb_data_ipaddrlib_free where maxip in (select  maxip from tb_data_ipaddrlib_tables where id =',
	init+i,
	' )limit 1 )');
    set @sql_truncate_ip = CONCAT("truncate tb_data_ipaddrlib_free_", 1001+1);
PREPARE sql_truncate_ip FROM @sql_truncate_ip;
PREPARE sql_insert_ip FROM @sql_insert_ip;

EXECUTE sql_truncate_ip;
EXECUTE sql_insert_ip;
set i = i+1;
end WHILE;
end//
DELIMITER;

執行此儲存過程 call proc_ip_inserttotables();
驗證步驟1,我們可以查詢任意分表然後檢視是否是100萬條資料,檢視最後一張表1027是否是407504條資料
查詢分表1008驗證
在這裡插入圖片描述
查詢分表1027驗證
在這裡插入圖片描述
查詢某個ip(221.224.63.146)地址進行驗證
查詢此ip地址落在哪個分表

select * from tb_data_ipaddrlib_tables where INET_ATON('221.224.63.146') BETWEEN minip and maxip;

在這裡插入圖片描述
根據結果我們知道該ip落在了分表1025,我們根據此ip在tb_data_ipaddrlib_free_1025表查詢,看是否存在該ip記錄

select * from tb_data_ipaddrlib_free_1025 where INET_ATON('221.224.63.146') BETWEEN minip and maxip;

在這裡插入圖片描述
綜上根據儲存過程建立分表成功