《高性能MySQL》阅读笔记-第7章 MySQL高级特性

7.1 分区表

分区表是一个独立的逻辑表,底层由多个物理子表组成。实现分区的代码是对一组底层表的句柄对象的封装。

MySQL在创建表时使用PARTITION BY子句定义每个分区存放的数据。

分区一个目的就是将数据按一个较粗粒度分在不同表中。可以将相关数据存放在一起,如果想一次删除整个分区的数据也很方便。

分区表操作

优点

  • 表非常大无法全部放在内存中,或在表的最后部分有热点数据,其他都是历史数据。
  • 分区表数据易维护,想批量删除大量数据可以清除整个分区。还可对分区进行优化、检查、修复等。
  • 分区表的数据可分在不同物理设备上,高效利用硬件设备
  • 可用分区表避免某特殊瓶颈,InnoDB的单个索引互斥访问。
  • 还可备份和恢复独立的分区,在非常大的数据集场景效果很好。

缺点

  • 一个表最多只能有1024个分区
  • 5.5之前分区表达式必须是整数;5.5之后可以直接用列来进行分区
  • 如果分区字段中有主键或唯一索引的列,那所有主键列和唯一索引列都必须包含进来
  • 分区表无法使用外键约束。

7.1.2 分区表类型

MySQL支持多种分区表,最多情况是根据范围进行分区,每个分区存储某个范围内的记录。
分区表达式可以是列,可以是包含列的表达式。

例如,将每年的销售额放在不同分区里:

CREATE TABLE sales(
    order_date DATETIME NOT NULL,
    ...
) ENGINE=InnoDB PARTITION BY RANGE(YERA(order_date))(
    PARTITION p_2010 VALUES LESS THAN (2010),
    PARTITION p_2011 VALUES LESS THAN (2011),
    PARTITION p_2012 VALUES LESS THAN (2012),
    PARTITION p_all VALUES LESS THAN MAXVALUE
);

分区子句中可使用各种函数RANGE()。但表达式返回的值是一个确定的整数,且不能是一个常数。

还支持键值、哈希和列表分区,有些还支持子分区。

我们通过实践分区的表系统通过子分区可降低索引的互斥访问竞争。例如,最近一年的分区数据会被很频繁的访问,导致大量的互斥量的竞争。使用哈希子分区可以将数据切成多个小片,降低互斥竞争。

7.1.3 如何用分区表

10TB数据,大概10亿条记录。
因为数据量大,不能每次查询做全表扫描。使用索引,也会产生大量碎片,导致程序崩溃。B-Tree索引已经没用了,维护代价也太高。

这样就可以用分区,可以用小代价定位数据的区域位置。在这个分区可以顺序扫描,可以建索引,还可将数据缓存到内存。

包括大数据量可扩展性
1. 全量扫描数据,不要任何索引。根据分区规则定位大致的数据位置。在某个分区能使用WHERE条件,效率是很高的。
2. 索引数据,分离热点。
如果数据有明显热点。其他数据很少被访问,可以将热点这部分数据单独放在一个分区。让这个分区的数据有机会缓存到内存。使得这个很小的分区表中能使用索引,也可用缓存。

7.1.4 出现的问题

NULL值使分区过滤无效
假设以order_date字段进行分区,那么有的记录order_date是null的话,就默认放到第一个分区。
当进行一个order_date BETWEEN ‘2012-1-1’ AND ‘2012-1-31’的查询。它除了检查2012年的分区还会检查,还会检查第一个分区,因为有的记录是order_date是null。

分区列和索引列不匹配
定义的索引列和分区列不匹配,导致查询无法进行分区过滤。

选择分区的成本很高
随着分区数的增长,成本会越来越高。
按行写入大量数据时,每写入一行数据到范围分区表,都要扫描分区定义列表来找到目的分区。
可通过限制分区数量缓解此问题,正常100个左右的分区是没有问题的。

其他分区如键分区和哈希分区,没有此问题。

打开并锁住所有底层表成本很高
查询访问分区表时,MySQL要打开并锁住所有底层表。这个操作在分区过滤前发生。

维护分区成本可能很高
例如重组分区或ALTER语句操作;这种操作要复制数据。

分区其他限制

  • 所有分区必须使用相同的存储引擎
  • 分区函数中可使用的函数和表达式也有限制
  • 某些存储引擎不支持分区

7.1.5 查询优化

有些优化器是不能执行分区过滤的,会扫描全部分区表
例如:

PARTITIONS SELECT * FROM sales WHERE YEAR(day)=2010

mysql不能根据表达式值去过滤分区,要改写成这样的

PARTITIONS SELECT * FROM sales WHERE day BETWEEN '2010-1-1' AND '2010-12-31'

这里的WHERE条件带入的是分区列,而不是基于分区列的表达式,所以优化器能够利用这个条件过滤部分分区。

7.2 视图

视图本身是虚拟表,不存放任何数据。返回的数据是从其他表中生成的。
MySQL很多对于视图和表是同样对待的。
但不能对视图创建触发器,不能用DROP TABLE删除视图。

CREATE VIEW country_view AS
SELECT * FROM country WHERE contient = 'Oceania'
WITH CHECK OPTION;

7.3 外键约束

