mysql面试--分区

2019-11-12  本文已影响0人  洁癖汪

Mysql基础篇--分区类型

原创: 洁癖汪 洁癖是一只狗 昨天

分区是按照一定规则把一个表分解成多个更小的表,更容易管理的部分,当访问数据库应用而言,逻辑上是一个表或一个索引,实际上是可以有数个物理对象组成,每个分区都是一个独立的对象,可以独自处理作为表的一部分进行数据处理

分区的优点

和单个磁盘或文件相比,可以储存更多数据

优化查询,当where子句中包含分区条件的时候,可以扫描一个或几个分区提高查询条件,同时处理sum() ,count()聚合函数的查询可以容易在每个分区进行处理,最后汇总得到结果

对于不用的数据即将过期的数据,可以删除有关数据的某个分区

多个磁盘分散数据的查询,获得更大的查询的吞吐量

mysql 分区类型

RANGE分区:基于一个给定连续区间分为,把数据分配到不通风分区

LIST :和RANGE类似,是基于给定枚举值,把数据分配到不同的分区

HASH:基于分区的个数,把数据分配到不同的分区

KEY:和HASH类似

无论哪种分区类型,要么分区表不包含主键或唯一键,要么分区表的主键或唯一键包含分区键,并且分区的名称是区分大小写的

RANGE分区

按照range分区表示利用取值范围将数据分成分区,区间要连续且不能重叠,使用values  less than 进行分区定义,如下

mysql> create table emp(

->id intnotnull,

->name varchar(10),

->store_id intnotnull

-> )

-> partitionbyrange (store_id)(

->partition p0 values less than (10),

->partition p1 values less than (20),

->partition p2 values less than (30)

-> );

Query OK,0rows affected (0.07sec)

mysql> insert into emp values (1,'jiepi',1),(2,'jiepi2',2),(3,'jiepi3',3);

Query OK,3rows affected (0.01sec)

Records:3Duplicates:0Warnings:0

我们把1-9存储到p0分区,10-19存储到p1,一次类推,但是当store_id大于30的时候会发生什么呢?

mysql>insertintoempvalues(50,'jiepi50',50);

ERROR1526(HY000): Table has no partitionforvalue50

我们发现他是报错的,因此我可以使用values less than maxvalue语句添加分区,maxvalue 代表的是最大的可能的整数值,当服务器不知道把数据放到哪个分区的时候,我们就把这个数据放到这个分区。

mysql>alter table empaddpartition(partition p3 values less than maxvalue);

Query OK,0rowsaffected(0.08sec)

Records: 0  Duplicates: 0  Warnings: 0

mysql> insertintoempvalues(50,'jiepi50',50)

;

Query OK,1rowaffected(0.01sec)

分区使用的场景

当需要删除过期的数据,只要简单的执行 alter table emp drop partition po来删除p0中的数据,对于上百万的记录表来说,删除一个分区的数据,往往比使用delete 有效的多.

经常运行包含分区键的查询,mysql可以快速的明确只有某一个或者某些分区需要扫面,因为并不是所有分区都要相关的数据,例如我们要查询store_id大于等25的数据,可能只要扫描p2分区。

LIST分区

list分区建立在离散的值列表告诉数据库应该放到个分区,list分区很多方面是和range分区相似,区别在于list分区从属于一个枚举列表的值的集合,range是一个连续区间的集合,

list分区使用 partition  by list(expr) 实现,expr 是某列值,或一个基于某列值得表达式,然后通过 values in (value_list) 方式定义分区,始终value_list是用逗号分隔的整数列表,他也不必按照上面顺序声明。

mysql> create table expenses(

->id intnotnull,

->category int,

->amout decimal (10,3)

-> )partitionbylist (category) (

->partition p0 valuesin(3,5),

->partition p1 valuesin(1,10),

->partition p2 valuesin(4,9),

->partition p3 valuesin(2),

->partition p4 valuesin(6)

-> );

Query OK,0rows affected (0.15sec)

mysql> insert into expenses values (1,1,12.9),(2,2,12.8);

Query OK,2rows affected (0.01sec)

Records:2Duplicates:0Warnings:0

