22.Mysql的分区表之浅谈

  1. mysql的分区功能不是在存储引擎层完成的,因此不会只有innodb存储引擎支持分区,常见的存储引擎有MYISM、NDB
  2. mysql的分区是在5.1版本时才支持的,分区的过程是将一个表或索引分成多个更小、更可管理的部分,就访问的数据库的应用而言,从逻辑上讲,只有一个表或一个索引,但是在物理上这个表或索引可能由数十个物理分区组成。每个分区都是独立的,可以独自处理。
  3. mysql数据库支持的分区类型为水平分区,并不支持垂直分区。且分区是局部分区索引,一个分区中既存放了数据又存放了索引,而全局分区是指,数据存放在各个分区中,但是所有数据的索引放在一个对象中。

一.查看当前数据库是否启动了分区功能:

root@localhost:(none)>show plugins;
+----------------------------+----------+--------------------+---------+---------+
| Name                       | Status   | Type               | Library | License |
+----------------------------+----------+--------------------+---------+---------+
| binlog                     | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| mysql_native_password      | ACTIVE   | AUTHENTICATION     | NULL    | GPL     |
| sha256_password            | ACTIVE   | AUTHENTICATION     | NULL    | GPL     |
| CSV                        | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| MyISAM                     | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| MRG_MYISAM                 | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| PERFORMANCE_SCHEMA         | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| MEMORY                     | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| InnoDB                     | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| INNODB_TRX                 | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_LOCKS               | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_LOCK_WAITS          | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMP                 | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMP_RESET           | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMPMEM              | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMPMEM_RESET        | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMP_PER_INDEX       | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMP_PER_INDEX_RESET | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_BUFFER_PAGE         | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_BUFFER_PAGE_LRU     | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_BUFFER_POOL_STATS   | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_TEMP_TABLE_INFO     | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_METRICS             | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_DEFAULT_STOPWORD | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_DELETED          | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_BEING_DELETED    | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_CONFIG           | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_INDEX_CACHE      | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_INDEX_TABLE      | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_TABLES          | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_TABLESTATS      | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_INDEXES         | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_COLUMNS         | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_FIELDS          | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_FOREIGN         | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_FOREIGN_COLS    | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_TABLESPACES     | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_DATAFILES       | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_VIRTUAL         | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| partition                  | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| ARCHIVE                    | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| FEDERATED                  | DISABLED | STORAGE ENGINE     | NULL    | GPL     |
| BLACKHOLE                  | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| ngram                      | ACTIVE   | FTPARSER           | NULL    | GPL     |
+----------------------------+----------+--------------------+---------+---------+

二.当前mysql支持的分区类型

  • range分区:行数据基于属于一个连续空间的列值被放入分区,mysql5.5开始支持range columns的分区
  • list分区:这种是定义列表的值对应的行数据,mysql5.5开始支持list columns的分区
  • hash分区:根据用户自动以的表达式的返回值来进行分区,返回值不能为负数
  • key分区:根据mysql数据库提供的哈希函数来进行分区

 

注意:不论哪种类型分区,如果表中存在主键或唯一索引时,分区列必须是唯一索引的一个组成部分

情况一:use test;
CREATE TABLE t1 (
    co11 INT NOT NULL,
    co12 date NOT NULL,
    co13 INT NOT NULL,
    co14 INT NOT NULL,
UNIQUE KEY ( co11, co12 )) PARTITION BY HASH ( co13 ) PARTITIONS 4;
1503 - A PRIMARY KEY must include all columns in the table's partitioning function, Time: 0.005000s

情况二:
CREATE TABLE t1 (
    co11 INT NOT NULL,
    co12 date NOT NULL,
    co13 INT NOT NULL,
    co14 INT NOT NULL,
UNIQUE KEY ( co11, co12,co13,co14 )) PARTITION BY HASH ( co13 ) PARTITIONS 4;
OK, Time: 0.072000s

 

如果建表时没有指定主键,唯一索引,可以指定任何一个列为分区列,因此下面两句创建分区的SQL语句都是可以运行的

CREATE TABLE t2 (
    co11 INT NULL,
    co12 date NULL,
    co13 INT NULL,
    co14 INT NULL 
) ENGINE = INNODB PARTITION BY HASH ( co13 ) PARTITIONS 4;


CREATE TABLE t3 (
    co11 INT NULL,
    co12 date NULL,
    co13 INT NULL,
    co14 INT NULL,
    KEY(co14) 
) ENGINE = INNODB PARTITION BY HASH ( co13 ) PARTITIONS 4;

