MySQL高级特性
1. 分区表
对于用户来说,分区表是一个独立的逻辑表,但是底层由多个物理子表组成。实现分区的代码实际上是对一组底层表的句柄对象的封装
以下场景分区表可以起到非常大的作用:
1. 表非常大以至于无法全部存放于内存中,或者最后的是热点数据其他是历史数据
2. 分区表的数据更容易维护。
3. 分区表的数据可以分布在不同的物理设备上,从而高效地利用多个硬件设备。
4. 使用分区表避免某些特殊的瓶颈。
5. 备份和恢复独立的分区
分区表的限制:
1. 一个表最多只能有1024个分区
2. 分区表必须是整数,或者是返回整数的表达式
3. 如果分区字段中有主键或者唯一索引的列,那么所有主键列和唯一索引列都必须包含进来
4. 分区表中无法使用外键约束
1.1 分区表的原理
存储引擎管理分区的各个底层表和管理普通标一样(所有底层表都必须使用相同的存储引擎),分区表的索引只是在各个底层表上各自加上一个完全相同的索引
分区表上的操作按照下面的操作逻辑进行:
SELECT查询:
当查询一个分区表的时候,分区表先代开并锁住所有的底层表,优化器先判断是否可以过滤部分分区,然后再调用对应的存储引擎接口访问各个分区的数据。
INSERT操作:
当写入一条记录时,分区表先打开并锁住所有的底层表,然后确定哪个分区接收这条记录,再将记录写入对应底层表。
DELETE操作:
当删除一条记录时,分区表先打开并锁住所有的底层表,然后确定数据对应的分区,最后对相应底层进行删除操作。
UPDATE操作:
当更新一条记录时,分区表先打开并锁住所有的底层表,MySQL先确定需要更新的记录在哪个分区,然后取出数据并更新,再判断更新后的数据应该在哪个分区,最后对底层表进行写入操作,并对原数据所在的底层表进行删除操作。
虽然每个操作都会“先打开并锁住所有的底层表”,但这并不是说分区表在处理过程中是锁住全表的。如果存储引擎能够自己实现行级锁,例如InnoDB,则会在分区层释放对应表锁。
1.2 分区表的类型
1. 根据时间间隔进行分区
2. 根据键值、哈希和列表进行分区,减少InnoDB的互斥量竞争
3. 使用数学模函数进行分区,然后将数据轮询放入不同的分区
1.3 如何使用分区表
为了保证大数据量的可扩展性,一般有下面两种策略:
1. 全量的数据扫描,不要任何索引
警告:必须将查询需要扫描的分区个数限制在一个很小的数量
2. 索引数据,并分类热点
1.4 什么情况下会出问题
1. NULL值会使分区过滤无效
2. 分区列和索引列不匹配
3. 选择分区的成本很高
4. 打开并所著所有底层表的成本可能很高
5. 维护分区的成本可能很高
6. 分区实现中的一些限制:
7. 所有分区必须使用相同的存储引擎
8. 分区函数中可以使用的函数和表达式也有一些限制
某些存储引擎不支持分区
对于MyISAM的分区表,不能再使用LOAD INDEX INTO CACHE操作
对于MyISAM表,使用分区表时需要打开更多的文件描述符
1.5 查询优化
对于访问分区表来说,很重要的一点就是要在WHERE条件中带入分区列,有时候即使看似多余也要带上,这样就可以让优化器能过过滤掉无须访问的分区。如果没有这些条件,就会访问所有分区。
一个很重要的原则是:即便在创建分区时可以使用表达式,但在查询时却只能根据列来过滤分区。
1.6 合并表
2 视图
视图本身是一个虚拟表,不存放任何数据
在使用SQL语句访问视图的时候,它返回的数据是MySQL从其他表中生成的。视图和表是在同一个命名空间。不过视图和表也有不同。例如,不能对视图创建触发器,也不能使用DROP TABLE命令删除视图
定义视图的两种算法:合并算法和临时表算法。应尽可能使用合并算法;如果视图中包含GROUP BY、DISTINCT、任何聚合函数、UNION、子查询等,只要无法在原表记录和视图记录建立一一映射的场景中,MySQL都将使用临时表算法来实现视图。
mysql> EXPLAIN SELECT * FROM
结果为DEIRIVED 即为采用临时表算法实现的
2.1 可更新视图
☞ 可以通过更新这个视图来更新视图涉及的相关表
限制:
1. 如果视图定义中包含了GROUP BY、UNION、聚合函数,以及其他一些特殊情况,就 不能被更新了
2. 被更新的列必须是来自一个表中
3. 所有使用临时表算法实现的视图都无法被更新
2.2 视图对性能的影响
某些情况下视图也可以帮助提升性能,而且视图还可以与其他提升性能的凡是叠加使用
可以使用视图实现基于列的权限控制,却不需要真正的在系统中创建列权限,因此没有额外的开销
即使是合并算法实现的视图也会有额外的开销,而且视图的性能很难预测。
MySQL优化器中 ,视图的代码执行路径也完全不同,这部分代码测试还不够全面,可能会有一些隐藏缺陷和问题
2.3 视图的限制
MySQL还不支持物化视图
不支持在视图中创建索引
MySQL不会保存视图定义的原始SQL语句
3 外键约束
使用外键是有成本的(外键通常都要求每次在修改数据时都要在另外一张表中多执行一次查找操作)
某些场景下,外键会提升一些性能
外键约束使得查询需要额外访问一些别的表,这也意味着需要额外的锁
如果只是使用外键做约束,那通常在应用程序里实现该约束会更好
4 在MySQL内部存储代码
MySQL允许通过触发器、存储过程、函数、定时任务的形式来存储代码
使用存储代码的优点:
1. 在服务器内部运行,离数据最近,节省宽带和网络延时
2. 是一种代码重用,方便统一业务规则
3. 简化代码的维护和版本更新
4. 帮助提升安全,比如提供更细粒度的权限控制
5. 服务器端可以缓存存储过程的执行计划,这对于需要反复调用的过程,会大大降低消耗
6. 备份、维护都可以在服务器端完成,没什么外部依赖,维护简单
7. 可以在应用开发和数据开发人员之间更好地分工。
存储代码的缺点:
1. MySQL本身没有提供很好的开发和调试工具,所以编写MySQL的存储代码比其他的数据库要更难些
2. 相比应用程序,存储代码效率要稍微差些。能使用的函数有限,很难实现复杂的逻辑
3. 可能会给应用程序代码的部署带来额外的复杂性
4. 因为存储程序都部署在服务器内,所以可能有安全隐患
5. 存储过程会给数据库服务器增加额外的压力,而数据库服务器的扩展性相比应用服务器要差很多
6. MySQL并没有什么选项可以控制存储程序的资源消耗,所以在存储过程的一个小错误,可能直接把服务器拖死。
7. 存储代码在MySQL中的实现有很多限制,功能非常弱
8. 调试MySQL的存储过程时一种很困难的事情
9. 和基于语句的二进制日志复制合作得并不好
4.1 存储过程和函数
限制:
1. 优化器无法使用DETERMINISTRIC来优化单个查询中多次调用存储函数的情况
2. 优化器无法评估存储函数的成本
3. 每个连接都有独立的存储过程的执行计划缓存
4. 存储程序和复制是一对诡异组合
4.2 触发器
限制:
1. 可以掩盖服务器背后的工作
2. 触发器的问题很难排查
3. 可能导致死锁和锁等待
4.3 事件
4.4 在存储程序中保留注释
5 游标
6 绑定变量
优点:
1. 在服务端只需要解析一次SQL语句
2. 在服务端某些优化器的工作只需要执行一次
3. 绑定变量的形式可以分块传输,而无须一次性传输,减少网络开销和数据转化开销
4. 仅仅是参数需要发送到服务器端,所以网络开销会更小
5. 存储参数时,直接将其存放到缓存中,不需要在内存中多次复制
6. 相对更安全,大大减少了SQL注入和攻击的危险
6.1 绑定变量的优化
在准备阶段:
服务器解析SQL语句,移除不可能的条件,并且重写子查询
在第一次执行的时候:
如果可能的话,服务器先简化嵌套循环的关联,并将关联转化成内关联
在每次SQL语句执行时:
过滤分区
如果可能的话,尽量移除COUNT() MIN() MAX()
移除常数表达式
检测常量表
做必要的等值传播
分析和优化ref、range和索引优化等访问数据的方法
优化关联顺序
6.2 SQL接口的绑定变量
6.3 绑定变量的限制
7 用户自定义函数
8 插件
存储过程插件
后台插件
全文解析插件
审计插件
认证插件
9 字符集和校对
9.1 MySQL如何使用字符集
创建对象时的默认设置
MySQL服务器有默认的字符集和校对规则,每个数据库也有自己的默认值,每个表也有自己的默认值,这是一个逐层继承的默认设置,最终最靠底层的默认设置将影响你创建的对象。
服务器和客户端通信时的设置
服务器总是假设客户端是按好character_set_client设置的字符来传输数据和SQL语句的
当服务器收到客户端的SQL语句时,它先将其转换成字符集character_set_connection。它还使用这个设置来决定如何将数据转换成字符串。
当服务器端返回数据或者错误信息给客户端时,它会将其转换成character_set_result。
9.2 选择字符集和校对规则
9.3 字符集和校对规则如何影响查询
10 全文索引
希望通过关键字的匹配来进行查询过滤,那么就需要基于相似度的查询,而不是原来的精确数值比较。全文索引就是为这种场景设计的。
全文索引可以支持各种字符内容的搜索(包括CHAR、VARCHAR和TEXT类型),也支持自然语言搜索和布尔搜索。
10.1 自然语言的全文索引
自然语言搜索引擎将计算每一个文档对象和查询的相关度。相关度是基于匹配的关键词个数,以及关键词在文档中出现的次数。
在整个索引中出现次数越少的词语,匹配时的相关度就越高。相反,非常常见的单词将不会搜索,即使不在停用词列表中出现。如果一个词语在超过50%的记录中都出现了,那么自然语言将不会搜索这类词语。
全文索引的语法和普通查询略有不同。可以根据WHERE子句中的MATCH AGAINST来区分查询是否使用全文索引。
10.2 布尔全文索引
在布尔搜索中,用户可以在查询中自定义某个被搜索的词语的相关新。布尔搜索通过停用词列表过滤掉那些“噪声”词,除此以外,布尔搜索还要求搜索关键词长度必须大于ft_min_word_len,同时小于ft_max_word_len,搜索的返回结果是未经排序的。
当编写一个布尔搜索查询时,可以通过一些前缀修饰符来定制搜索。
10.3 MySQL5.1 中全文索引的变化
10.4 全文索引的限制和替代方案
限制:
1. MySQL全文索引只有一种判断相关性的方法:词频
2. MySQL全文索引只有全部在内存中的时候,性能才非常好
3. 全文索引还会影响查询优化器的工作
10.5 全文索引的配置和优化
1. 需要经常使用OPTIMIZE TABLE来减少碎片
2. 需要保证索引缓存足够大,从而保证所有的全文索引都能缓存在内存中
3. 提供一个好的停用词表
4. 忽略一些太短的单词也可以提升全文索引的效率
5. 当向一个有全文索引的表中导入大量数据的时候,最好先通过命令DISABLE KEYS来禁用全文索引,然后在导入结束后使用ENABLE KEYS来建立全文索引
11 分布式(XA)事务
11.1 内部XA事务
11.2 外部XA事务
12 查询缓存
MySQL拥有一种不同的缓存类型:缓存完整的SELECT查询结果,也就是“查询缓存”。
MySQL查询缓存保存查询返回的完整结果。当查询命中该缓存,MySQL会立即返回结果,跳过解析、优化和执行阶段。
查询缓存系统会跟踪查询中涉及的每个表,如果这些表发生变化,那么和这个表相关的所有缓存数据都将失效。但是这种机制其实实现代价很小
查询缓存对应用程序是完全透明的。应用程序无须关系MySQL是通过查询缓存返回的结果还是实际执行返回的记过。事实上,这两种执行的结果是完全相同的。
12.1 MySQL如何判断缓存命中
MySQL判断缓存命中的方法很简单:缓存存放在一个引用表中,通过一个哈希值引用,这个哈希值包含了如下因素,即查询本身、当前要查询的数据库、客户端协议的版本等
任何字符上的不同、例如空格、注释——任何的不同——都会导致缓存的不命中。
当查询语句中有一些不确定的数据时,则不会缓存。例如包含函数NOW()或者CURRENT_DATE()的查询不会被缓存。事实上,如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、mysql库中的系统表、或者任何包含列级别权限的表,都不会被缓存。
在事务提交之前,这个表的相关查询也是无法被缓存的。
12.2 查询缓存如何使用内存
查询缓存是完全存储在内存中的。
MySQL用于查询缓存的内存被分成一个个的数据块,数据块是变长的。每一个数据块中,存储了自己的类型、大小和存储的数据本身,还外加指向前一个和后一个数据块的指针。
当有查询结果需要缓存的时候,MySQL先从大的空间块中申请一个数据块用于存储结果,这个数据块需要大于参数query_cache_min_res_unit的配置,即使查询结果远远小于此。
12.3 什么情况下查询缓存能发挥作用
理论上,可以通过观察打开或者关闭缓存时候的系统效率来决定是否需要开启查询缓存
对于那些需要消耗大量资源的查询通常都是非常适合缓存的。例如一些汇总查询。这些都是执行消耗大,但是返回的结果集却很小,非常适合缓存。
这里推荐查看“命中和写入”的比率来查看缓存是否对系统有好处。根据经验来看,当这个壁纸大于3:1时通常查询缓存是有效的,不过这个比率最好能够达到10:1。如果你的应用没有达到这个比率,建议禁用查询缓存。
如果查询缓存空间长时间都有剩余,那么建议缩小;如果经常由于空间不足而导致查询缓存失效,那么则需要增大查询缓存。
缓存未命中的可能原因:
查询语句无法被缓冲,可能是因为查询中包含一个不确定的函数,或者查询结果太大而无法缓存
MySQL从未处理这个查询,所以结果也从不层被缓存过
还有一种情况是虽然之前缓存了查询结果,但是由于查询缓存的内存用完了,MySQL需要将某些缓存“逐出”,或者由于数据包被修改导致缓存失效
12.4 如何配置和维护查询缓存
下面是一些参数配置:
query_cache_type:
是否打开查询缓存。可设置成OFF、ON或DEMAND。DEMAND表示只有在查询语句中明确写明SQL_CACHE的语句才放入查询缓存。这个变量可以是会话级别的也可以使全局级别的。
query_cache_size
查询缓存使用的内存空间,单位是字节。这个值必须是1024的整数倍
query_cache_min_res_unit
在查询缓存中分配内存块时的最小单位。
query_cache_limit
MySQL能够缓存的最大查询结果。如果查询结果大于这个值,则不会被缓存。如果超出,MySQL则增加状态值Qcache_not_cached,并将结果结果从查询缓存中删除。
query_cache_wlock_invalidate
如果某个数据表被其他的连接锁住,是否仍然从查询缓存中换回结果,这个参数默认是OFF。
维护查询缓存
1. 减少碎片
2. 提高查询缓存的使用率
12.5 InnoDB和查询缓存
12.6 通用查询缓存优化
库表结构的设计、查询语句、应用程序设计都可能会影响到查询缓存的效率
1. 用多个小表代替一个大表对查询缓存有好处
2. 批量写入时只需要做一次缓存失效,所以相比单条写入效率更好
3. 对于写密集型的应用,直接禁用查询缓存可能会提高系统的性能
4. 因为对互斥信号量的竞争,有时直接关闭查询缓存对读密集型的应用也会有好处