mysql中任意有效的函数_MYSQL之表分区

分区优势

1、冷热分离:表非常大且只在表的最后部分有热点数据,冷数据根据分区规则自动归档。

2、定期淘汰历史数据:按时间写入,历史数据可淘汰,可快速删除,空间可快速回收。

3、优化查询:在where字句中包含分区列时,分区可以大大提高查询效率,减少缓存开销、减少IO开销。

4、统计性能提升:在涉及sum()和count()这类聚合函数的查询时,可以在每个分区上面并行处理,最终只需要汇总所有分区得到的结果。

分区类型

目前MySQL支持范围分区(RANGE),列表分区(LIST),哈希分区(HASH)以及KEY分区四种。下面我们逐一介绍每种分区:

RANGE分区

基于属于一个给定连续区间的列值,把多行分配给分区。最常见的是基于时间字段. 基于分区的列最好是整型,如果日期型的可以使用函数转换为整型。

CREATE TABLE `kx_storage_log_part1` (

`id` int(10) unsigned NOT NULL AUTO_INCREMENT,

`brandid` int(11) NOT NULL DEFAULT '1' COMMENT '品牌商id',

`in_store` varchar(32) NOT NULL DEFAULT '' COMMENT '入库方',

`out_store` varchar(32) NOT NULL DEFAULT '' COMMENT '出库方',

`price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '出入库单价',

`created_at` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',

PRIMARY KEY (`id`,`created_at`)

) ENGINE=InnoDB AUTO_INCREMENT=15925508 DEFAULT CHARSET=utf8 COMMENT='库存日志表'

p11是一个默认分区,所有大于1522512000的记录都会在这个分区。MAXVALUE是一个无穷大的值。p11是一个可选分区。如果在定义表的没有指定的这个分区,当我们插入大于1522512000的数据的时候,会收到一个错误。

我们在执行查询的时候,必须带上分区字段。这样可以使用分区剪裁功能

mysql> insert into kx_storage_log_part1 select * from kx_storage_log;

mysql> select count(1) from kx_storage_log where created_at <= 1494657605;

+----------+

| count(1) |

+----------+

| 1082 |

+----------+

1 row in set (54.11 sec)

mysql> select count(1) from kx_storage_log_part1 where created_at <= 1494657605;

+----------+

| count(1) |

+----------+

| 1075 |

+----------+

1 row in set (0.27 sec)

mysql> explain select count(1) from kx_storage_log_part1 where created_at <= 1496246400;

+----+-------------+----------------------+------------+-------+---------------+------+---------+------+--------+----------+--------------------------+

| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |

+----+-------------+----------------------+------------+-------+---------------+------+---------+------+--------+----------+--------------------------+

| 1 | SIMPLE | kx_storage_log_part1 | p0,p1,p2 | index | NULL | type | 1 | NULL | 332999 | 33.33 | Using where; Using index |

+----+-------------+----------------------+------------+-------+---------------+------+---------+------+--------+----------+--------------------------+

在5.7版本之前,对于DATA和DATETIME类型的列,如果要实现分区裁剪,只能使

YEAR() 和TO_DAYS()函数,在5.7版本中,又新增了TO_SECONDS()函数

mysql> CREATE TABLE part_date3

-> ( c1 int default NULL,

-> c2 varchar(30) default NULL,

-> c3 date default NULL) engine=myisam

-> partition by range (to_days(c3))

-> (PARTITION p0 VALUES LESS THAN (to_days('1995-01-01')),

-> PARTITION p1 VALUES LESS THAN (to_days('1996-01-01')) ,

-> PARTITION p2 VALUES LESS THAN (to_days('1997-01-01')) ,

-> PARTITION p3 VALUES LESS THAN (to_days('1998-01-01')) ,

-> PARTITION p4 VALUES LESS THAN (to_days('1999-01-01')) ,

-> PARTITION p5 VALUES LESS THAN (to_days('2000-01-01')) ,

-> PARTITION p6 VALUES LESS THAN (to_days('2001-01-01')) ,

-> PARTITION p7 VALUES LESS THAN (to_days('2002-01-01')) ,

-> PARTITION p8 VALUES LESS THAN (to_days('2003-01-01')) ,

-> PARTITION p9 VALUES LESS THAN (to_days('2004-01-01')) ,

-> PARTITION p10 VALUES LESS THAN (to_days('2010-01-01')),

-> PARTITION p11 VALUES LESS THAN MAXVALUE );

Query OK, 0 rows affected (0.00 sec)

LIST分区

LIST分区和RANGE分区类似,区别在于LIST是枚举值列表的集合,RANGE是连续的区间值的集合。二者在语法方面非常的相似。同样建议LIST分区列是非null列,否则插入null值如果枚举列表里面不存在null值会插入失败,这点和其它的分区不一样,RANGE分区会将其作为最小分区值存储,HASH\KEY分为会将其转换成0存储,主要LIST分区只支持整形,非整形字段需要通过函数转换成整形.

create table t_list(

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)

);

Hash分区

我们在实际工作中经常遇到像会员表的这种表。并没有明显可以分区的特征字段。但表数据有非常庞大。为了把这类的数据进行分区打散mysql 提供了hash分区。基于给定的分区个数,将数据分配到不同的分区,HASH分区只能针对整数进行HASH,对于非整形的字段只能通过表达式将其转换成整数。表达式可以是mysql中任意有效的函数或者表达式,对于非整形的HASH往表插入数据的过程中会多一步表达式的计算操作,所以不建议使用复杂的表达式这样会影响性能。

Hash分区表的基本语句如下:

CREATE TABLE my_member (

id INT NOT NULL, fname VARCHAR(30),

lname VARCHAR(30),

created DATE NOT NULL DEFAULT '1970-01-01',

separated DATE NOT NULL DEFAULT '9999-12-31',

job_code INT,

store_id INT )

PARTITION BY HASH(id)

PARTITIONS 4;

注意:

HASH分区可以不用指定PARTITIONS子句,如上文中的PARTITIONS 4,则默认分区数为1。

不允许只写PARTITIONS,而不指定分区数。

同RANGE分区和LIST分区一样,PARTITION BY HASH (expr)子句中的expr返回的必须是整数值。

HASH分区的底层实现其实是基于MOD函数。譬如,对于下表

CREATE TABLE t1 (

col1 INT,

col2 CHAR(5),

col3 DATE)

PARTITION BY HASH( YEAR(col3) )

PARTITIONS 4;

如果你要插入一个col3为“2017-09-15”的记录,则分区的选择是根据以下值决定的:

MOD(YEAR(‘2017-09-01’),4) = MOD(2017,4) = 1

LINEAR HASH分区

LINEAR HASH分区是HASH分区的一种特殊类型,与HASH分区是基于MOD函数不同的是,它基于的是另外一种算法。

格式如下:

CREATE TABLE my_members (

id INT NOT NULL, fname VARCHAR(30),

lname VARCHAR(30),

hired DATE NOT NULL DEFAULT '1970-01-01',

separated DATE NOT NULL DEFAULT '9999-12-31',

job_code INT, store_id INT )

PARTITION BY LINEAR HASH( id )

PARTITIONS 4;

说明: 它的优点是在数据量大的场景,譬如TB级,增加、删除、合并和拆分分区会更快,缺点是,相对于HASH分区,它数据分布不均匀的概率更大。

KEY分区

KEY分区其实跟HASH分区差不多,不同点如下:

KEY分区允许多列,而HASH分区只允许一列。

如果在有主键或者唯一键的情况下,key中分区列可不指定,默认为主键或者唯一键,如果没有,则必须显性指定列。

KEY分区对象必须为列,而不能是基于列的表达式。

KEY分区和HASH分区的算法不一样,PARTITION BY HASH (expr),MOD取值的对象是expr返回的值,而PARTITION BY KEY (column_list),基于的是列的MD5值。

格式如下:

CREATE TABLE k1 (

id INT NOT NULL PRIMARY KEY,

name VARCHAR(20) )

PARTITION BY KEY()

PARTITIONS 2;

在没有主键或者唯一键的情况下,格式如下:

CREATE TABLE tm1 (

s1 CHAR(32) )

PARTITION BY KEY(s1)

PARTITIONS 10;

一、RANGE partitioning

CREATE TABLE members01 (

id int(11) NOT NULL AUTO_INCREMENT,

username VARCHAR(16) NOT NULL,

email VARCHAR(35),

joined DATETIME NOT NULL,

PRIMARY KEY (id,joined),

UNIQUE KEY `uk_username` (username,email,joined)

)

PARTITION BY RANGE( TO_DAYS(joined) ) (

PARTITION p20170801 VALUES LESS THAN (736908),

PARTITION p20170802 VALUES LESS THAN (736909)

);

这种是最常见的,也是我们MDB平台提供自动按天见分区的格式。一般也比较适合按天分区,或者固定范围的分区,比如时间范围,只能按照数字大小(年龄/编号)进行区间划分。

优势:

1、按分区快速淘汰历史数据

2、按分区字段的范围查询

二、LIST partitioning

CREATE TABLE members02 (

id int(11) NOT NULL AUTO_INCREMENT,

username VARCHAR(16) NOT NULL,

email VARCHAR(35),

joined DATETIME NOT NULL,

PRIMARY KEY (id,joined),

UNIQUE KEY `uk_username` (username,email,joined)

)

PARTITION BY LIST( TO_DAYS(joined) ) (

PARTITION p20170801 VALUES IN (736905,736907),

PARTITION p20170803 VALUES IN (736908,736909)

);

表面上看,咦?好像使用list分区的都可以使用rang分区实现呢,其实大部分场景两种分区方式都是可以实现的,线上实际只能使用list分区的场景也比较少。

连续数据更趋向于使用range分区, list分区一般比较适合离散数据的分区,同时可以将多个离散的属性归类存储,比如我需要把20170801、20170803、20170809三个时间的数据放一个分区,20170802、20170805、20170808放个分区,这种就适合使用list分区,针对自己业务特性进行离散的分区,可以非常灵活的将数据打散到不同的分区。可以看出这种分区策略就不适合where条件的范围查询,适合固定值的in条件查询。

优势:

1、灵活的离散数据分区,可自定义分区list规则。

2、 离散分区不适合where条件date>20170801 and date >20170809,适合固定分区的等值查询或in条件查询

HASH partitioning

CREATE TABLE members03 (

id int(11) NOT NULL AUTO_INCREMENT,

username VARCHAR(16) NOT NULL,

email VARCHAR(35),

joined DATETIME NOT NULL,

PRIMARY KEY (id,joined),

UNIQUE KEY `uk_username` (username,email,joined)

)

PARTITION BY hash(TO_DAYS(joined))

PARTITIONS 2;

hash分区很好理解,就是对指定列做hash,均匀的存到指定的分区,比如按用户名hash分区,那么按用户名进行查找的速度就会快很多,这种针对分区列数据不固定,想把数据根据分区列离散的存储到固定分区数的表中,不需要做数据淘汰的场景比较适合。

优势:

1、维护简单,分区数固定,根据hash自动分区。

2、适合固定条件的等值查询

3、对于分区列数据不固定,分区列值不固定(不适合list),可根据hash值均匀打散数据到不同分区。

KEY partitioning

CREATE TABLE members04 (

id int(11) NOT NULL AUTO_INCREMENT,

username VARCHAR(16) NOT NULL,

email VARCHAR(35),

joined DATETIME NOT NULL,

PRIMARY KEY (id,joined),

UNIQUE KEY `uk_username` (username,email,joined)

)

PARTITION BY key(joined)

PARTITIONS 4;

同样,使用key分区跟hash分区有着神奇的相似,不同的是,如果表有主键或者唯一键的时候无需指定key的列名,key分区自动根据键值进行分区。

优势:

对于有主键的表,可无需关心分区列,MySQL自行根据主键/唯一键分区。如果主键设置不合理,查询条件都不带主键,查询性能会很差。

删除分区

移除分区:ALTER TABLE tablename REMOVE PARTITIONING ;

删除分区:ALTER TABLE tablename DROP PARTITIONING ;

移除分区仅仅修改表分区定义,数据不会被删除;删除分区会删除分区定义同时删除分区上的数据。

你可能感兴趣的:(mysql中任意有效的函数)