MySQL分区浅谈

MySQL分区


分区原理和优缺点

  • 分区原理

    分区表是由多个相关的底层表实现,这些底层表也是由句柄对象表示,所以我们也可以直接访问各个分区,存储引擎管理分区和一个普通表没有任何不同,存储引擎也无须知道这是一个普通表是分区表的一部分。

    在分区表上的操作是按照下面的操作逻辑进行:
    select 查询:
    当查询一份分区表的时候,分区层先打开并锁住所有的 底层表,优化器判断是否可以过滤部分分区,然后再调 用对应的存储接口访问各个分区。
    insert操作
    当写入一条记录时,分区层打开并锁住所有底层表,然后确定那个分区接受这条记录,再将这条记录写入对应的底层表
    delete操作
    当删除一条记录是,分区层先打开并锁住所有底层表,mysql先确定对象分区,最后对相应的底层表进行删除操作。
    update操作
    当更新一条数据时,分区层先打开并锁住所有的底层表,mysql先确定更新的记录再哪个分区,然后取出数据并更新,在判断更新后的数据应该放在哪个分区,然后对底层表进行写入操作,并对原数据的底层表进行删除操作

  • 分区使用的场景

    A : 表非常大以至于无法全部都放在内存中,或者只在表的最后部分有热点数据,其他都是历史数据
    B:分区表的数据容易维护,如:想批量删除数据可以清楚整个分区的方式,另外,还可以对一个独立分区进行优化、检查、修复等操作。
    C : 分区的数据可以分布在不同物理设备上,从而高效地利用多个硬件设备。
    D: 可以使用分区表来避免某些特殊的瓶颈
    E:如果需要,还可以备份和恢复独立的分页去,这在非常大的数据集场景下效果非常有效
    F:优化查询,在where字句中包含分区列时,可以只使用必要的分区来提高查询效率,同时在涉及sum()和count()这类聚合函数的查询时,可以在每个分区上面并行处理,最终只需要汇总所有分区得到的结果。

    • 分区限制

    A:一个表最多只能有1024个分区(mysql5.6之后支持8192个分区)
    B:在mysql5.1中分区表达式必须是整数,或者返回整数表达式,在5.5之后,某些场景可以直接使用字符串和日期类型列进行分区
    C:如果分区字段中有主键或者唯一索引列,那么所有主键列和唯一索引列都必须包含进来,如果表中有主键或唯一索引,那么分区键必须是主键或唯一索引。
    D:分区表中无法使用外键约束
    E:mysql数据库支持的分区类型为水平分区,并不支 持垂直分区,因此,mysql数据库的分区中索引是局部分区索引,一个分区中既存放了数据又存放了索引,而全局分区是指的数据库放在各个分区中,但是所有的数据的索引放在另外一个对象中
    F:目前mysql不支持空间类型和临时表类型进行分区。不支持全文索引

mysql分区类型

