天天看点

19_MySQL分表、分区为什么要分表和分区?一、分表二、分区

文章目录

  • 为什么要分表和分区?
  • 一、分表
    • 分表的几种方式
        • 1.mysql集群
        • 2.预先估计会出现大数据量并且访问频繁的表,将其分为若干个表
        • 3.利用merge存储引擎来实现分表
  • 二、分区
    • RANGE分区
      • 分区表和未分区表性能测试
      • 将不同分区放到不同存储位置
    • LIST分区
    • HASH分区
    • KEY分区
    • COLUMNS分区
    • 总结
      • mysql分表和分区有什么区别呢
      • mysql分表和分区有什么联系?

为什么要分表和分区?

我们的数据库数据越来越大,随之而来的是单个表中数据太多。以至于查询速度变慢,而且由于表的锁机制导致应用操作也搜到严重影响,出现了数据库性能瓶颈。

mysql中有一种机制是表锁定和行锁定,是为了保证数据的完整性。表锁定表示你们都不能对这张表进行操作,必须等我对表操作完才行。行锁定也一样,别的sql必须等我对这条数据操作完了,才能对这条数据进行操作。当出现这种情况时,我们可以考虑分表或分区。

一、分表

什么是分表?

分表是将一个大表按照一定规划的分解成多张具有独立存储空间的实体表,每个表都对应三个文件,MYD数据文件,.MYI索引文件,.frm表结构文件。这些表可以分布在同一块磁盘上,也可以在不同的机器上。app读写的时候根据事先定义好的规则得到对应的表名,然后去操作它。

将单个数据库表进行拆分,拆分成多个数据表,然后用户访问的时候,根据一定的算法(如用hash的方式,也可以用求余(取模)的方式),让用户访问不同的表,这样数据分散到多个数据表中,减少了单个数据表的访问压力。提升了数据库访问性能。分表的目的就在于此,减小数据库的负担,缩短查询时间。

Mysql分表分为垂直切分和水平切分

垂直切分是指数据表列的拆分

,把一张列比较多的表拆分为多张表 通常我们按以下原则进行垂直拆分: 把不常用的字段单独放在一张表; 把text,blob(binary large object,二进制大对象)等大字段拆分出来放在附表中;经常组合查询的列放在一张表中; 垂直拆分更多时候就应该在数据表设计之初就执行的步骤,然后查询的时候用join关键起来即可。

水平拆分是指数据表行的拆分

,把一张的表的数据拆成多张表来存放。 水平拆分原则 通常情况下,我们使用hash、取模等方式来进行表的拆分 比如一张有400W的用户表users,为提高其查询效率我们把其分成4张表users1,users2,users3,users4 通过用ID取模的方法把数据分散到四张表内Id%4= [0,1,2,3] 然后查询,更新,删除也是通过取模的方法来查询 部分业务逻辑也可以通过地区,年份等字段来进行归档拆分; 进行拆分后的表,这时我们就要约束用户查询行为。比如我们是按年来进行拆分的,这个时候在页面设计上就约束用户必须要先选择年,然后才能进行查询。

分表的几种方式

1.mysql集群

它并不是分表,但起到了和分表相同的作用。集群可分担数据库的操作次数,将任务分担到多台数据库上。集群可以读写分离,减少读写压力。从而提升数据库性能。

2.预先估计会出现大数据量并且访问频繁的表,将其分为若干个表

根据一定的算法(如用hash的方式,也可以用求余(取模)的方式)让用户访问不同的表。 例如论坛里面发表帖子的表,时间长了这张表肯定很大,几十万,几百万都有可能。聊天室里面信息表,几十个人在一起一聊一个晚上,时间长了,这张表的数据肯定很大。像这样的情况很多。所以这种能预估出来的大数据量表,我们就事先分出个N个表,这个N是多少,根据实际情况而定。以聊天信息表为例:我们事先建100个这样的表,message_00,message_01,message_02…message_98,message_99.然后根据用户的ID来判断这个用户的聊天信息放到哪张表里面,可以用hash的方式来获得,也可以用求余的方式来获得,方法很多。 或者可以设计每张表容纳的数据量是N条,那么如何判断某张表的数据是否容量已满呢?可以在程序段对于要新增数据的表,在插入前先做统计表记录数量的操作,当<N条数据,就直接插入,当已经到达阀值,可以在程序段新创建数据库表(或者已经事先创建好),再执行插入操作)。

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

