1、為什麼要分表?
資料庫資料越來越大,随之而來的是單個表中資料太多。以至于查詢速度變慢,而且由于表的鎖機制導緻應用操作也搜到嚴重影響,出現了資料庫性能瓶頸。
mysql中有一種機制是表鎖定和行鎖定,是為了保證資料的完整性。表鎖定表示你們都不能對這張表進行操作,必須等我對表操作完才行。行鎖定也一樣,别的sql必須等我對這條資料操作完了,才能對這條資料進行操作。當出現這種情況時,我們可以考慮分表或分區。
2、MySQL分表
分表是将一個大表按照一定的規則分解成多張具有獨立存儲空間的實體表,每個表都對應三個檔案,MYD資料檔案,.MYI索引檔案,.frm表結構檔案。這些表可以分布在同一塊磁盤上,也可以在不同的機器上。app讀寫的時候根據事先定義好的規則得到對應的表名,然後去操作它。
将單個資料庫表進行拆分,拆分成多個資料表,然後使用者通路的時候,根據一定的算法(如用hash的方式,也可以用求餘(取模)的方式),讓使用者通路不同的表,這樣資料分散到多個資料表中,減少了單個資料表的通路壓力。提升了資料庫通路性能。分表的目的就在于此,減小資料庫的負擔,縮短查詢時間。
Mysql分表分為垂直切分和水準切分,具體差別如下:
垂直切分是指資料表列的拆分,把一張列比較多的表拆分為多張表 通常我們按以下原則進行垂直拆分: 把不常用的字段單獨放在一張表; 把text,blob(binary large object,二進制大對象)等大字段拆分出來放在附表中;
經常組合查詢的列放在一張表中; 垂直拆分更多時候就應該在資料表設計之初就執行的步驟,然後查詢的時候用join關鍵起來即可。
水準拆分是指資料表行的拆分,把一張的表的資料拆成多張表來存放。 水準拆分原則,通常情況下,我們使用hash、取模等方式來進行表的拆分 比如一張有400W的使用者表users,為提高其查詢效率我們把其分成4張表users1,users2,users3,users4 通過用ID取模的方法把資料分散到四張表内Id%4= [0,1,2,3] 然後查詢,更新,删除也是通過取模的方法來查詢 部分業務邏輯也可以通過地區,年份等字段來進行歸檔拆分; 進行拆分後的表,這時我們就要限制使用者查詢行為。比如我們是按年來進行拆分的,這個時候在頁面設計上就限制使用者必須要先選擇年,然後才能進行查詢。
3、利用merge存儲引擎實作分表
注:隻有myisam引擎的原表才可以利用merge存儲引擎實作分表。
merge分表,分為主表和子表,主表類似于一個殼子,邏輯上封裝了子表,實際上資料都是存儲在子表中的。 我們可以通過主表插入和查詢資料,如果清楚分表規律,也可以直接操作子表。
例:
1)建立一個完整表
mysql> create database test1;
mysql> use test1;
mysql> create table member
-> (
-> id bigint auto_increment primary key,
-> name varchar(20),
-> sex tinyint not null default '0'
-> )engine=myisam default charset=utf8 auto_increment=1;
#插入資料
mysql> insert into member(name,sex) values('tom1',1);
mysql> insert into member(name,sex) select name,sex from member; # 插入語句多執行幾次,即可插入大量的資料
mysql> select count(*) from member; # 手賤了,這裡我插入了16384條資料
+----------+
| count(*) |
+----------+
| 16384 |
+----------+
1 row in set (0.00 sec)
2)對上面完整的表進行分表
**分表注意事項:**
* 子表和主表的字段定義需要一緻,包括資料類型,資料長度等;
* 當分表完成後,所有的操作(增删改查)需要對主表進行,雖然主表并不存放實際的資料。
#建立兩個分表,表結構必須和上面完整的表結構一緻
mysql> create table tb_member1 like member;
mysql> create table tb_member2 like member;
#建立merge引擎的表作為主表,并關聯上面的兩個分表
mysql> create table tb_member
-> (
-> id bigint auto_increment primary key,
-> name varchar(20),
-> sex tinyint not null default '0'
-> )engine=merge union=(tb_member1,tb_member2) insert_method=last charset=utf8;
注:在上面建立主表時,指定的“insert_method=last”有三個可選參數,分别是:last:表示插入到最後一張表裡面;first:表示插入到第一張表裡面;NO:表示該表不能做任何寫入操作,隻作為查詢使用。
3)檢視剛剛建立的三個表結構如下:

