一.概述
之所以接触到Mysql表分区,是缘由最近客户公司运营自去年,数据量曾几何暴增,导致表数据非常的庞大,一张表有上亿数据行不止。普通MySql优化,如关键字段索引,字段优化,查询语句优化也已经远远满足不了需求。前期经过开会商讨,给予的方案是:①数据库上:按月建立分表,按年建立分库;②客户端:默认显示当月数据,实现按月查询 ③服务端:按日期进行建表/查表,及相应数据的改/删。此方案基本解决了客户端用户的体验,但是对于针对个别用户要进行数据校验,就要进行从年库汇总,还要从月库汇总累加,服务端代码量的修改,及业务逻辑十分复杂。此时便萌生我们的MySql有没有为我们提供此问题的解决方案,通过了解,查到MySQL--分区表的相关知识。
二.应用场景/为什么要进行分区/优点(MySQL版本在5.1及以上)
①对于那些已经失去保存意义的数据,通常可以通过删除与那些数据有关的分区,很容易地删除那些数据。相反地,在某些情况下,添加新数据的过程又可以通过为那些新数据专门增加一个新的分区,来很方便地实现。
②与单个磁盘或文件系统分区相比,可以存储更多的数据。
③一些查询可以得到极大的优化,这主要是借助于满足一个给定WHERE语句的数据可以只保存在一个或多个分区内,这样 在查找时就不用查找其他剩余的分区。因为分区可以在创建了分区表后进行修改,所以在第一次配置分区方案时还不曾这么做时,可以重新组织数据,来提高那些常用查询的效率。
④涉及到例如SUM()和COUNT()这样聚合函数的查询,可以很容易地进行并行处理。通过“并行”,这意味着该查询可以在每个分区上同时进行,最终结果只需通过总计所有分区得到的结果。
⑤通过跨多个磁盘来分散数据查询,来获得更大的查询吞吐量。
三.局限性
①适合开发周期短,成本低的项目,不建议大型项目使用。 /*许多大型项目基本采用按照实际业务逻辑,进行建立年库,分表*/
②分区表无法创建全文索引/*所以使用,就得结合自己的使用场景,是否涉要涉及到全文检索*/
③分区表无法使用外键约束
④一个表最多只能有1024个分区(5.6之后支持8192分区)
四.实现原理
分区表是由多个相关的底层表实现,这些底层表也是由句柄对象表示,所以我们也可以直接访问各个分区,存储引擎管理分区的各个底层表和管理普通表一样(所有的底层表都必须使用相同的存储引擎),分区表的索引只是在各个底层表上各自加上一个相同的索引,从存储引擎的角度来看,底层表和一个普通表没有任何不同,存储引擎也无须知道这是一个普通表还是一个分区表的一部分。
操作的业务逻辑:
①SELECT查询:当查询分区表时候,会锁住所有的底层表,优化器判断是否可以过滤部分分区,然后调用接口访问分区数据
②INSERT操作: 写入一条记录时候,会锁住所有的底层表,然后确定哪个分区,写入对应的底层表
③DELETE操作: 删除一条记录时候,会锁住所有的底层表,然后确定哪个分区,删除对应底层表的记录
④UODATE操作:更新一条记录时候,会锁住所有的底层表,然后确定哪个分区,然后取出相应数据并更新,在判断更新应该放哪个分区,然后插入对应底层表,并对原数据进行删除。
备注:如果对应的存储引擎能欧实现行级锁,就会如普通表一样释放对应的表锁。
五.使用教程
在开始使用教程之前,必须先了解下分区表一共有四种类型,先大致介绍下。
1.RANGE分区:基于属于一个给定连续区间的列值,把多行分配给分区。
2.LIST分区:类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择。
3.HASH分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL 中有效的、产生非负整数值的任何表达式。
4.KEY分区:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL 服务器提供其自身的哈希函数。必须有一列或多列包含整数值。
【使用教程:今天先使用range分区进行说明后续再补上教程】
1.建表,插入数据
#不分区的表
CREATE TABLE no_part_tab
(id INT DEFAULT NULL,
remark VARCHAR(50) DEFAULT NULL,
d_date DATE DEFAULT NULL
)ENGINE=MYISAM
#分区的表
CREATE TABLE part_tab
(id INT DEFAULT NULL,
remark VARCHAR(50) DEFAULT NULL,
d_date DATE DEFAULT NULL
)ENGINE=MYISAM
PARTITION BY RANGE(YEAR(d_date))(
PARTITION p0 VALUES LESS THAN(1995),
PARTITION p1 VALUES LESS THAN(1996),
PARTITION p2 VALUES LESS THAN(1997),
PARTITION p3 VALUES LESS THAN(1998),
PARTITION p4 VALUES LESS THAN(1999),
PARTITION p5 VALUES LESS THAN(2000),
PARTITION p6 VALUES LESS THAN(2001),
PARTITION p7 VALUES LESS THAN(2002),
PARTITION p8 VALUES LESS THAN(2003),
PARTITION p9 VALUES LESS THAN(2004),
PARTITION p10 VALUES LESS THAN maxvalue);
#插入未分区表记录
DROP PROCEDURE IF EXISTS no_load_part;
DELIMITER//
CREATE PROCEDURE no_load_part()
BEGIN
DECLARE i INT;
SET i =1;
WHILE i<8000001
DO
INSERT INTO no_part_tab VALUES(i,'no',ADDDATE('1995-01-01',(RAND(i)*36520) MOD 3652));
SET i=i+1;
END WHILE;
END//
DELIMITER ;
CALL no_load_part;
#插入分区表记录
DROP PROCEDURE IF EXISTS load_part;
DELIMITER&&
CREATE PROCEDURE load_part()
BEGIN
DECLARE i INT;
SET i=1;
WHILE i<8000001
DO
INSERT INTO part_tab VALUES(i,'partition',ADDDATE('1995-01-01',(RAND(i)*36520) MOD 3652));
SET i=i+1;
END WHILE;
END&&
DELIMITER ;
CALL load_part;
2.测试SQL
SELECT COUNT(*) FROM no_part_tab WHERE d_date > DATE '1995-01-01' AND d_date< DATE '1995-12-31';
SELECT COUNT(*) FROM part_tab WHERE d_date > DATE '1995-01-01' AND d_date< DATE '1995-12-31';
3.测试结果【当数据量更大的时候,效果更加明显】