如果要把已有的大数据量表分开比较痛苦,最痛苦的事就是改代码,因为程序里面的sql语句已经写好了,用merge存储引擎来实现分表, 这种方法比较适合。 merge分表,分为主表和子表,主表类似于一个壳子,逻辑上封装了子表,实际上数据都是存储在子表中的。 我们可以通过主表插入和查询数据,如果清楚分表规律,也可以直接操作子表。 下面我们来实现一个简单的利用merge存储引擎来实现分表的演示: 创建一个完整表存储着所有的成员信息(表名为member)

//注:必须是myisam引擎的表才可以

mysql> create database qin;
Query OK, 1 row affected (0.00 sec)

mysql> use qin;
Database changed
mysql> create table member (
    -> id bigint auto_increment primary key,
    -> name varchar(33),
    -> sex tinyint not null default '0')
    -> engine=myisam default charset=utf8 auto_increment=1;
Query OK, 0 rows affected (0.01 sec)
           

//在创建表里添加数据

mysql> insert into member(name,sex) values('tom',1);
Query OK, 1 row affected (0.06 sec)

mysql> insert into member(name,sex) select name,sex from member;
//多执行几次这条语句就有很多内容了
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0
           
mysql> select * from member;
+----+------+-----+
| id | name | sex |
+----+------+-----+
|  1 | tom  |   1 |
|  2 | tom  |   1 |
|  3 | tom  |   1 |
|  4 | tom  |   1 |
|  5 | tom  |   1 |
|  6 | tom  |   1 |
|  7 | tom  |   1 |
|  8 | tom  |   1 |
|  9 | tom  |   1 |
| 10 | tom  |   1 |
| 11 | tom  |   1 |
| 12 | tom  |   1 |
| 13 | tom  |   1 |
| 14 | tom  |   1 |
| 15 | tom  |   1 |
| 16 | tom  |   1 |
+----+------+-----+
16 rows in set (0.00 sec)
           

下面我们进行分表,这里我们吧member分两个表分别为tb_member1,tb_member2

//创建tb_member1,tb_member2两个表,表结构要和要分离的表(member)结构一样

mysql> create table tb_member1 ( id bigint auto_increment primary key, name varchar(33), sex tinyint not null default '0') engine=myisam default charset=utf8 auto_increment=1;
Query OK, 0 rows affected (0.01 sec)

mysql> create table tb_member2 ( id bigint auto_increment primary key, name varchar(33), sex tinyint not null default '0') engine=myisam default charset=utf8 auto_increment=1;
Query OK, 0 rows affected (0.00 sec)
           

//创建主表tb_member

主表引擎使用merge,使用union将两个子表(tb_member1,tb_member2)链接起来

mysql> create table tb_member ( id bigint auto_increment primary key, name varchar(33), sex tinyint not null default '0') 
    -> engine=merge union=(tb_member1,tb_member2) insert_method=last charset=utf8;
Query OK, 0 rows affected (0.01 sec)
           

INSERT_METHOD参数解释:

INSERT_METHOD = NO 表示该表不能做任何写入操作只作为查询使用。

INSERT_METHOD = LAST表示插入到最后的一张表里面。

INSERT_METHOD = first表示插入到第一张表里面。

//查看主表结构

mysql> desc tb_member;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | bigint(20)  | NO   | PRI | NULL    | auto_increment |
| name  | varchar(33) | YES  |     | NULL    |                |
| sex   | tinyint(4)  | NO   |     | 0       |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
           
注意:查看子表与主表的字段定义要一致

//接下来,我们把数据分到两个子表中去

mysql> insert into tb_member1(id,name,sex) select id,name,sex from member where id%2=0;
#将member表中id%2=0的数据插入到 tb_member1表中
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql> insert into tb_member2(id,name,sex) select id,name,sex from member where id%2=1;
#将member表中id%2=1的数据插入到 tb_member2表中
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0

           

//查看两个子表的数据

