MySQL高级特性
主要内容
- 分区表
- 视图
- 外健约束
- 存储过程
- 触发器
- 绑定变量
- 字符集
- 全文索引
- 分布式(XA)事物
- 查询缓存
-
分区表
-
概念
分区表是一种粗粒度的、简易的索引策略,适用于大数据量的过滤场景。对用户来说,分区表是一个独立的逻辑表,但是底层是由多个物理子表组成。
-
使用场景
- 表的数据非常大以致无法全部放到内存中,或者只在表的最后部分有热点数据,其他均是历史数据
- 分区表的数据更容易维护。可以删除整个分区,对单独分区优化
- 分区表的数据可以存储在不同的物理设备,有效的利用多个硬件设备
- 可以使用分区表避免某些特殊的瓶颈,例如InoDB的单个索引的互斥访问、ext3文件系统的inode锁竞争等。
-
自身限制
- 一个表只能1024个分区
- 在MySQL5.1中,分区表达必须是整数,或者是返回整数的表达式,MySQL5.5中,某些场景中可以直接使用列进行分区。
- 如果分区字段中有主键或者唯一索引,那么所有主键列和唯一索引列都必须包含进来。(即:分区字段要么不包含主键或者索引列,要么包含全部主键和索引列。)
- 分区表中无法使用外健约束
-
使用策略
- 全量扫描数据, 不要任何索引
- 索引数据, 分离热点
-
分区表使用注意场景
- NULL值会使分区过滤无效
- 分区列和索引列不匹配
- 选择分区的成本可能很高
- 打开并锁住所有底层表的成本可能很高
- 维护分区的成本可能很高
- 所有分区表必须使用相同的存储引擎
- MERGE, CSV, or FEDERATED存储引擎不支持分区
-
查询优化
- 分区表最大的优点就是优化器可以根据分区函数过滤一些分区。根据粗粒度索引的优势,通过分区过滤通常可以让查询扫描更少的数据。
- 在WHERE条件中带入分区列,有时候即使看似多余也要带上,可以让优化器过滤掉无需访问的分区
-
分区表类型
- RANGE分区:基于属于一个给定连续区间的列值,把多行分配给分区。
- 当插入的记录中对应的分区键的值不在分区定义的范围中的时候,插入语句会失败
- Range分区中,分区键的值如果是NULL,将被作为一个最小值来处理。
- LIST分区:类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择。
- 如果插入的记录对应的分区键的值不在list分区指定的值中,将会插入失败。并且,list不能像range分区那样提供maxvalue。
- HASH分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL 中有效的、产生非负整数值的任何表达式。
- KEY分区:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值
- COLUMN分区是5.5开始引入的分区功能,只有RANGE COLUMN和LIST COLUMN这两种分区;支持整形、日期、字符串;RANGE和LIST的分区方式非常的相似。
- RANGE分区:基于属于一个给定连续区间的列值,把多行分配给分区。
-
分区表相关命令
查看是否支持分区表
SHOW PLUGINS ; #如果 partition 的状态是ACTIVE 表示支持分区表 partition | ACTIVE
创建RANGE分区
CREATE TABLE employees ( 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 NOT NULL, store_id INT NOT NULL ) PARTITION BY RANGE (store_id) ( PARTITION p0 VALUES LESS THAN (6), PARTITION p1 VALUES LESS THAN (11), PARTITION p2 VALUES LESS THAN (16), PARTITION p3 VALUES LESS THAN (21) );
创建LIST分区
CREATE TABLE tblist ( id INT NOT NULL, store_id INT ) PARTITION BY LIST(store_id) ( PARTITION a VALUES IN (1,5,6), PARTITION b VALUES IN (2,7,8), PARTITION c VALUES IN (3,9,10), PARTITION d VALUES IN (4,11,12) );
创建HASH分区
CREATE TABLE tblinhash ( id INT NOT NULL, hired DATE NOT NULL DEFAULT '1970-01-01' ) PARTITION BY LINEAR HASH( YEAR(hired) ) PARTITIONS 6;
创建KEY分区
CREATE TABLE tb_key ( id INT , var CHAR(32) ) PARTITION BY KEY(var) PARTITIONS 10;
range column
CREATE TABLE members ( id INT, joined DATE NOT NULL ) PARTITION BY RANGE COLUMNS(joined) ( PARTITION a VALUES LESS THAN ('1960-01-01'), PARTITION b VALUES LESS THAN ('1970-01-01'), PARTITION c VALUES LESS THAN ('1980-01-01'), PARTITION d VALUES LESS THAN ('1990-01-01'), PARTITION e VALUES LESS THAN MAXVALUE );
移除表的分区
- 使用remove移除分区是仅仅移除分区的定义,并不会删除数据和drop PARTITION不一样,后者会连同数据一起删除
ALTER TABLE tablename REMOVE PARTITIONING ;
-
demo
CREATE TABLE employees ( 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 NOT NULL, store_id INT NOT NULL ) PARTITION BY RANGE (store_id) ( PARTITION p0 VALUES LESS THAN (6), PARTITION p1 VALUES LESS THAN (11), PARTITION p2 VALUES LESS THAN (16), PARTITION p3 VALUES LESS THAN (21) ); ##查看分区情况 SELECT PARTITION_NAME, PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_DESCRIPTION,TABLE_ROWS,SUBPARTITION_NAME,SUBPARTITION_METHOD,SUBPARTITION_EXPRESSION FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA=SCHEMA() AND TABLE_NAME='employees'; ### 创建索引 alter table employees add index ix_store_id(store_id) ; alter table employees add index ix_job_code(job_code) ; ###插入数据 insert into employees(id,job_code,store_id) values(1,1001,1),(2,1002,2),(3,1003,3),(4,1004,4); ###比较效果 explain select * from employees where store_id = 3; explain select * from employees where store_id < 11; ##超过分区范围提示错误 insert into employees(id,job_code,store_id) values(1,1001,33); ##增加新分区 alter table employees add PARTITION (PARTITION p4 VALUES LESS THAN MAXVALUE); ##删除分区 alter table employees drop PARTITION p4; ##拆分分区 ALTER TABLE employees REORGANIZE PARTITION p0 INTO ( PARTITION s0 VALUES LESS THAN (3), PARTITION s1 VALUES LESS THAN (6) ); ##移除分区 ALTER TABLE employees REMOVE PARTITIONING ;
-
合并表
-
概念
合并表是一种早期的、简单的分区实现,和分区表相比有一些不同的限制,并且缺乏优化。(一种将被淘汰的技术,在未来的版本中可能被删除),合并表和merge引擎 -
和分区表的区别
- 合并表允许用户单独访问各个子表,分区表对用户来说是一个独立的逻辑表
- 分区表和优化器的结合更加紧密,
-
合并表的不足
- 合并表中的每一个子表行为和定义都是相同的,但是合并表在全局上并不接受这些条件限制
- 查询访问合并表,需要访问所有子表。而分区表能根据查询条件过滤部分分区
-
-
-
字符集和校对
-
字符集概念
基本概念- 字符(Character)是指人类语言中最小的表义符号。例如’A'、’B'等;
- 给定一系列字符,对每个字符赋予一个数值,用数值来代表对应的字符,这一数值就是字符的编码(Encoding)。例如,我们给字符’A'赋予数值0,给字符’B'赋予数值1,则0就是字符’A'的编码;
- 给定一系列字符并赋予对应的编码后,所有这些字符和编码对组成的集合就是字符集(Character Set)。例如,给定字符列表为{’A',’B'}时,{’A'=>0, ‘B’=>1}就是一个字符集;
- 字符序(Collation)是指在同一字符集内字符之间的比较规则;
- 确定字符序后,才能在一个字符集上定义什么是等价的字符,以及字符之间的大小关系;
- 每个字符序唯一对应一种字符集,但一个字符集可以对应多种字符序,其中有一个是默认字符序(Default Collation);
- MySQL中的字符序名称遵从命名惯例:以字符序对应的字符集名称开头;以_ci(表示大小写不敏感)、_cs(表示大小写敏感)或_bin(表示按编码值比较)结尾。例如:在字符序“utf8_general_ci”下,字符“a”和“A”是等价的;
-
系统变量
- character_set_server:默认的内部操作字符集
- character_set_client:客户端来源数据使用的字符集
- character_set_connection:连接层字符集
- character_set_results:查询结果字符集
- character_set_database:当前选中数据库的默认字符集
- character_set_system:系统元数据(字段名等)字符集
-
相关命令
-
查看系统支持字符集
SHOW CHARACTER SET; #查看支持字符集 SHOW COLLATION; #查看支持校验 show variables like 'collation_%';
-
设置系统字符集
SET character_set_client = utf8 ; SET character_set_connection = utf8 ; SET character_set_database = utf8 ; SET character_set_results = utf8 ; SET character_set_server = utf8 ; SET collation_connection = utf8 ; SET collation_database = utf8 ; SET collation_server = utf8 ; ######### SET NAMES 命令相当与执行了 character_set_client, character_set_connection,character_set_results SET NAMES UTF8
-
查看系统字符集
SHOW VARIABLES LIKE 'character%'; STATUS;
-
查看表的默认字符集
show create table `t1`;
-
设置表的字符集
alter table `t1` charset=gbk;
-
查看字段字符集
show full columns from t1;
-
修改字段字符集
alter table t1 change `name` `name` varchar(20) character set utf8 COLLATE utf8_bin not null default '';
-
-
字符集继承顺序
- SERVER->DB->TABLE->COLUMN
-
注意点
- character_set_client 字符集要和客户端字符集保持一致,否者多字节字符会出现乱码
- LENGTH函数和CHAR_LENGTH函数的区别LENGTH计算字符数,CHAR_LENGTH计算字节数,多字节字符这两个函数的结果会不一样
- 字符集会影响临时表的分配UTF8字符集会按最大字节数分配就算存的全是字母,也是按每个字符三个字节分配
- 影响索引的最长索引前缀(不同版本的MySQL最长索引前缀不同,单位按字节算)
create table t2 ( id int not null auto_increment primary key, `name` varchar(1025) not null default '', key(`name`) ) engine=innodb charset =utf8; ERROR 1071 (42000): Specified key was too long; max key length is 3072 bytes
-
-
绑定变量
-
使用场合
当查询语句的解析和执行计划生成消耗了主要的时间,那么绑定变量可以在一定程度上解决问题,因为只需要解析一次,对于大量重复类型的查询语句,
性能会有很大的提高。 -
高效的原因
- 在服务端只需要解析一次
- 在服务端的某些优化器的工作只需要执行一次,因为会缓存一部分执行计划。
- 一二进制的方式只发送参数和句柄, 比起每次都发送ASCII码文本效率高,一个二进制的日期只需要三个字节,但如果是ASCII码则需要十个字节
- 仅仅是参数----而不是整个查询语句----需要发送到服务器,所以网络开销会更小。
- MySQL在存储参数的时候,直接将其存放在缓存中,不再需要在内存中多次复制
-
绑定变量安全性相对较高(任何时候都不要相信用户的数据,即使是绑定变量)
- 无需在应用程序中处理转义,操作更简便,大大减少了SQL注入和攻击的风险
-
绑定变量的优化
-
准备阶段
服务器解析SQL语句,移除不可能的条件,并且重写子查询 -
在第一次执行的时候
如果可能, 服务器先简化嵌套循环的关联,并将外关联转化成内关联 -
在每次SQL语句执行时
- 过滤分区
- 如果可能的话, 尽量移除COUNT(),MIN(),MAX()
- 移除常量表达式
- 检测常量表
- 做必要的等值传播
- 分析和优化ref、 range和索引优化等访问数据的方式
- 优化关联顺序
-
绑定变量的限制
- 绑定变量是会话级别的,所以连接之间不能共用绑定句柄,一旦连接断开,原来的句柄就不能使用(连接池和持久连接一定程度上缓解这个问题)
- 在MySQL5.1之前不能使用查询缓存
- 如果只是执行一次SQL,那么使用绑定变量的方式无疑比直接执行多了一次额外的准备阶段消耗,还需要一次额外的网络开销
- 如果总是忘记释放绑定变量资源,服务器很容易发生资源泄漏,绑定变量的SQL总数是一个全局限制
-
-
相关命令
SET @sql := 'select * from `t1` where `name` = ?'; PREPARE stmt_fetch FROM @sql; SET @name :='a'; EXECUTE stmt_fetch USING @name;
-