MySQL分库分表

一、分表

在日常开发或维护中经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表。这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询的情况,性能会更加糟糕。分表和表分区的目的就是减少数据库的负担,提高数据库的效率,通常点来讲就是提高表的增删改查效率。

1. 什么是分表

分表是将一个大表按照一定的规则分解成多张的实体表,我们可以称为子表。对于myisam引擎,每个表都对应三个文件,MYD数据文件,.MYI索引文件,.frm表结构文件。根据分表技术对海量数据的优化方式目前有2种方法。

1.1 垂直分割

把一个数据量很大的表,可以把不同的列划分到不同的子表中,子表的结构各不相同。垂直分割的理由一般是根据数据的活跃度进行分离,不同活跃的数据,处理方式是不同的。

垂直分割

【案例】

  • 对于一个博客系统,文章标题,作者,分类,创建时间等,是变化频率慢,查询次数多,我们把它叫做冷数据
  • 而博客的浏览量,回复数等,类似的统计信息,或者别的变化频率比较高的数据,我们把它叫做活跃数据/热数据

所以,在进行数据库结构设计的时候,就应该考虑分表,首先是纵向分表的处理。

(1)数据库引擎的选择

这样纵向分表后:首先存储引擎的使用不同,冷数据使用MyIsam 可以有更好的查询效果;活跃数据,可以使用Innodb ,可以有更好的更新速度。

(2)读写优化

其次,对冷数据进行更多的从库配置,因为更多的操作是查询,这样来加快查询速度。对热数据,可以相对有更多的主库的横向分表处理。

(3)热数据加缓存

其实,对于一些特殊的活跃数据,也可以考虑使用memcache、redis之类的缓存,等累计到一定量再去更新数据库。或者mongodb 一类的nosql 数据库,这里只是举例,就先不说这个。


1.2 水平分割

根据一列或者多列的值把数据行放到多个独立的子表里,字表的结构相同。水平分表方式可以通过多个低配置主机整合起来,实现高性能。分表理由:保证单表的容量不会太大,从而来保证单表的查询等处理能力。

水平分割

下面要介绍的“分表”主要指水平分表。


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

2.1 步骤

假如我有一张用户表user,有50W条数据,现在要拆成二张表user1和user2,每张表25W条数据。

(1)新建子表

mysql> CREATE TABLE IF NOT EXISTS `user1` (  
 ->   `id` int(11) NOT NULL AUTO_INCREMENT,  
 ->   `name` varchar(50) DEFAULT NULL,  
 ->   `sex` int(1) NOT NULL DEFAULT '0',  
 ->   PRIMARY KEY (`id`)  
 -> ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;  
Query OK, 0 rows affected (0.05 sec)  

mysql> CREATE TABLE IF NOT EXISTS `user2` (  
 ->   `id` int(11) NOT NULL AUTO_INCREMENT,  
 ->   `name` varchar(50) DEFAULT NULL,  
 ->   `sex` int(1) NOT NULL DEFAULT '0',  
 ->   PRIMARY KEY (`id`)  
 -> ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;  
Query OK, 0 rows affected (0.01 sec)  

(2)往子表里插入数据

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

(3)建立merge表

mysql> CREATE TABLE IF NOT EXISTS `alluser` (  
 ->   `id` int(11) NOT NULL AUTO_INCREMENT,  
 ->   `name` varchar(50) DEFAULT NULL,  
 ->   `sex` int(1) NOT NULL DEFAULT '0',  
 ->   INDEX(id)  
 -> ) TYPE=MERGE UNION=(user1,user2) INSERT_METHOD=LAST AUTO_INCREMENT=1 ;  
Query OK, 0 rows affected, 1 warning (0.00 sec)  

(4)备份user表,然后删除它

(5)把这个alluser表的表名改成user


2.2 注意事项

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

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


3. 常用的分表策略

上面介绍的分表其实是对表结构进行了更改,对大表进行拆分。实际上我们在设计数据库表结构和业务逻辑的时候,就应该计算业务需求,提前做好分表,避免大表的出现。下面是基于业务层面的一些常用分表策略。下面以新闻发布系统举例。下面分离出的子表可以位于一个数据实例上,如果位于不同的实例上那么就存在分库了,也就多了表与库的映射这一步。

(1)按时间结构

类似:

article_201701
article_201702
article_201703

在这个系统中,主键是13位带毫秒的时间戳

(2)归档式

类似:

article_old
article_new

一张是旧文章表,一张是新文章表,新文章表放2个月的信息,每天定期把2个月中的最早一天的文章归入旧表中。

(3)按版块结构

news_category
news_article
sports_category
sports_article

(4)按哈希结构

md5取前两位哈希可以达到1296张表,如果觉得不够,那就再加一位,总数可达46656张表。


二、分区

1. 什么是分区?

分表是逻辑层面的分割,分区是物理层面、存储层面的分割。准确来说,分区是将数据分段划分在多个位置存放,可以是同一块磁盘也可以在不同的机器。分区后,表面上还是一张表。

mysql分表和分区有什么联系呢?
(1)都能提高mysql的性高,在高并发状态下都有一个良好的表现。
(2)分表和分区不矛盾,可以相互配合的,对于那些大访问量,并且表数据比较多的表,我们可以采取分表和分区结合的方式。

(3)merge这种分表方式,不能和分区配合。
(4)表分区相对于分表,操作方便,不需要创建子表。


2. 分区的类型

2.1 Range

把连续区间按范围划分:

create table user(
    id int(11),
    money int(11) unsigned not null,
    date datetime
)
partition by range(YEAR(date))(
    partition p2014 values less than (2015),
    partition p2015 values less than (2016),
    partition p2016 values less than (2017),
    partition p2017 values less than maxvalue
);

2.2 List

把离散值分成集合,按集合划分,适合有固定取值列的表

create table user(
    a int(11),
    b int(11)
)
partition by list(b)(
    partition p0 values in (1,3,5,7,9),
    partition p1 values in (2,4,6,8,0)
);

2.3 Hash

随机分配,分区数固定:

create table user(
    a int(11),
    b datetime
)
partition by hash(YEAR(b))
partitions 4;

2.4 Key

类似Hash,区别是只支持1列或多列,且mysql提供自身的Hash函数

create table user(
    a int(11),
    b datetime
)
partition by key(b)
partitions 4;

3. 分区管理

(1)新增分区

ALTER TABLE sale_data
ADD PARTITION (PARTITION p201710 VALUES LESS THAN (201711));

(2)删除分区

当删除了一个分区,也同时删除了该分区中所有的数据。

ALTER TABLE sale_data DROP PARTITION p201710;

(3)分区的合并
下面的SQL,将p201701 - p201709 合并为3个分区p2017Q1 - p2017Q3:

ALTER TABLE sale_data
REORGANIZE PARTITION p201701,p201702,p201703,
p201704,p201705,p201706,
p201707,p201708,p201709 INTO
(
PARTITION p2017Q1 VALUES LESS THAN (201704),
PARTITION p2017Q2 VALUES LESS THAN (201707),
PARTITION p2017Q3 VALUES LESS THAN (201710)
);
发布了94 篇原创文章 · 获赞 43 · 访问量 12万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章