mysql> select * from tb_member1;
+----+------+-----+
| id | name | sex |
+----+------+-----+
|  2 | tom  |   1 |
|  4 | tom  |   1 |
|  6 | tom  |   1 |
|  8 | tom  |   1 |
| 10 | tom  |   1 |
| 12 | tom  |   1 |
| 14 | tom  |   1 |
| 16 | tom  |   1 |
+----+------+-----+
8 rows in set (0.00 sec)

mysql> select * from tb_member2;
+----+------+-----+
| id | name | sex |
+----+------+-----+
|  1 | tom  |   1 |
|  3 | tom  |   1 |
|  5 | tom  |   1 |
|  7 | tom  |   1 |
|  9 | tom  |   1 |
| 11 | tom  |   1 |
| 13 | tom  |   1 |
| 15 | tom  |   1 |
+----+------+-----+
8 rows in set (0.00 sec)
           

//查看主表数据

mysql> select * from tb_member;
+----+------+-----+
| id | name | sex |
+----+------+-----+
|  2 | tom  |   1 |
|  4 | tom  |   1 |
|  6 | tom  |   1 |
|  8 | tom  |   1 |
| 10 | tom  |   1 |
| 12 | tom  |   1 |
| 14 | tom  |   1 |
| 16 | tom  |   1 |
|  1 | tom  |   1 |
|  3 | tom  |   1 |
|  5 | tom  |   1 |
|  7 | tom  |   1 |
|  9 | tom  |   1 |
| 11 | tom  |   1 |
| 13 | tom  |   1 |
| 15 | tom  |   1 |
+----+------+-----+
16 rows in set (0.00 sec)
           

注意:主表只是一个外壳,存取数据发生在一个一个的子表里面。 注意:每个子表都有自已独立的相关表文件,而主表只是一个壳,并没有完整的相关表文件

19_MySQL分表、分区为什么要分表和分区?一、分表二、分区

二、分区

分区和分表相似,都是按照规则分解表。不同在于分表将大表分解为若干个独立的实体表,而分区是将数据分段划分在多个位置存放,分区后,表还是一张表,但数据散列到多个位置了。app读写的时候操作的还是表名字,db自动去组织分区的数据。

分区主要有两种形式:

水平分区

(Horizontal Partitioning)这种形式分区是对表的行进行分区,所有在表中定义的列在每个数据集中都能找到,所以表的特性依然得以保持。

举个简单例子:一个包含十年发票记录的表可以被分区为十个不同的分区,每个分区包含的是其中一年的记录。

垂直分区

(Vertical Partitioning)这种分区方式一般来说是通过对表的垂直划分来减少目标表的宽度,使某些特定的列被划分到特定的分区,每个分区都包含了其中的列所对应的行。

举个简单例子:一个包含了大text和BLOB列的表,这些text和BLOB列又不经常被访问,这时候就要把这些不经常使用的text和BLOB了划分到另一个分区,在保证它们数据相关性的同时还能提高访问速度。

//分区技术支持 在5.6之前,使用这个参数查看当将配置是否支持分区

mysql> SHOW VARIABLES LIKE '%partition%';
+-----------------------+---------------+
|Variable_name | Value |
+-----------------------+---------------+
| have_partition_engine | YES |
+-----------------------+------------------+
           

如果是yes表示你当前的配置支持分区

//在5.6及以采用后,则采用如下方式进行查看

19_MySQL分表、分区为什么要分表和分区?一、分表二、分区

在显示结果中,可以看到partition是ACTIVE的,表示支持分区

RANGE分区

RANGE分区 基于属于一个给定连续区间的列值,把多行分配给分区。这些区间要连续且不能相互重叠,使用VALUES LESS THAN操作符来进行定义。

mysql>  create table user (
    ->     id int not null auto_increment,
    ->     name varchar(30) not null default '',
    ->     sex int(10) not null default '0',
    ->     primary key(id))
    ->     default charset=utf8 auto_increment=1
    ->     partition by range(id) (          #针对id分区
    ->     partition p0 values less than(3), #id小于3的放到p0分区
    ->     partition p1 values less than(6), #id小于6的放到p1分区,以此类推
    ->     partition p2 values less than(9),
    ->     partition p3 values less than(12),
    ->     partition p4 values less than maxvalue #其他的放到p4分区
    ->     );
Query OK, 0 rows affected (0.03 sec)
           

//插入数据