注意的是list分区没有像range分区有values less than maxvalue,如果数据在list分区中不到会报错,所以定分区的时候必须包含多有可能的值。

mysql>insertintoexpensesvalues(1,11,12.9),(2,2,12.8);

ERROR1526(HY000): Table has no partitionforvalue11

columns分区

Columns分区是在mysql5.5引进的分区类型,上面的分区是都是基于整形分区,是为了解决之前版本要进行函数或者表达式转换成整形,他分为 list columns  和 range columns ,他们支持 整形 日期 ,字符串,

整形:tinyint smallint ,mediumint ,int ,bigint ,其他类型不支持

日期:data ,datatime

字符串:char ,varcahr ,binary ,varbinary 不支持 text和blob 类型做分区键

除了添加了类型支持,并且还支持多列分区.

mysql> CREATE TABLE m_num(

->  a INT,

->  b INT

-> )

-> PARTITION BY RANGE COLUMNS(a,b)(

->     PARTITION p0 VALUES LESS THAN (0,10),

->     PARTITION p1 VALUES LESS THAN (10,20),

->     PARTITION p2 VALUES LESS THAN (10,MAXVALUE),

->     PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE)

-> );

Query OK,0rows affected (0.16sec)

他的分区规则优点稍微不一样,他是按照字段组的比较

mysql> insert into m_num values (1,10);

Query OK,1row affected (0.01sec)

mysql> select (1,10)<(10,10) from m_num;

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

| (1,10)<(10,10) |

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

|              1 |

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

1rowinset (0.00sec)

mysql> select

-> partition_name part ,

-> partition_expression expr,

-> partition_description descr,

-> table_rows

-> from

-> information_schema.partitions

-> where table_schema=schema()

->andtable_name='m_num';

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

| part |expr| descr             |table_rows|

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

|

p0| `a`,`b` |0,10|          0 |

| p1   |`a`,`b`| 10,20             |1|

|

p2| `a`,`b` |10,MAXVALUE|          0 |

| p3   |`a`,`b`| MAXVALUE,MAXVALUE |0|

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

4 rowsinset (0.01 sec)

他的比较原则如下

我们在看一个例子

mysql> insert into m_num values (10,25);

Query OK,1row affected (0.01sec)

mysql> select  partition_name part , partition_expression expr, partition_description descr, table_rows from information_schema.partitions  where table_schema=schema()andtable_name='m_num';

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

| part |expr| descr             |table_rows|

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

|

p0| `a`,`b` |0,10|          0 |

| p1   |`a`,`b`| 10,20             |1|

|

p2| `a`,`b` |10,MAXVALUE|          1 |

| p3   |`a`,`b`| MAXVALUE,MAXVALUE |0|

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

4 rowsinset (0.00 sec)

Hash分区

hash分区主要用来分散热点读,确保数据在预先知道分区数目,尽可能的平均分布,在数据进行分区的时候,使用一个散列函数,计算数据到那个分区.

hash分区分为两类 常规hash和线性hash分区,常规分区是使用模运算计算,而线性hash是一个线性的2的幂运算规则。

我们使用 partition by hash (expr) partitions num实现

mysql> create table emp_hash(

->id intnotnull,

->name varchar(20),

->store_id intnotnull

-> )

-> partitionbyhash (store_id) partitions4;

Query OK,0rows affected (0.14sec)

hash分区的数据是按照N=MOD(expr,num)计算的,比如我插入一个store_id=234的数据,那么他存储的数据计算在N=MOD(234,4)=2 分区。

mysql> insert into emp_hash values (1,'jiepi',234);

Query OK, 1 row affected (0.00 sec)

mysql> explain partitions select *  from  emp_hash where store_id=234 \G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: emp_hash

partitions: p2

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 2

Extra: Using where

1 row in set (0.00 sec)

expr可以是一个表达式也可以是一某列的值,当进行插入删除更新操作的时候,这个表达式都要重新计算一次,所以在表达式比较复杂的时候,还是很消耗性能的,建议不要使用这种分区方式。

Hash分区在增加分区也是一个比较麻烦的事情,因为要把以前的数据重新计算分配到新的分区的需求,因此我们还有一种线性Hash分区,分区函数是一个线性的2的幂的运算规则。比常规hash分区多了一个linear.

