第十章:第32节 MySQL进阶篇——水平分表的实现

更新于:2017-09-21 10:33:02

垂直分表后,完全可由客户端去实现(比如PHP客户端逻辑中去实现),无非是:


原先A表垂直拆分为B、C表


select * from A;


也就变成


select * from B;

select * from C;


把两个结果集合并就是完整的A表记录,垂直分表后还是很好操作的。


那水平分表呢?


即可以由客户端实现(PHP代码写逻辑),也可以在MySQL中实现。


客户端实现(PHP逻辑):


1.png


1.png


分享一段代码:


1.png


大体思路分析:


5行 定义了一个数组,数组元素是四个表名:“student_0”、“student_1”、“student_2”、“student_3”。

7行 一个变量记录的数组个数,为4。

10行 一条SQL语句,意思往“student_id”表插入一个null数据,目的只是在11行获取一个主键ID。

12行 通过“ID%4”求余,作为数组的一个下标去获取一个表名。

14行 往获得的表里插入数据。


这样很简单地就可以实现水平分表功能了。


当然我们不仅可以按照ID取模分表,也可以按照数据的时间分表、ID区间范围分表或者通过某个数据的hash值分表……


MySQL中实现:


当我们要分表时,往往都是数据量很大的时候需要水平分表。有时候可能觉得受牵连的代码太多了,为了实现分表功能而去修改很多很多代码不值得。那也没问题,也有不用动业务代码就可以实现水平分表的方法。


分表后,如果不想客户端(比如PHP)自己做判断该操作哪个表,由MySQL智能去完成,也是可以的。


说两种方法:(如果楠神要分表会选择由PHP逻辑代码实现分表功能,楠神也没有实际操作过在MySQL中开启智能分表,以下内容大家可做参考学习)


1)partition分区:


将某张表数据,分别存储到不同的区域中。


1.png

其实:每个分区,就是独立的表。要存储该分区数据的数据,索引等信息。

 

创建分区:

在创建表时,指定分区的选项:

 

Create table table_name (

定义

)

Partition by 分区算法 (参数) 分区选项。


1.png

1.png


Tip分区与存储引擎无关,是MySQL逻辑层完成的。

通过变量查看当前mysql是否支持分区:


1.png


分区算法:

MySQL提供4

取余:Keyhash

条件:Listrange

 

提示,参与分区的参数字段需要为主键的一部分


KEY – 取余

按照某个字段进行取余


1.png


Hash – 取余

按照某个表达式的值进行取余

 

学生表分区,按照生日的月份,划分12个表中。


1.png


Tip

Keyhash都是取余算法,要求分区参数,返回的数据必须为整数。

 

List – 条件 列表

需要指定的每个分区数据的存储条件。

 

按照生日中的月份,分成春夏秋冬四个分区。


1.png


1.png


List,条件依赖的数据时列表形式。

Range - 条件 范围

Range:条件依赖的数据是一个条件表达式。

 

逻辑:按照生日的年份分成


1.png

1.png


管理分区语法

 

取余:keyhash

增加分区数量:

Add partition N


1.png


减少分区数量:

COALESCE partition N


1.png


采用取余算法的分区数量的修改,不会导致已有分区数据的丢失,需要重新分配数据到新大地分区。


条件:listrange

添加分区

1.png


删除分区:

Drop partition partition_name;


1.png


 

注意:删除条件算法的分区,导致分区数据丢失。


选择分区算法

平均分配: 就按照主键进行key(primary key)即可(非常常见)

按照某种业务逻辑分区: 选择那种最容易被筛选的字段,整数型


2)利用merge存储引擎来实现分表



MySQL提供一个可以将多个结构相同的myisam表,合并到一起的存储引擎——merge:

1.png

1.png

从上面的操作中,不知道你有没有发现点什么?假如有一张用户表user,有50W条数据,现在要拆成二张表user1和user2,每张表25W条数据,


INSERT INTO user1(user1.id,user1.name,user1.sex)SELECT (user.id,user.name,user.sex)FROM user where user.id <= 250000

INSERT INTO user2(user2.id,user2.name,user2.sex)SELECT (user.id,user.name,user.sex)FROM user where user.id > 250000


这样成功的将一张user表,分成了二个表,这个时候有一个问题,代码中的sql语句怎么办,以前是一张表,现在变成二张表了,代码改动很大,这样给程序员带来了很大的工作量,有没有好的办法解决这一点呢?办法是把以前的user表备份一下,然后删除掉,上面的操作中我建立了一个alluser表,只把这个alluser表的表名改成user就行了。但是,不是所有的mysql操作都能用的


a,如果你使用 alter table 来把 merge 表变为其它表类型,到底层表的映射就被丢失了。取而代之的,来自底层 myisam 表的行被复制到已更换的表中,该表随后被指定新类型。


b,网上看到一些说replace不起作用,试了一下可以起作用的。


1.png


c,一个 merge 表不能在整个表上维持 unique 约束。当你执行一个 insert,数据进入第一个或者最后一个 myisam 表(取决于 insert_method 选项的值)。mysql 确保唯一键值在那个 myisam 表里保持唯一,但不是跨集合里所有的表。


d,当你创建一个 merge 表之时,没有检查去确保底层表的存在以及有相同的机构。当 merge 表被使用之时,mysql 检查每个被映射的表的记录长度是否相等,但这并不十分可靠。如果你从不相似的 myisam 表创建一个 merge 表,你非常有可能撞见奇怪的问题。