mysql> insert into user(name,sex)values ('tom1','0'),('tom2','0'),('tom3','0'),('tom4','0'),('tom5','0'),('tom6','0'),('tom7','0'),('tom8','0'),('tom9','0'),('tom1','10'),('tom1','11'),('tom1','12'),('tom1','13'),('tom1','14');
Query OK, 14 rows affected (0.00 sec)
Records: 14  Duplicates: 0  Warnings: 0

mysql> select * from user;
+----+------+-----+
| id | name | sex |
+----+------+-----+
|  1 | tom1 |   0 |
|  2 | tom2 |   0 |
|  3 | tom3 |   0 |
|  4 | tom4 |   0 |
|  5 | tom5 |   0 |
|  6 | tom6 |   0 |
|  7 | tom7 |   0 |
|  8 | tom8 |   0 |
|  9 | tom9 |   0 |
| 10 | tom1 |  10 |
| 11 | tom1 |  11 |
| 12 | tom1 |  12 |
| 13 | tom1 |  13 |
| 14 | tom1 |  14 |
+----+------+-----+
14 rows in set (0.00 sec)
           

到存放数据库表文件的地方看一下

19_MySQL分表、分区为什么要分表和分区?一、分表二、分区

//查看分区信息

从information_schema系统库中的partitions表中查看分区信息

mysql> select * from information_schema.partitions where table_schema='qin' and table__name='user'\G
*************************** 1. row ***************************
                TABLE_CATALOG: def
                 TABLE_SCHEMA: qin
                   TABLE_NAME: user
               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: 3
                   TABLE_ROWS: 2
               AVG_ROW_LENGTH: 8192
                  DATA_LENGTH: 16384
              MAX_DATA_LENGTH: NULL
                 INDEX_LENGTH: 0
                    DATA_FREE: 0
                  CREATE_TIME: 2021-01-20 22:36:33
                  UPDATE_TIME: 2021-01-20 22:44:04
                   CHECK_TIME: NULL
                     CHECKSUM: NULL
            PARTITION_COMMENT: 
                    NODEGROUP: default
              TABLESPACE_NAME: NULL
*************************** 2. row ***************************
                TABLE_CATALOG: def
                .......省略
           

从某个分区中查询数据

mysql> select * from qin.user partition(p1); #查看p1分区内容
+----+------+-----+
| id | name | sex |
+----+------+-----+
|  3 | tom3 |   0 |
|  4 | tom4 |   0 |
|  5 | tom5 |   0 |
+----+------+-----+
3 rows in set (0.00 sec)
           

//删除分区

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

mysql> alter table qin.user drop partition p4;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0
           

//新增分区

mysql> alter table qin.user add partition (partition tp4 values less than maxvalue);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0
           

//合并分区

下面的SQL,将p1 – p3合并为2个分区p01– p02

mysql> alter table qin.user
    -> reorganize partition p1,p2,p3 into 
    -> (partition p01 values less than (8),
    -> partition p02 values less than (12)
    -> );
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0
           
19_MySQL分表、分区为什么要分表和分区?一、分表二、分区

分区表和未分区表性能测试

//创建一个未分区表

mysql> create table tab1 (c1 int , c2 varchar(22),c3 date);
Query OK, 0 rows affected (0.01 sec)
           

//创建分区表,按日期的年份拆分

mysql> create table tab2 (c1 int,c2 varchar(22),c3 date)
    ->      partition by range(year(c3)) (
    ->      partition p0 values less than(1995),
    ->      partition p1 values less than (1996),partition p2 values less than(1997),
    ->      partition p3 values less than (1998),partition p4 values less than(1999),
    ->      partition p5 values less than (2000),partition p6 values less than(2001),
    ->      partition p7 values less than (2002),partition p8 values less than(2003),
    ->      partition p9 values less than (2004),partition p10 values less than(2010),
    ->      partition p11 values less than maxvalue
    ->      );
Query OK, 0 rows affected (0.04 sec)
           

//通过存储过程插入200万条测试数据模拟大数据

创建存储过程

mysql>  delimiter $$
mysql> CREATE PROCEDURE load_t1() 
    ->  begin 
    ->  declare i int default 0; 
    ->  while i < 2000000 
    ->  do 
    ->  insert into tab1 values (i,'testing partitions',adddate('1995-01-01',(rand(i)*36520) mod 3652)); 
    ->  set i = i + 1; 
    ->  end while; 
    ->  end 
    ->  $$