mysql> create table emp_hash_linear(

->id intnotnull,

->name varchar(20),

->store_id intnotnull

-> )

-> partitionbylinear hash (store_id) partitions4;

他的就算方式如下公式

首先,找到下一个大于等于num的2的幂,这个值为V,V通过下面公式计算,V=Power(2,Ceiling(Log(2,num)))

其次,设置N=F(column_list)&(V-1),

当N>=num,使用V=Ceiling(V/2),设置N=N&(V-1),N就是分区的位置,否则,上一步计算的N就是分区的位置。

mysql> insert into  emp_hash_linear values (1,'jiepi',234);

Query OK, 1 row affected (0.01 sec)

mysql> explain partitions select * from emp_hash_linear where store_id=234 \G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: emp_hash_linear

partitions: p2

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 2

Extra: Using where

1 row in set (0.00 sec)

Key分区

KEY分区和HASH分区类似,使用hash进行分区,只不过Hash分区支持自定义表达式,而key不支持,使用的是mysql服务器提供的HASH函数,同时hash只支持整数分区,而key分区除了Text和BLOB其他类型都支持,

mysql> create table emp_key(

->id intnotnull,

->name varchar(20),

->job varchar(20)

-> )

-> partitionbykey (job) partitions4

-> ;

Query OK,0rows affected (0.11sec)

mysql> create table emp_key_primary(

->id intnotnull,

->name varchar(20),

->job varchar(20),

->primary key (id)

-> )

-> partitionbykey () partitions4;

Query OK,0rows affected (0.10sec)

mysql> create table emp_key_unique(

->id intnotnull,

->name varchar(20),

->job varchar(20),

->unique key (id)

-> )

-> partitionbykey () partitions4;

Query OK,0rows affected (0.11sec)

需要注意的是,我们可以不指定分区间,默认会选取主键,其次是唯一键作为分区间,如果没有主键和唯一键,就不能不指定分区键了。key分区也是使用线性2的幂计算出数据在哪个分区。当我们处理大量记录时,能够有效的分散热点。

Mysql分区处理NULL值的方式

mysql不禁止在分区键上使用null,mysql是把null值按照最小值,或者零值进行处理,range分区是按照最小值处理,list分区中,null值必须出现在枚举中,否则不被接受,Hash和key分区,把null按照零值处理。

分区管理

添加,删除,重新定义分区处理上,range和list 语法基本一直,我们来来看一下

range删除分区

先创建range分区,再插入数据,查看数据在p2,在使用

alter table range_test drop partition p2;

mysql> create table range_test(

->  id intnotnull,

->  separated datenotnull default'9999-12-31',

->  store_id intnotnull

-> )

-> partition by range(year(separated))(

->   partition p0values less than (1995),

->   partition p1 values less than (2000),

->   partition p2 values less than (2005),

->   partition p3 values less than (2015)

-> );

Query OK,0rows affected (0.10sec)

mysql> insert into range_test values (1,'2002-12-01',1);

Query OK,1row affected (0.00sec)

mysql> select  partition_name part ,

->  partition_expression expr,

->  partition_description descr,

->  table_rows from

->  information_schema.partitions

->   where table_schema=schema()

->andtable_name='range_test';

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

| part |expr| descr |table_rows|

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

|

p0| year(separated) |1995|          0 |

| p1   |year(separated)| 2000  |0|

|

p2| year(separated) |2005|          1 |

| p3   |year(separated)| 2015  |0|

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

4 rowsinset (0.00 sec)

mysql> alter table range_test drop partition p2;

Query OK, 0 rows affected (0.05 sec)

Records: 0  Duplicates: 0  Warnings: 0

mysql> select  partition_name part ,

->  partition_expression expr,

->  partition_description descr,

->  table_rows from

->  information_schema.partitions

->   where table_schema=schema()

->andtable_name='range_test';

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

|

part| expr            |descr| table_rows |

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

| p0   |year(separated)| 1995  |0|

|

p1| year(separated) |2000|          0 |

| p3   |year(separated)| 2015  |0|

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