在linux系统中显示情况如下:

[root@node01 data]# ls
auto.cnf  ib_buffer_pool  ibdata1  ib_logfile0  ib_logfile1  ibtmp1  mysql  performance_schema  slow.log  sys  test
[root@node01 data]# cd test
[root@node01 test]# ls
db.opt       t1#P#p1.ibd  t2.frm       t2#P#p2.ibd  t3#P#p0.ibd  t3#P#p3.ibd  test2.frm     userinfo.ibd
t1.frm       t1#P#p2.ibd  t2#P#p0.ibd  t2#P#p3.ibd  t3#P#p1.ibd  test1.frm    test2.ibd
t1#P#p0.ibd  t1#P#p3.ibd  t2#P#p1.ibd  t3.frm       t3#P#p2.ibd  test1.ibd    userinfo.frm

其中,其中每个表的有一个.frm文件,4个数据文件组成

 

三.range 分区

  range 分区是分区类型中最常见的分区的,下面的create table 语句创建了一个id列的区间分区表。当id小于10时,数据插入到p0分区,当id大于10小于20时,数据插入p1分区。

#range 分区:
CREATE TABLE t ( id INT ) ENGINE = INNODB PARTITION BY RANGE ( id )(
	PARTITION p0
	VALUES
		less than ( 10 ),
		PARTITION p1
	VALUES
	less than ( 20 ));

show tables;
#插入数据
insert into t VALUES(1),(2),(3),(10),(15);
[root@node01 test]# ls
db.opt  test1.frm  test1.ibd  test2.frm  test2.ibd  t.frm  t#P#p0.ibd  t#P#p1.ibd  userinfo.frm  userinfo.ibd

这里的表是根据id键进行分区的,因此,这里我们可以通过以下命令来查看每个分区的具体信息;

root@localhost:test>select * from information_schema.partitions where table_schema=database() and table_name='t'\G:
*************************** 1. row ***************************
                TABLE_CATALOG: def
                 TABLE_SCHEMA: test
                   TABLE_NAME: t
               PARTITION_NAME: p0
            SUBPARTITION_NAME: NULL
   PARTITION_ORDINAL_POSITION: 1
SUBPARTITION_ORDINAL_POSITION: NULL
             PARTITION_METHOD: RANGE
          SUBPARTITION_METHOD: NULL
         PARTITION_EXPRESSION:  id
      SUBPARTITION_EXPRESSION: NULL
        PARTITION_DESCRIPTION: 10
                   TABLE_ROWS: 3
               AVG_ROW_LENGTH: 5461
                  DATA_LENGTH: 16384
              MAX_DATA_LENGTH: NULL
                 INDEX_LENGTH: 0
                    DATA_FREE: 0
                  CREATE_TIME: 2021-02-05 00:17:00
                  UPDATE_TIME: 2021-02-05 00:19:56
                   CHECK_TIME: NULL
                     CHECKSUM: NULL
            PARTITION_COMMENT: 
                    NODEGROUP: default
              TABLESPACE_NAME: NULL
*************************** 2. row ***************************
                TABLE_CATALOG: def
                 TABLE_SCHEMA: test
                   TABLE_NAME: t
               PARTITION_NAME: p1
            SUBPARTITION_NAME: NULL
   PARTITION_ORDINAL_POSITION: 2
SUBPARTITION_ORDINAL_POSITION: NULL
             PARTITION_METHOD: RANGE
          SUBPARTITION_METHOD: NULL
         PARTITION_EXPRESSION:  id
      SUBPARTITION_EXPRESSION: NULL
        PARTITION_DESCRIPTION: 20
                   TABLE_ROWS: 2
               AVG_ROW_LENGTH: 8192
                  DATA_LENGTH: 16384
              MAX_DATA_LENGTH: NULL
                 INDEX_LENGTH: 0
                    DATA_FREE: 0
                  CREATE_TIME: 2021-02-05 00:17:00
                  UPDATE_TIME: 2021-02-05 00:19:56
                   CHECK_TIME: NULL
                     CHECKSUM: NULL
            PARTITION_COMMENT: 
                    NODEGROUP: default
              TABLESPACE_NAME: NULL
2 rows in set (0.00 sec)

 其中:table_rows列反映了每个分区中记录的数量,这里要注意因为是按照id进行了分配,所以如果我们插入的数值不再这些范围中,那么数据就会抛出一个ERROR 1526:table has no partition for value 30,