Query OK, 0 rows affected (0.00 sec)

mysql> delimiter ;
mysql> call load_t1();
           

//向tab2表中插入数据

//测试sql性能

mysql> select count(*) from tab1 where  c3 > '1995-01-01' and c3 < '1995-12-31';
+----------+
| count(*) |
+----------+
|   198799 |
+----------+
1 row in set (1.25 sec)

mysql> select count(*) from tab2 where  c3 > '1995-01-01' and c3 < '1995-12-31';
+----------+
| count(*) |
+----------+
|   198799 |
+----------+
1 row in set (0.14 sec)
           
19_MySQL分表、分区为什么要分表和分区?一、分表二、分区

结果表明分区表比未分区表的执行时间少很多。

//通过explain语句来分析执行情况

mysql> explain select  count(*) from tab1 where  c3 > '1995-01-01' and c3 < '1995-12-331'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tab1
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1896888    #未分区扫描
     filtered: 11.11
        Extra: Using where
1 row in set, 1 warning (0.00 sec)

mysql> explain select  count(*) from tab2 where  c3 > '1995-01-01' and c3 < '1995-12-331'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tab2
   partitions: p1
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 199332     #分区扫描
     filtered: 11.11
        Extra: Using where
1 row in set, 1 warning (0.00 sec)
           

explain语句显示了SQL查询要处理的记录数目可以看出分区表比未分区表的明显扫描的记录要少很多。

//创建索引后情况测试

