分区php
分区就是把一个数据表的文件和索引分散存储在不一样的物理文件中。mysql
mysql支持的分区类型包括Range、List、Hash、Key,其中Range比较经常使用:sql
RANGE分区:基于属于一个给定连续区间的列值,把多行分配给分区。数据库
LIST分区:相似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择。服务器
HASH分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数能够包含MySQL 中有效的、产生非负整数值的任何表达式。网络
KEY分区:相似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值。less
案例:函数
创建一个user 表 以id进行分区 id 小于10的在user_1分区id小于20的在user_2分区工具
create table user( id int not null auto_increment, username varchar(10), primary key(id) )engine = innodb charset=utf8 partition by range (id)( partition user_1 values less than (10), partition user_2 values less than (20) );
创建后添加分区:性能
maxvalue 表示最大值 这样大于等于20的id 都出存储在user_3分区
alter table user add partition( partition user_3 values less than maxvalue );
删除分区:
alter table user drop partition user_3;
如今打开mysql的数据目录
能够看见多了user#P#user_1.ibd 和user#P#user_2.ibd 这两个文件
若是表使用的存储引擎是MyISAM类型,就是:
user#P#user_1.MYD,user#P#user_1.MYI和user#P#user_2.MYD,user#P#user_2.MYI
因而可知,mysql经过分区把数据保存到不一样的文件里,同时索引也是分区的。相对于未分区的表来讲,分区后单独的数据库文件索引文件的大小都明显下降,效率则明显的提示了。能够插入一条数据而后分析查询语句验证一下:
insert into user values(null,'测试'); explain partitions select * from user where id =1;
能够看见仅仅在user_1分区执行了这条查询。
具体分区的效率是多少还须要看数据量。在分区时能够经过 DATA DIRECTORY 和 INDEX DIRECTORY 选项吧不一样的分区放到不一样的磁盘上进一步提升系统的I/O吞吐量。
分区类型的选择,一般使用Range类型,不过有些状况,好比主从结构中,主服务器不多使用‘select’查询,在主服务器上使用 Range类型分区一般没有太大的意义,此时使用Hash类型分区更好例如:
partition by hash(id) partitions 10;
当插入数据时,根据id吧数据平均散到各个分区上,因为文件小,效率高,更新操做变得更快。
在分区时使用的字段,一般状况下按时间字段分区,具体状况以需求而定。划分应用的方式有不少种,好比按时间或用户,哪一种用的多,就选择哪一种分区。若是使用主从结构可能就更加灵活,有的从服务器使用时间,有的使用用户。不过如此一来当执行查询时,程序应该负责选择真确的服务器查询,写个mysql proxy脚本应该能够透明的实现。
分区的限制:
1.主键或者惟一索引必须包含分区字段,如primary key (id,username),不过innoDB的大组建性能很差。
2.不少时候,使用分区就不要在使用主键了,不然可能影响性能。
3.只能经过int类型的字段或者返回int类型的表达式来分区,一般使用year或者to_days等函数(mysql 5.6 对限制开始放开了)。
4.每一个表最多1024个分区,并且多分区会大量消耗内存。
5.分区的表不支持外键,相关的逻辑约束须要使用程序来实现。
6.分区后,可能会形成索引失效,须要验证分区可行性。
分区模式详解:
* Range(范围) – 这种模式容许DBA将数据划分不一样范围。例如DBA能够将一个表经过年份划分红三个分区,80年代(1980's)的数据,90年代(1990's)的数据以及任何在2000年(包括2000年)后的数据。
CREATE TABLE users ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, usersname VARCHAR(30) NOT NULL DEFAULT '', email VARCHAR(30) NOT NULL DEFAULT '' ) PARTITION BY RANGE (id) ( PARTITION p0 VALUES LESS THAN (3000000), PARTITION p1 VALUES LESS THAN (6000000), PARTITION p2 VALUES LESS THAN (9000000), PARTITION p3 VALUES LESS THAN MAXVALUE );
在这里,将用户表分红4个分区,以每300万条记录为界限,每一个分区都有本身独立的数据、索引文件的存放目录。
还能够将这些分区所在的物理磁盘分开彻底独立,能够提升磁盘IO吞吐量。
CREATE TABLE users ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, usersname VARCHAR(30) NOT NULL DEFAULT '', email VARCHAR(30) NOT NULL DEFAULT '' ) PARTITION BY RANGE (id) ( PARTITION p0 VALUES LESS THAN (3000000) DATA DIRECTORY = '/data0/data' INDEX DIRECTORY = '/data0/index', PARTITION p1 VALUES LESS THAN (6000000) DATA DIRECTORY = '/data1/data' INDEX DIRECTORY = '/data1/index', PARTITION p2 VALUES LESS THAN (9000000) DATA DIRECTORY = '/data2/data' INDEX DIRECTORY = '/data2/index', PARTITION p3 VALUES LESS THAN MAXVALUE DATA DIRECTORY = '/data3/data' INDEX DIRECTORY = '/data3/index' );
* List(预约义列表) – 这种模式容许系统经过DBA定义的列表的值所对应的行数据进行分割。例如:DBA根据用户的类型进行分区。
CREATE TABLE user ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(30) NOT NULL DEFAULT '' , user_type int not null ) PARTITION BY LIST (user_type ) ( PARTITION p0 VALUES IN (0,4,8,12) , PARTITION p1 VALUES IN (1,5,9,13) , PARTITION p2 VALUES IN (2,6,10,14), PARTITION p3 VALUES IN (3,7,11,15) );
分红4个区,一样能够将分区设置的独立的磁盘中。
* Key(键值) – 上面Hash模式的一种延伸,这里的Hash Key是MySQL系统产生的。
CREATE TABLE user ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(30) NOT NULL DEFAULT '', email VARCHAR(30) NOT NULL DEFAULT '' ) PARTITION BY KEY (id) PARTITIONS 4 ( PARTITION p0, PARTITION p1, PARTITION p2, PARTITION p3 );
* Hash(哈希) – 这中模式容许DBA经过对表的一个或多个列的Hash Key进行计算,最后经过这个Hash码不一样数值对应的数据区域进行分区,。例如DBA能够创建一个对表主键进行分区的表。
CREATE TABLE user ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, username VARCHAR(30) NOT NULL DEFAULT '', email VARCHAR(30) NOT NULL DEFAULT '' ) PARTITION BY HASH (id) PARTITIONS 4 ( PARTITION p0 , PARTITION p1, PARTITION p2, PARTITION p3 );
分红4个区,一样能够将分区设置的独立的磁盘中。
= 分区管理 =
删除分区
ALERT TABLE users DROP PARTITION p0;
重建分区
RANGE 分区重建
ALTER TABLE users REORGANIZE PARTITION p0,p1 INTO (PARTITION p0 VALUES LESS THAN (6000000));
将原来的 p0,p1 分区合并起来,放到新的 p0 分区中。
LIST 分区重建
ALTER TABLE users REORGANIZE PARTITION p0,p1 INTO (PARTITION p0 VALUES IN(0,1,4,5,8,9,12,13));
将原来的 p0,p1 分区合并起来,放到新的 p0 分区中。
HASH/KEY 分区重建
ALTER TABLE users REORGANIZE PARTITION COALESCE PARTITION 2;
用 REORGANIZE 方式重建分区的数量变成2,在这里数量只能减小不能增长。想要增长能够用 ADD PARTITION 方法。
新增分区
新增 RANGE 分区
alter table user add partition(partition user_3 values less than maxvalue);
新增 LIST 分区
ALTER TABLE category ADD PARTITION (PARTITION p4 VALUES IN (16,17,18,19));
新增 HASH/KEY 分区
ALTER TABLE users ADD PARTITION PARTITIONS 8;
将分区总数扩展到8个。
给已有的表加上分区
alter table results partition by RANGE (month(ttime)) (PARTITION p0 VALUES LESS THAN (1), PARTITION p1 VALUES LESS THAN (2) , PARTITION p2 VALUES LESS THAN (3) , PARTITION p3 VALUES LESS THAN (4) , PARTITION p4 VALUES LESS THAN (5) , PARTITION p5 VALUES LESS THAN (6) , PARTITION p6 VALUES LESS THAN (7) , PARTITION p7 VALUES LESS THAN (8) , PARTITION p8 VALUES LESS THAN (9) , PARTITION p9 VALUES LESS THAN (10) , PARTITION p10 VALUES LESS THAN (11), PARTITION p11 VALUES LESS THAN (12), PARTITION P12 VALUES LESS THAN (13) );
分表
分表和分区相似,区别是,分区是把一个逻辑表文件分红几个物理文件后进行存储,而分表则是把原先的一个表分红几个表。进行分表查询时能够经过union或者视图。
分表又分垂直分割和水平分割,其中水平分分割最为经常使用。水平分割一般是指切分到另一个数据库或表中。例如对于一个会员表,按对3的模进行分割:
table = id%3
若是id%3 = 0 则将用户数据放入到user_0表中,如id%3=1就放入user_1表中,依次类推。
在这里有个问题,这个uid应该是全部会员按序增加的,可他是怎么获得的呢?使用auto_increment是不行的,这样就用到序列了。
对于一些流量统计系统,其数据量比较大,而且对过往数据的关注度不高,这时按年、月、日进行分表,将每日统计信息放到一个以日期命名的表中;或者按照增量进行分表,如每一个表100万数据,超过100万就放入第二个表。还能够按Hash进行分表,可是按日期和取模余数分表最为常见,也容易扩展。
分表后可能会遇到新的问题,那就是查询,分页和统计。通用的方法是在程序中进行处理,辅助视图。
使用分表案例:
案例1:
对会员数据对5取模,放在5个表中,如何查询会员数据:
1.已知id查询会员数据,代码以下:
<?php //查询单个会员数据 $customer_table = 'customer'.$id%5; $sql = 'select * from '.$customer_table.' where customer_id = '.$id; //查询所有会员数据 $sql = ''; $tbale = ['customer0','customer1','customer2','customer3','customer4']; foreach($table as $v){ $sql .='select * from '.$v.' union'; } $sql = substr($sql,0,-5); ?>
这样就能够查询某一个会员的数据或者所有会员的数据了。同理,分页的话在这个大集合中使用limit 就能够了。可是这样作又会有一个疑问,把全部的表连起来查询和部分表没有什么区别,其实在实际的应用中,不可能查看全部的会员资料,一次查看20个而后分页。彻底没有必要作union,仅查询一个表就能够了,惟一须要考虑的是在分页零界点时的衔接。其实,这个衔接是否那么重要?即便偶尔出现几条数据的差别,也不会对业务有任何的影响。
2.和其它表进行关联和1相似。
3.根据会员姓名搜索用户信息。在这种需求下,须要搜索全部的表,并对结果进行汇总。虽然这样作产生了屡次的查询,但并不表明效率低。好的sql语句执行10次也比差的sql语句执行一次快。
案例2:
在一个流量监控系统中,因为网络流量巨大,统计数据很庞大,须要按天分表。先要获得任意日,周,月的数据。
1.须要任意一天的数据。直接查询当天的数据表便可。
2.须要几天的数据。分爱查询这几天的数据,而后进行汇总。
3.须要查询一周的数据。对一周的数据按期汇总到一个week表,从这个表里面查询。这个汇总过程能够由一个外部程序完成,也能够由按期的脚本完成。
4.查询一个月的数据。汇总本月全部的数据到month表,在此表查询。
5.查询5个月内的详细数据。不支持。仅支持最多3个月的详细数据。数据没3个月已归档一次。在大数据的处理中,必须作出一些牺牲。对于超出3个月的数据,仅提供统计数据,详细数据须要查看归档。90天或者180天,给数据保存设个界限,也是大部分这类系统的常规作法,超出90天的数据就再也不提供数据详单了。好比,移动的通话记录最多保存半年,即180天,超过这个范围的数据不在提供查询。若是你实在须要,可能就要联系移动的工程师了。
分表前应该尽可能按照实际业务来分表,参考依据就是哪些字段在查询中起到做用,那就这些字段来分表,而且须要在分表前就估算好规模,也就是先肯定好规则在分表。
对于分表后的操做,依然是联合查询,视图等基本操做,或者使用merge引擎合并数据并在此表中查询。复杂一些操做须要借助存储过程来完成,借助外部工具实现对分表的管理。
对于比较庞大的数据,不管是否进行分表,都必须考虑功能和效率的平衡性,并在功能上作出让步。咱们不能事事迁就用户,而应该对某些影响效率的功能作出限制。例如移动公司的180天限制、论坛禁止对老帖进行回复等。