分区类型 说明 使用频率
range 对连续的值进行分区,例如日期,年月 较高
list 基于列值匹配一个离散值集合中的某个值来进行选择 一般
hash 分散热点读取,确保数据在预定确定个数分区中尽可能的平均分布 较多
key 类似于hash分区,区别在于KEY分区支持只支持计算一列或多列,且MySQL服务器提供自身的哈希函数。必须有一列或多列包含整数值 一般
range 分区
按照RANGE分区的表是通过如下一种方式进行分区的,每个分区包含那些分区表达式的值位于一个给定的连续区间内的行。一般使用这种分区方式大都是对连续的值进行分区,常见的如:按年份,日期进行分区。
CREATE TABLE IF NOT EXISTS ruanl_user (
 id INT(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
 user_name VARCHAR(50) NOT NULL COMMENT '用户姓名',
 sex  INT(2) NOT NULL COMMENT '性别 0为男 1为女',
 PRIMARY KEY(id)
  )ENGINE INNODB DEFAULT CHARSET = utf8 AUTO_INCREMENT = 1
  PARTITION BY RANGE (id) (
  PARTITION p0 VALUES LESS THAN (3),
  PARTITION p1 VALUES LESS THAN (6),
  PARTITION p2 VALUES LESS THAN (9),
  PARTITION p3 VALUES LESS THAN (12),
  PARTITION p4 VALUES LESS THAN maxvalue
  );

通过查看下面语句来查看分区分布

SELECT TABLE_NAME, PARTITION_NAME, TABLE_ROWS, AVG_ROW_LENGTH, DATA_LENGTH
        FROM INFORMATION_SCHEMA.PARTITIONS
        WHERE  TABLE_NAME LIKE 'ruanl_user'
list 分区
类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择。
PARTITION BY LIST(expr) 其中“expr” 是某列值或一个基于某个列值、并返回一个整数值的表达式
VALUES IN (value_list) 其中“value_list”是一个通过逗号分隔的整数列表
CREATE TABLE employees (
id INT NOT NULL,
first_name VARCHAR(30),
last_name VARCHAR(30),
store_id INT NOT NULL,
create_time DATE NOT NULL DEFAULT '9999-12-31',
)
PARTITION BY LIST(store_id)
PARTITION pNorth VALUES IN (1,5,6,9,17),
PARTITION pEast VALUES IN (2,4,10,11,19,20),
PARTITION pWest VALUES IN (3,12,13,14,18),
PARTITION pCentral VALUES IN (7,8,15,16)
)
*如果插入一行值时,里面的store_id不在上面的这列value_list时会出现插入失败并报错。而且。List分区没有range分区那样,range分区的“VALUES LESS THAN MAXVALUE”会将其他的值包含在内的定义*.
hash 分区

hash分区主要用来分散热点读取,确保数据在预定确定个数分区中尽可能的平均分布。一个表执行hash分区,mysql会对分区键应用一个散列函数,以此确定数据应该放在n个分区中的哪一个分区.

  • 取模(默认)
  • 线性2的幂次运算

常规hash分区

CREATE TABLE employees (
id INT NOT NULL,
first_name VARCHAR(30),
last_name VARCHAR(30),
store_id INT NOT NULL,
create_time DATE NOT NULL DEFAULT '9999-12-31',
)
PARTITION BY HASH(id)
PARTITIONS 5;
CREATE TABLE employees (
id INT NOT NULL,
first_name VARCHAR(30),
last_name VARCHAR(30),
store_id INT NOT NULL,
create_time DATE NOT NULL DEFAULT '9999-12-31',
)
PARTITION BY LINEAR HASH(id)
PARTITIONS 5
key 分区

key分区和hash分区的区别:

  • hash分区允许用户自定义的表达式,而key分区不允许使用用户自定义的表达式
  • hash分区只支持整数分区,key分区支持除了blob或text类型之外的其他数据类型分区
  • 与hash分区不同,创建key分区表的时候,可以不指定分区键,默认会选择使用主键/唯一键作为分区键,没有主键/唯一键,必须指定分区键。
column 分区
COLUMN分区是5.5开始引入的分区功能,只有RANGE COLUMN和LIST COLUMN这两种分区;支持整形、日期、字符串;
  • 支持类型

    整形支持:tinyint,smallint,mediumint,int,bigint;不支持decimal和float

    时间类型支持:date,datetime

    字符类型支持:char,varchar,binary,varbinary;不支持text,blob

  • range column

单字段分区

alter table employees  partition by range(column(create_time))
            (PARTITION p201701 VALUES LESS THAN ('2017-02-01')) ENGINE = InnoDB,
             PARTITION p201702 VALUES LESS THAN ('2017-02-01') ENGINE = InnoDB,
             PARTITION p201703 VALUES LESS THAN ('2017-02-01') ENGINE = InnoDB);

多字段分区

alter table employees  partition by range(column(store_id,create_time))
            (PARTITION p201701 VALUES LESS THAN (10,'2017-02-01')) ENGINE = InnoDB,
             PARTITION p201702 VALUES LESS THAN (20,'2017-02-01') ENGINE = InnoDB,
             PARTITION p201703 VALUES LESS THAN (30,'2017-02-01') ENGINE = InnoDB);

多字段的分区键比较是基于数组的比较。它先用插入的数据的第一个字段值和分区的第一个值进行比较,如果插入的第一个值小于分区的第一个值那么就不需要比较第二个值就属于该分区;如果第一个值等于分区的第一个值,开始比较第二个值同样如果第二个值小于分区的第二个值那么就属于该分区

  • list 分区