mysql> create index index_t1 on tab1(c3);
Query OK, 0 rows affected (2.66 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> create index index_t2 on tab2(c3);
Query OK, 0 rows affected (2.24 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> flush tables;
Query OK, 0 rows affected (0.00 sec)
           
mysql> select count(*) from tab1 where  c3 > '1995-01-01' and c3 < '1995-12-31';
+----------+
| count(*) |
+----------+
|   198799 |
+----------+
1 row in set (0.11 sec)

mysql> select count(*) from tab2 where  c3 > '1995-01-01' and c3 < '1995-12-31';
+----------+
| count(*) |
+----------+
|   198799 |
+----------+
1 row in set (0.12 sec)
           

创建索引后分区表比未分区表相差不大(数据量越大差别会明显些)

将不同分区放到不同存储位置

//建表时,提前创建好存储目录,并授权给mysql

[[email protected] ~]# mkdir -p /data/area{1..3}
[[email protected] ~]# ls /data/
area1  area2  area3
[[email protected] ~]# chown -R mysql:mysql /data/
[[email protected] ~]# ls -l /data/
total 0
drwxr-xr-x. 2 mysql mysql 6 Jan 22 17:49 area1
drwxr-xr-x. 2 mysql mysql 6 Jan 22 17:49 area2
drwxr-xr-x. 2 mysql mysql 6 Jan 22 17:49 area3
           

//创建表格

注:使用mysql默认的存储引擎inodb时候,只需要指定data directory 就可以,因为inodb的数据和索引在一个文件中。但是创建表格时指定engine=myisam时,修改分区的存储位置,需要同时指定datadirectory和index directory。

mysql>   create table user1(id int not null auto_increment,
    ->     name varchar(22) not null default '',
    ->     primary key(id)) 
    ->     default  charset=utf8 auto_increment=1
    ->     partition by range(id)(
    ->     partition p1 values less than (3) data directory '/data/area1',
    ->     partition p2 values less than (6) data directory '/data/area2',
    -> partition p3 values less than (9) data directory '/data/area3'
    -> );
Query OK, 0 rows affected (0.06 sec)
           

//查看分区情况

[[email protected] ~]# tree  -C /data/
/data/
├── area1
│   └── qin
│       └── user1#P#p1.ibd
├── area2
│   └── qin
│       └── user1#P#p2.ibd
└── area3
    └── qin
        └── user1#P#p3.ibd

6 directories, 3 files
           

//查看默认数据存储位置文件

19_MySQL分表、分区为什么要分表和分区?一、分表二、分区

LIST分区

类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择。LIST分区通过使用“PARTITION BY LIST(expr)”来实现,其中“expr” 是某列值或一个基于某个列值、并返回一个整数值的表达式,然后通过“VALUES IN (value_list)”的方式来定义每个分区,其中“value_list”是一个通过逗号分隔的整数列表。 要按照属于同一个地区商店的行保存在同一个分区中的方式来分割表,可以使用下面的“CREATETABLE”语句。

mysql> create table employees (
   -> id int not null,
   -> fname varchar(22),
   -> lname varchar(22),
   -> hired date not null default '1970-01-01',
   -> separated date not null default '9999-12-31',
   -> job_code int ,
   -> store_id int)
   -> partition by list(store_id)(
   -> partition pp1 values in (3,5,6,9,17),   #store_id为(3,5,6,9,17)存放在pp1分区,以此类推
   -> partition pp2 values in  (1,2,10,11,19,20),
   -> partition pp3 values in  (4,12,13,14,18),
   -> partition pp4 values in  (7,8,15,16)
   -> );
Query OK, 0 rows affected (0.03 sec)
           

//插入数据测试

mysql> insert into  employees values(224, 'Linus', 'Torvalds', '2002-05-01', '2004-10-12', 42, 4);
Query OK, 1 row affected (0.00 sec)
//插入store_id为4的数据

mysql> select * from employees partition(pp3);
+-----+-------+----------+------------+------------+----------+----------+
| id  | fname | lname    | hired      | separated  | job_code | store_id |
+-----+-------+----------+------------+------------+----------+----------+
| 224 | Linus | Torvalds | 2002-05-01 | 2004-10-12 |       42 |        4 |
+-----+-------+----------+------------+------------+----------+----------+
1 rows in set (0.00 sec)
           

要点:如果试图插入列值不在分区值列表中的一行时,那么“INSERT”查询将失败并报错。例如,假定LIST分区的采用上面的方案,下面的插入将失败。

mysql> mysql> insert into  employees values(224, 'Linus', 'Torvalds', '2002-05-01', '2004-10-12', 42, 21);
ERROR 1526 (HY000): Table has no partition for value 21
           

这是因为“store_id”列值21不能在用于定义分区的值列表中找到。要重点注意的是,LIST分区没有类似如“VALUES LESS THAN MAXVALUE”这样的包含其他值在内的定义。将要匹配的任何值都必须在值列表中找到。

HASH分区

这种模式允许DBA通过对表的一个或多个列的Hash Key进行计算,最后通过这个Hash码不同数值对应的数据区域进行分区。 hash分区的目的是将数据均匀的分布到预先定义的各个分区中,保证各分区的数据量大致一致。在RANGE和LIST分区中,必须明确指定一个给定的列值或列值集合应该保存在哪个分区中;而在HASH分区中,MYSQL自动完成这些工作,用户所要定一个列值或者表达式,以及指定被分区的表将要被分割成的分区数量。

mysql> create table t_hash (
   -> a int,b datetime)
   -> partition by hash(year(b))
   -> partitions 4; #表示为4个分区
Query OK, 0 rows affected (0.02 sec)
           

hash的分区函数页需要返回一个整数值。partitions子句中的值是一个非负整数,不加的partitions子句的话,默认为分区数为1。

//插入数据

mysql> insert into t_hash values(1,'2020-05-21');
Query OK, 1 row affected (0.01 sec)
           

//查看数据存放在那个分区里面

mysql> select mod(year('2020-05-21'),4);
+---------------------------+
| mod(year('2020-05-21'),4) |
+---------------------------+
|                         0 |
+---------------------------+
1 row in set (0.00 sec)
           
mysql> select * from information_schema.partitions where table_schema='qin' and table_name='t_hash'\G
*************************** 1. row ***************************
               TABLE_CATALOG: def
                TABLE_SCHEMA: qin
                  TABLE_NAME: t_hash
              PARTITION_NAME: p0
           SUBPARTITION_NAME: NULL
  PARTITION_ORDINAL_POSITION: 1
SUBPARTITION_ORDINAL_POSITION: NULL
            PARTITION_METHOD: HASH
         SUBPARTITION_METHOD: NULL
        PARTITION_EXPRESSION: year(b)
     SUBPARTITION_EXPRESSION: NULL
       PARTITION_DESCRIPTION: NULL
                  TABLE_ROWS: 1
              AVG_ROW_LENGTH: 16384
                 DATA_LENGTH: 16384
             MAX_DATA_LENGTH: NULL
                INDEX_LENGTH: 0
                   DATA_FREE: 0
                 CREATE_TIME: 2021-01-22 21:30:08
                 UPDATE_TIME: 2021-01-22 21:32:34
                  CHECK_TIME: NULL
                    CHECKSUM: NULL
           PARTITION_COMMENT: 
                   NODEGROUP: default
             TABLESPACE_NAME: NULL
*************************** 2. row ***************************
.....省略
           

可以看到P2分区有一条记录。当前这个例子并不能把数据均匀的分布到各个分区,因为按照YEAR函数进行的,该值本身是离散的。如果对连续的值进行HASH分区,如自增长的主键,则可以较好地将数据平均分布。

//查看

mysql> select * from t_hash partition(p0);
+------+---------------------+
| a    | b                   |
+------+---------------------+
|    1 | 2020-05-21 00:00:00 |
+------+---------------------+
1 row in set (0.00 sec)
           

KEY分区

key分区和hash分区相似,不同在于hash分区是用户自定义函数进行分区,key分区使用mysql数据库提供的函数进行分区,NDB cluster使用MD5函数来分区,对于其他存储引擎mysql使用内部的hash函数。

mysql> create table t_key (
  -> a int ,b datetime)
  -> partition by key(b) 
  -> partitions 4;
Query OK, 0 rows affected (0.02 sec)
           

COLUMNS分区

mysql-5.5开始支持COLUMNS分区,可视为RANGE和LIST分区的进化,COLUMNS分区可以直接使用非整形数据进行分区。COLUMNS分区支持以下数据类型: 所有整形,如INT SMALLINTTINYINT BIGINT。FLOAT和DECIMAL则不支持。 日期类型,如DATE和DATETIME。其余日期类型不支持。

字符串类型,如CHAR、VARCHAR、BINARY和VARBINARY。BLOB和TEXT类型不支持。 COLUMNS可

以使用多个列进行分区。

总结

mysql分表和分区有什么区别呢

1.实现方式上

a)mysql的分表是真正的分表,一张表分成很多表后,每一个小表都是完整的一张表,都对应三个文件,一个.MYD数据文件,.MYI索引文件,.frm表结构文件。

b)分区不一样,一张大表进行分区后,他还是一张表,不会变成二张表,但是他存放数据的区块变多了。