3 rowsinset (0.00 sec)

2.range添加分区

 注意的是range分区只能在最大端增加分区,否则会报错

mysql> alter table range_test add partition ( partition p4 values less than (2030));

Query OK,0rows affected (0.07sec)

Records:0Duplicates:0Warnings:0

mysql> show create table range_test\G

***************************1.row ***************************

Table: range_test

Create Table: CREATE TABLE `range_test` (

`id`int(11)NOTNULL,

`separated`dateNOTNULLDEFAULT'9999-12-31',

`store_id`int(11)NOTNULL

) ENGINE=InnoDBDEFAULTCHARSET=latin1

/*!50100PARTITION BY RANGE (year(separated))

(PARTITION p0 VALUES LESS THAN (1995) ENGINE = InnoDB,

PARTITION p1 VALUES LESS THAN (2000) ENGINE = InnoDB,

PARTITION p3 VALUES LESS THAN (2015) ENGINE = InnoDB,

PARTITION p4 VALUES LESS THAN (2030) ENGINE = InnoDB) */

1rowinset(0.00sec)

mysql> alter table range_test add partition ( partition p5 values less than (2025));

ERROR1493(HY000): VALUES LESS THAN value must be strictly increasingforeachpartition

3.range重新定义分区

mysql> show create table range_test\G;

*************************** 1. row ***************************

Table: range_test

Create Table: CREATE TABLE`range_test`(

`id`int(11) NOT NULL,

`separated`date NOT NULL DEFAULT '9999-12-31',

`store_id`int(11) NOT NULL

) ENGINE=InnoDB DEFAULT CHARSET=latin1

/*!50100 PARTITION BY RANGE (year(separated))

(PARTITION p0 VALUES LESS THAN (1995) ENGINE = InnoDB,

PARTITION p1 VALUES LESS THAN (2000) ENGINE = InnoDB,

PARTITION p3 VALUES LESS THAN (2015) ENGINE = InnoDB,

PARTITION p4 VALUES LESS THAN (2030) ENGINE = InnoDB) */

1 row in set (0.00 sec)

ERROR:

No query specified

mysql> alter table range_test reorganize partition p3 into(

-> partition p2 values less than (2005),

-> partition p3 values less than (2015));

Query OK, 0 rows affected (0.14 sec)

Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table range_test\G

*************************** 1. row ***************************

Table: range_test

Create Table: CREATE TABLE`range_test`(

`id`int(11) NOT NULL,

`separated`date NOT NULL DEFAULT '9999-12-31',

`store_id`int(11) NOT NULL

) ENGINE=InnoDB DEFAULT CHARSET=latin1

/*!50100 PARTITION BY RANGE (year(separated))

(PARTITION p0 VALUES LESS THAN (1995) ENGINE = InnoDB,

PARTITION p1 VALUES LESS THAN (2000) ENGINE = InnoDB,

PARTITION p2 VALUES LESS THAN (2005) ENGINE = InnoDB,

PARTITION p3 VALUES LESS THAN (2015) ENGINE = InnoDB,

PARTITION p4 VALUES LESS THAN (2030) ENGINE = InnoDB) */

1 row in set (0.00 sec)

4.list分区重新定义分区

mysql> show create table list_test\G

***************************1.row ***************************

Table: list_test

Create Table: CREATE TABLE `list_test` (

`id`int(11)NOTNULL,

`separated`dateNOTNULLDEFAULT'9999-12-31',

`store_id`int(11)NOTNULL

) ENGINE=InnoDBDEFAULTCHARSET=latin1

/*!50100PARTITION BY LIST (store_id)

(PARTITION p0 VALUESIN(3,5) ENGINE = InnoDB,

PARTITION p1 VALUESIN(1,2) ENGINE = InnoDB,

PARTITION p2 VALUESIN(4,7) ENGINE = InnoDB,

PARTITION p3 VALUESIN(6) ENGINE = InnoDB) */

1rowinset(0.00sec)

mysql> alter table list_test add partition (partition p4 valuesin(8));

Query OK,0rows affected (0.06sec)

Records:0Duplicates:0Warnings:0