对于以上的问题,我们可以设置一个maxvalue值得分区,maxvalue可以理解为一个正无穷,因此所有大于等于20且小于maxvalue的值都会放在p2分区。

 

ALTER TABLE t ADD PARTITION (
    PARTITION p2
    VALUES
        less than MAXVALUE 
    );

 

用途:range 分区主要用于日期列分区,例如对于销售类的表,可以根据年来分区存放销售记录,如下面的分区表sales:

create table sales(money int unsigned not null,date datetime)engine=innodb partition by range(year(date))(partition p2008 values less than(2009),partition p2009 values less than(2010),partition p2010 values less than(2011));

插入数据

INSERT into sales select 100,'2008-02-01';

INSERT into sales select 100,'2009-02-01';

INSERT into sales select 100,'2010-02-01';

# 这样创建的好处是,对于sales这张表管理,如果我们要删除2008年的数据,不需要执行delete from sales where date>'2008-02-01' and date<'2009-02-01',只需要删除2008年数据所在的分区即可。

alter table sales drop partition p2008;

#第二,这里可以在某些情况下加快查询速度(这个看情况),这里看优化器在选择的时候是选择了那几个分区进行查询。

 

四.LIst分区

  list 分区和range分区非常相似,只是分区列的值是离散的,而非连续的。

CREATE TABLE t1 ( a INT, b INT ) ENGINE = INNODB PARTITION BY list ( b )(
    PARTITION p0
    VALUES
        IN ( 1, 3, 5, 7, 9 ),
        PARTITION p1
VALUES
    IN ( 0, 2, 4, 6, 8 ));
    

 然后再插入数据

insert into t1 select 1,1;
insert into t1 select 1,2;
insert into t1 select 1,3;
insert into t1 select 1,4;
insert into t1 select 1,5;

#注意对于不同的存储引擎来说,myism和innodb,其中myISM遇到对插入超过范围的值时,后面的数据无论是否合理都不能插入,但是前面合理的数据都是能进行插入的,但是对于innodb来说,只要有一条数据不合理,所有的数据都不能插入进去。

五.HASH分区

hash分区的目的是将数据均匀分布到预先定义的各个分区中,保证各分区的数据数量大致上是一样的,而在range和list分区中,必须要指定一个给定的列值或列值集合应该保存在哪个分区中,而在hash分区中,mysql自动完成这些工作,用户所需要做只是将要进行哈希分区的列值指定一个列值或表达式,以及指定被分区的表将要被分割成的分区数量。

要使用hash分区来分割一个表,要在create table语句上添加一个’partition by hash(expr)'子句,其中‘expr'是一个返回一个整数的表达式。它可以仅仅字段类型为mysql整型的列名,此外用户很可能需要在后面再添加一个’partition num'子句,其中num是一个非负的整数,它表示将要被分割成分区的数量,如果没有包括一个partitions子句,那么分区的数量将默认为1.

 

创建一个hash分区的表t,分区按日期b进行:
create table t_hash(a int,b datetime)engine=innodb partition by hash(year(b)) partitions 4;

这里如果插入一个列b为2010-04-01的记录到表t_hash中,那么保存该条记录的分区如下:
MOD(year('2010-04-01'),4)
=MOD(2010,4)
=2

 

....

六.分区和性能

  数据库的应用分为两类:一类是OLTP(在线事务处理),如Blog、电子商务、网络游戏等;另一类是OLAP(在线分析处理),如数据仓库、数据集中。

  因此,对于OLAP的应用,分区的确是可以很好地提高查询的性能,因为OLAP应用大多数查询需要频繁地扫描一张很大的表。例如有一张1亿行的表,其中有一个时间戳属性列。用户的查询需要从这张表中获取一年的数据,如果按照时间戳进行分区,则只需要扫描相应的分区即可,这就是前面的Patition pruning技术。对于OLTP的应用,分区应该小心,在这中应用下,通过不可能会获取一张大表中10%的数据,大部分都是通过索引返回几条记录即可。而根据B+树原理可知,对于一张大表,一般的B+树需要2~3次磁盘IO 因此B+树可以很好地完成操作,不需要分区的帮助,并且设计不好,分区可能会带来严重的性能问题。

 

七.使用分区表主要事项

  • 结合业务场景选择分区键,避免跨分区查询
  • 对分区表进行查询最好在WHERE从句中包含分区键
  • 具有主键或唯一索引的表,主键或唯一索引必须是分区键的一部分

 

posted on 2021-02-13 22:30  太白金星有点烦  阅读(56)  评论(0编辑  收藏  举报

导航