2.数据处理上

a)分表后,数据都是存放在分表里,总表只是一个外壳,存取数据发生在一个一个的分表里面。

b)分区呢,不存在分表的概念,分区只不过把存放数据的文件分成了许多小块,分区后的表呢,还是一张表,数据处理还是由自己来完成。

3. 提高性能上

a)分表后,单表的并发能力提高了,磁盘I/O性能也提高了。并发能力为什么提高了呢,因为查寻一次所花的时间变短了,如果出现高并发的话,总表可以根据不同的查询,将并发压力分到不同

的小表里面。

b)mysql提出了分区的概念,主要是想突破磁盘I/O瓶颈,想提高磁盘的读写能力,来增加mysql性能。在这一点上,分区和分表的测重点不同,分表重点是存取数据时,如何提高mysql并发能力上;而分区呢,如何突破磁盘的读写能力,从而达到提高mysql性能的目的。

4.实现的难易度上

a)分表的方法有很多,用merge来分表,是最简单的一种方式。这种方式跟分区难易度差不多,并且对程序代码来说可以做到透明的。如果是用其他分表方式就比分区麻烦了。

b)分区实现是比较简单的,建立分区表,根建平常的表没什么区别,并且对开代码端来说是透明的。

mysql分表和分区有什么联系?

  1. 都能提高mysql的性高,在高并发状态下都有一个良好的表现。
  2. 分表和分区不矛盾,可以相互配合的,对于那些大访问量,并且表数据比较多的表,我们可以采取分表和分区结合的方式,访问量不大,但是表数据很多的表,我们可以采取分区的方式等。
  3. 分表技术是比较麻烦的,需要手动去创建子表,app服务端读写时候需要计算子表名。采用merge好一些,但也要创建子表和配置子表间的union关系。
  4. 表分区相对于分表,操作方便,不需要创建子表。