4)将資料分到兩個表中:
mysql> insert into tb_member1(id,name,sex) select id,name,sex from member where id%2=0;
Query OK, 8192 rows affected (0.01 sec)
Records: 8192 Duplicates: 0 Warnings: 0
mysql> insert into tb_member2(id,name,sex) select id,name,sex from member where id%2=1;
Query OK, 8192 rows affected (0.02 sec)
Records: 8192 Duplicates: 0 Warnings: 0
5)檢視主表和兩個子表中的資料
第一個子表部分資料如下:
第二個子表部分資料如下:
主表部分查詢的部分資料如下:
資料總行數如下:
注意:總表隻是一個外殼,存取資料發生在一個一個的子表裡面。 每個子表都有自已獨立的相關表檔案,而主表隻是一個殼,并沒有完整的相關表檔案,當确定主表中可以查到的資料和分表之前查到的資料完全一緻時,就可以将原來的表删除了,之後對表的讀寫操作,都可以對分表後的主表進行。
上面三個表對應的本地檔案如下:
可以看出,能夠查詢到所有資料的主表的本地資料檔案是非常小的,這也驗證了,資料并沒有存在這個主表中。
6)對主表進行插入資料的操作,如下:
mysql> insert into tb_member values(16385,'tom2',0),(16386,'tom3',1);
可以看出,新增的兩條資料都插入在了第二張表中,因為在建立主表的時候,指定的“insert_method”是last,也就是所有插入資料的操作都是對最後一張表裡進行的,可以通過alter指令修改插入方法,如下:
mysql> alter table tb_member INSERT_METHOD=first;
修改插入方法後,再自行對表進行插入資料的操作,可以發現所有的資料都寫入了第一個表(我這裡插入了四條資料),檢視如下:
mysql> insert into tb_member values(16387,'tom4',2),(16388,'tom5',3),(16389,'tom6',4),(16390,'tom7',5);
上面是新增了四條資料,可以發現都插入到了第一張表。
若将插入方法修改為no,則表示這個表不能再插入任何資料,如下:
mysql> alter table tb_member insert_method=no;
mysql> insert into tb_member values(16391,'tom7',9);
4、MySQL分區
1)什麼是分區?
分區和分表相似,都是按照規則分解表。不同在于分表将大表分解為若幹個獨立的實體表,而分區是将資料分段劃分在多個位置存放,分區後,表還是一張表,但資料散列到多個位置了。app讀寫的時候操作的還是表名字,db自動去組織分區的資料。
分區主要有以下兩種形式:
水準分區:這種形式分區是對表的行進行分區,所有在表中定義的列在每個資料集中都能找到,是以表的特性依然得以保持。
舉個簡單例子:一個包含十年發票記錄的表可以被分區為十個不同的分區,每個分區包含的是其中一年的記錄。
垂直分區:這種分區方式一般來說是通過對表的垂直劃分來減少目标表的寬度,使某些特定的列被劃分到特定的分區,每個分區都包含了其中的列所對應的行。
舉個簡單例子:一個包含了大text和BLOB列的表,這些text和BLOB列又不經常被通路,這時候就要把這些不經常使用的text和BLOB了劃分到另一個分區,在保證它們資料相關性的同時還能提高通路速度。
2)檢視目前資料庫是否支援分區
MySQL 5.6之前,使用下面的參數檢視目前配置是否支援分區(如果為yes則表示支援分區):
mysql> SHOW VARIABLES LIKE '%partition%';
+-----------------------+---------------+
|Variable_name | Value |
+-----------------------+---------------+
| have_partition_engine | YES |
+-----------------------+------------------+
在5.6及以後采用以下方式檢視:
mysql> show plugins;
傳回的結果中,有以下字段(如果status列為“ACTIVE”,則表示支援分區):
3)按照範圍(range)方式的表分區
mysql> create table user
-> (
-> id int not null auto_increment,
-> name varchar(30) not null default '',
-> sex int(1) not null default '0',
-> primary key(id)
-> )default charset=utf8 auto_increment=1
-> partition by range(id)
-> (
-> partition p0 values less than (3),
-> partition p1 values less than (6),
-> partition p2 values less than (9),
-> partition p3 values less than (12),
-> partition p4 values less than maxvalue
-> );
注:在上面建立的表中,當id列的值小于3将會插入到p0分區,大于3小于6的記錄将會插入到p1分區,以此類推,所有id值大于12的記錄都會插入到p4分區。
4)利用存儲過程插入一些資料
mysql> delimiter //
mysql> create procedure adduser()
-> begin
-> declare n int;
-> declare summary int;
-> set n = 0;
-> while n <= 20
-> do
-> insert into test1.user(name,sex) values("tom",0);
-> set n=n+1;
-> end while;
-> end //
Query OK, 0 rows affected (0.01 sec)
mysql> delimiter ;
mysql> delimiter ;
mysql> call adduser();
Query OK, 1 row affected (0.01 sec)
mysql> select * from user;
+----+------+-----+
| id | name | sex |
+----+------+-----+
| 1 | tom | 0 |
| 2 | tom | 0 |
| 3 | tom | 0 |
| 4 | tom | 0 |
| 5 | tom | 0 |
| 6 | tom | 0 |
| 7 | tom | 0 |
5)到存放資料表檔案的目錄下看一下:
可以看到資料是被分散存到不同的檔案中的,本地的檔案名都是“user#P#p0...”命名的,其中p0是自定義的分區名。
6)統計資料行數
mysql> select count(*) from user;
+----------+
| count(*) |
+----------+
| 21 |
+----------+
1 row in set (0.00 sec)
7)從information_schema系統庫中的partition表中檢視分區資訊
mysql> select * from information_schema.partitions where table_schema='test1' and table_name='user'\G
8)從分區中查詢資料
9)添加及合并分區(需要先合并分區再新增分區)
1.添加分區:
注意:由于在建立表的時候,指定的最後一個分區range是maxvalue,是以是無法直接增加分區的,如下:
mysql> alter table user add partition (partition p5 values less than (20));
ERROR 1481 (HY000): MAXVALUE can only be used in last partition definition
大意是:MAXVALUE隻能在最後一個分區定義中使用
但也不可以将最後定義了maxvalue的分區直接删除,因為删除分區的話,分區中的資料也會丢失,是以,如果需要新增分區的正确做法,應該是先合并分區,再新增分區,這樣才可以保證資料的完整性,如下:
mysql> alter table user reorganize partition p4 into (partition p03 values less than (15),partition p04 values less than maxvalue );
mysql> alter table user reorganize partition p0,p1,p2,p3 into
-> (partition p02 values less than (12));
mysql> alter table user drop partition p02;