mysql> show create table list_test \G

***************************1.row ***************************

Table: list_test

Create Table: CREATE TABLE `list_test` (

`id`int(11)NOTNULL,

`separated`dateNOTNULLDEFAULT'9999-12-31',

`store_id`int(11)NOTNULL

) ENGINE=InnoDBDEFAULTCHARSET=latin1

/*!50100PARTITION BY LIST (store_id)

(PARTITION p0 VALUESIN(3,5) ENGINE = InnoDB,

PARTITION p1 VALUESIN(1,2) ENGINE = InnoDB,

PARTITION p2 VALUESIN(4,7) ENGINE = InnoDB,

PARTITION p3 VALUESIN(6) ENGINE = InnoDB,

PARTITION p4 VALUESIN(8) ENGINE = InnoDB) */

1rowinset(0.00sec)

mysql> alter table list_test reorganize partition p2 ,p3,p4 into (

-> partition p2 valuesin(4),

-> partition p3 valuesin(6),

-> partition p4 valuesin(7,8));

Query OK,0rows affected (0.19sec)

Records:0Duplicates:0Warnings:0

mysql> show create table list_test\G

***************************1.row ***************************

Table: list_test

Create Table: CREATE TABLE `list_test` (

`id`int(11)NOTNULL,

`separated`dateNOTNULLDEFAULT'9999-12-31',

`store_id`int(11)NOTNULL

) ENGINE=InnoDBDEFAULTCHARSET=latin1

/*!50100PARTITION BY LIST (store_id)

(PARTITION p0 VALUESIN(3,5) ENGINE = InnoDB,

PARTITION p1 VALUESIN(1,2) ENGINE = InnoDB,

PARTITION p2 VALUESIN(4) ENGINE = InnoDB,

PARTITION p3 VALUESIN(6) ENGINE = InnoDB,

PARTITION p4 VALUESIN(7,8) ENGINE = InnoDB) */

1rowinset(0.01sec)

HASH和KEY分区管理

1.减少Hash分区

mysql>  create table hash_test(

->   idintnotnull,

->   name varchar(10),

->   store_idintnotnull

->   )

->   partition by hash(store_id) partitions4;

Query OK,0rows affected (0.09sec)

mysql> alter table hash_test coalesce partition2;

Query OK,0rows affected (0.16sec)

Records:0Duplicates:0Warnings:0

mysql> show create table hash_test \G

***************************1.row ***************************

Table: hash_test

Create Table: CREATE TABLE `hash_test` (

`id`int(11)NOTNULL,

`name` varchar(10)DEFAULTNULL,

`store_id`int(11)NOTNULL

) ENGINE=InnoDBDEFAULTCHARSET=latin1

/*!50100PARTITION BY HASH (store_id)

PARTITIONS2*/

1rowinset(0.00sec)

2.增加hash分区

mysql> alter table hash_test add partition partitions 8;

Query OK, 0 rows affected (0.32 sec)

Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table hash_test\G

*************************** 1. row ***************************

Table: hash_test

Create Table: CREATE TABLE`hash_test`(

`id`int(11) NOT NULL,

`name`varchar(10) DEFAULT NULL,

`store_id`int(11) NOT NULL

) ENGINE=InnoDB DEFAULT CHARSET=latin1

/*!50100 PARTITION BY HASH (store_id)

PARTITIONS 10 */

1 row in set (0.00 sec)

注意alter table add partition partitions n 新增hash分区或key分区是在原表上再添加n个分区,不是增加到n个分区。

希望此文对大家有所帮助,也希望大家持续关注转载。关注公众号获取相关资料请回复:typescript,springcloud,springboot,nodejs,nginx,mq,javaweb,java并发实战,java并发高级进阶,实战java并发,极客时间dubbo,kafka,java面试题,ES,zookeeper,java入门到精通,区块链,java优质视频,大数据,kotlin,瞬间之美,HTML与CSS,深入体验java开发,web开发CSS系列,javaweb开发详解,springmvc,java并发编程,spring源码,python,go,redis,docker,即获取相关资料。

扫码关注

微信扫一扫

关注该公众号

上一篇下一篇

猜你喜欢

热点阅读