alter table employees  partition by list(column(create_time))
            (PARTITION p201701 VALUES LESS THAN ('2017-02-01','2017-03-01') ENGINE = InnoDB,
             PARTITION p201702 VALUES LESS THAN ('2017-04-01') ENGINE = InnoDB,
             PARTITION p201703 VALUES LESS THAN ('2017-05-01') ENGINE = InnoDB);
alter table employees  partition by list(column(id,create_time))
            (PARTITION p201701 VALUES LESS THAN ((1,'2017-02-01'),(1,'2017-03-01')) ENGINE = InnoDB,
             PARTITION p201702 VALUES LESS THAN (2,'2017-04-01') ENGINE = InnoDB,
             PARTITION p201703 VALUES LESS THAN (3,'2017-05-01') ENGINE = InnoDB);

## 分区操作

  • 创建表的时候,直接创建分区。注意主键
CREATE TABLE employees (
id INT NOT NULL,
first_name VARCHAR(30),
last_name VARCHAR(30),
store_id INT NOT NULL,
create_time DATE NOT NULL DEFAULT '9999-12-31',
)
partition BY 
range(to_days(create_time))
            (PARTITION p201701 VALUES LESS THAN (TO_DAYS('2017-02-01')) ENGINE = InnoDB,
             PARTITION p201702 VALUES LESS THAN (TO_DAYS('2017-03-01')) ENGINE = InnoDB,
              PARTITION p201703 VALUES LESS THAN (TO_DAYS('2017-04-01')) ENGINE = InnoDB);
  • 修改表的时候
alter table employees  partition by range(to_days(create_time))
            (PARTITION p201701 VALUES LESS THAN (TO_DAYS('2017-02-01')) ENGINE = InnoDB,
             PARTITION p201702 VALUES LESS THAN (TO_DAYS('2017-03-01')) ENGINE = InnoDB,
             PARTITION p201703 VALUES LESS THAN (TO_DAYS('2017-04-01')) ENGINE = InnoDB);
  • 增加分区
alter table employees add partition(PARTITION p2018 VALUES LESS THAN MAXVALUE);

只能从range分区列表最大端增加分区
增加list分区,不能添加一个包含现有分区值列表中的任意值分区,也就是说对一个固定的分区键值,必须指定并且只能指定一个 唯一的分区

  • 删除分区
alter table employees drop partition p201702;

这种删除会是删除表中的数据

  • 移除表的分区
alter table employees remove partitioning

使用remove移除分区是仅仅移除分区的定义,并不会删除数据和drop PARTITION不一样,后者会连同数据一起删除

  • 重新定义分区
ALTER TABLE employees REORGANIZE PARTITION p201701,p201702,p201703,p2018 INTO (PARTITION p0 VALUES LESS THAN MAXVALUE); 

将p201701,p201702,p201703,p2018这几个分区数据移到p0分区

注意 hash 和 key 分区不能使用 REORGANIZE

## 日期函数分区方法

  • 使用YEAR() 函数进行分区
alter table employees  partition by range(YEAR(create_time))
            (PARTITION p201701 VALUES LESS THAN ('2015-02-01') ENGINE = InnoDB,
             PARTITION p201702 VALUES LESS THAN ('2016-02-01') ENGINE = InnoDB,
             PARTITION p201703 VALUES LESS THAN ('2017-02-01') ENGINE = InnoDB);

对于日期字段分区,查询条件使用> 、< 、betnwen、=都会利用分区查询,如果条件使用函数转换则不会走分区,比如使用YEAR()。

  • TIMESTAMP类型的列的分区方法
alter table employees  partition by range(UNIX_TIMESTAMP(create_time))
            (PARTITION p201701 VALUES LESS THAN (UNIX_TIMESTAMP('2015-02-01')) ENGINE = InnoDB,
             PARTITION p201702 VALUES LESS THAN (UNIX_TIMESTAMP('2016-02-01')) ENGINE = InnoDB,
             PARTITION p201703 VALUES LESS THAN (UNIX_TIMESTAMP('2017-02-01')) ENGINE = InnoDB);
  • null值处理

当往分区列中插入null值RANG 分区会将其当作最小值来处理即插入最小的分区中

你可能感兴趣的:(MySQL分区浅谈)