InnoDB是mysql唯一支持外键的内置存储引擎。
外键有成本,外键每次在修改数据时都要在另外一张表中多执行一次查找操作。

提升性能场景:想确保两个相关表始终有一致的数据,外键比程序中检查一致性的性能高得多。外键操作是逐行进行,更新会比批量删除和更新要慢。

如果只是使用外键做约束,那还是在程序中约束更好,外键会带来很大额外消耗。严重影响性能。

7.10 全文索引

场景:希望通过关键字的匹配来进行查询过滤,需要基于相似度的查询,而不是原来的精确数值比较。

全文索引没有其他索引也可以工作,如果有索引效率会更高。
互联网搜索引擎就是类似于全文索引的,只不过它的索引对象是超大量的数据。

全文索引可支持各种字符内容搜索,如CHAR,VARCHAR和TEXT类型。

MyISAM的全文索引对象是一个“全文集合”,这可能是某个数据表的一列,也可能是多列。
对表的某一条记录,MySQL会将需要索引的列全部拼接成一个字符串,进行索引。

MyISAM全文索引是一类特殊的B-Tree索引。共两层,第一层是所有关键字,然后对于每个关键字的第二层,是一组相关的“文档指针”。

7.12 查询缓存

缓存完整的SELECT查询结果,也就是“查询缓存”。
MySQL查询缓存保存查询返回的完整结果。当查询命中该缓存,MySQL立即返回结果,跳过解析、优化和执行阶段。

查询缓存系统跟踪查询每个表,如果表发生变化,和这个表相关的缓存数据都将失效。

7.12.1 MySQL如何判断缓存命中

方法:缓存放在一个引用表中,通过一个哈希值引用,这个哈希值包括了查询本身、要查询的数据库、客户端协议的版本等。

当查询语句中有些不确定数据,则不会被缓存。
如函数NOW()或CURRENT_DATE()的查询不会被缓存。

打开查询缓存对读和写都会带来额外消耗

  • 读查询在开始前检查是否命中缓存
  • 这个读查询可被缓存,完成执行后,若查询缓存中没有这个查询,就将结果存入查询缓存。
  • 写操作的影响。向某表写入数据时,必须对该表的所有缓存设置失效。如果查询缓存很大或碎片多,对系统的消耗很大。

查询缓存操作是一个加锁排他操作,消耗很大。

如果查询缓存使用很大的内存,那缓存失效操作会成为严重性能瓶颈。
删除失效缓存是一个全局锁保护的,所有做该操作的查询等待这个锁。比如监测是否命中缓存,还有缓存失效检测都等待这个全局锁。

7.12.3 什么时候可以查询缓存

对哪些需要消耗大量资源的查询通常适合缓存的

例如,汇总计算查询COUNT()。
1.对于复杂的SELECT语句都可使用查询缓存,2.还有多表JSON后还做排序和分页,这种消耗很大,但返回结果集却很小,非常适合缓存。

缓存未命中原因

  • 查询语句无法被缓存,查询中包含一个不确定的函数,或查询结果太大无法缓存。
  • MySQL从未处理这个查询,结果也没被缓存过
  • 之前缓存了查询结果,但查询缓存的内存用完了

大多数查询被缓存了,但有大量缓存未命中

  • 没完成预热。MySQL查询完还没来得及将查询结果都缓存起来
  • 查询语句之前从未执行过,如果应用程序不重复执行一条语句,即使完成预热也会有很多未命中。
  • 缓存失效操作太多

缓存碎片、内存不足、数据修改都会造成缓存失效。

7.12.4 配置查询缓存

在 /etc/my.cnf中配置以打开查询缓存

query_cache_type
是否打开查询缓存,可以设置成OFF,ON,DEMAND。
DEMAND表示只在查询语句中写明SQL_CACHE的语句才放入缓存。

query_cache_size
缓存使用的总内存空间,单位是字节。该值必须是1024的整数倍。否则分配的和你设置的会有不同。

query_cache_min_res_unit
查询缓存中分配内存块的最小单位。

query_cache_limit
能村换的最大查询结果,大于该值,则不会被缓存。
查询缓存在数据开始生成时就尝试缓存数据,只有结果全部返回后,才知道是否超过限制。如果你事先知道,可以在查询语句中不让MySQL缓存,SQL_NO_CAHCE

减少碎片
选择合适的query_cache_min_res_unit大小可减少碎片导致的空间浪费;但是,该值太小,浪费空间少,但导致更频繁的内存块申请;设置值太大,碎片会很多。

可以使用FLUSH QUERY CACHE完成碎片整理。

7.12.6 通用查询缓存优化

注意:

  • 多个小表代替一个大表对查询缓存有好处。
  • 批量写入只做一次缓存失效,比单条写入效率更好。不要同时延迟写和批量写,会因失效导致服务器长时间僵死。
  • 因缓存空间太大,过期操作可导致服务器僵死。可控制缓存空间大小或禁用缓存。
  • 无法在数据库或表级别控制查询缓存,但可以控制某个SELECT语句是否进行缓存。
  • 对写密集型应用,直接禁用查询缓存会提高系统的性能。
  • 因对互斥信号量的竞争,直接关闭查询缓存对读密集型应用会有好处。

你可能感兴趣的:(数据库,MySQL,数据库)