分区表是一个独立的逻辑表,底层由多个物理子表组成。实现分区的代码是对一组底层表的句柄对象的封装。
MySQL在创建表时使用PARTITION BY子句定义每个分区存放的数据。
分区一个目的就是将数据按一个较粗粒度分在不同表中。可以将相关数据存放在一起,如果想一次删除整个分区的数据也很方便。
分区表操作
优点:
缺点:
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()。但表达式返回的值是一个确定的整数,且不能是一个常数。
还支持键值、哈希和列表分区,有些还支持子分区。
我们通过实践分区的表系统通过子分区可降低索引的互斥访问竞争。例如,最近一年的分区数据会被很频繁的访问,导致大量的互斥量的竞争。使用哈希子分区可以将数据切成多个小片,降低互斥竞争。
10TB数据,大概10亿条记录。
因为数据量大,不能每次查询做全表扫描。使用索引,也会产生大量碎片,导致程序崩溃。B-Tree索引已经没用了,维护代价也太高。
这样就可以用分区,可以用小代价定位数据的区域位置。在这个分区可以顺序扫描,可以建索引,还可将数据缓存到内存。
包括大数据量可扩展性:
1. 全量扫描数据,不要任何索引。根据分区规则定位大致的数据位置。在某个分区能使用WHERE条件,效率是很高的。
2. 索引数据,分离热点。
如果数据有明显热点。其他数据很少被访问,可以将热点这部分数据单独放在一个分区。让这个分区的数据有机会缓存到内存。使得这个很小的分区表中能使用索引,也可用缓存。
NULL值使分区过滤无效
假设以order_date字段进行分区,那么有的记录order_date是null的话,就默认放到第一个分区。
当进行一个order_date BETWEEN ‘2012-1-1’ AND ‘2012-1-31’的查询。它除了检查2012年的分区还会检查,还会检查第一个分区,因为有的记录是order_date是null。
分区列和索引列不匹配
定义的索引列和分区列不匹配,导致查询无法进行分区过滤。
选择分区的成本很高
随着分区数的增长,成本会越来越高。
按行写入大量数据时,每写入一行数据到范围分区表,都要扫描分区定义列表来找到目的分区。
可通过限制分区数量缓解此问题,正常100个左右的分区是没有问题的。
其他分区如键分区和哈希分区,没有此问题。
打开并锁住所有底层表成本很高
查询访问分区表时,MySQL要打开并锁住所有底层表。这个操作在分区过滤前发生。
维护分区成本可能很高
例如重组分区或ALTER语句操作;这种操作要复制数据。
分区其他限制:
有些优化器是不能执行分区过滤的,会扫描全部分区表
例如:
PARTITIONS SELECT * FROM sales WHERE YEAR(day)=2010
mysql不能根据表达式值去过滤分区,要改写成这样的
PARTITIONS SELECT * FROM sales WHERE day BETWEEN '2010-1-1' AND '2010-12-31'
这里的WHERE条件带入的是分区列,而不是基于分区列的表达式,所以优化器能够利用这个条件过滤部分分区。
视图本身是虚拟表,不存放任何数据。返回的数据是从其他表中生成的。
MySQL很多对于视图和表是同样对待的。
但不能对视图创建触发器,不能用DROP TABLE删除视图。
CREATE VIEW country_view AS
SELECT * FROM country WHERE contient = 'Oceania'
WITH CHECK OPTION;
InnoDB是mysql唯一支持外键的内置存储引擎。
外键有成本,外键每次在修改数据时都要在另外一张表中多执行一次查找操作。
提升性能场景:想确保两个相关表始终有一致的数据,外键比程序中检查一致性的性能高得多。外键操作是逐行进行,更新会比批量删除和更新要慢。
如果只是使用外键做约束,那还是在程序中约束更好,外键会带来很大额外消耗。严重影响性能。
场景:希望通过关键字的匹配来进行查询过滤,需要基于相似度的查询,而不是原来的精确数值比较。
全文索引没有其他索引也可以工作,如果有索引效率会更高。
互联网搜索引擎就是类似于全文索引的,只不过它的索引对象是超大量的数据。
全文索引可支持各种字符内容搜索,如CHAR,VARCHAR和TEXT类型。
MyISAM的全文索引对象是一个“全文集合”,这可能是某个数据表的一列,也可能是多列。
对表的某一条记录,MySQL会将需要索引的列全部拼接成一个字符串,进行索引。
MyISAM全文索引是一类特殊的B-Tree索引。共两层,第一层是所有关键字,然后对于每个关键字的第二层,是一组相关的“文档指针”。
缓存完整的SELECT查询结果,也就是“查询缓存”。
MySQL查询缓存保存查询返回的完整结果。当查询命中该缓存,MySQL立即返回结果,跳过解析、优化和执行阶段。
查询缓存系统跟踪查询每个表,如果表发生变化,和这个表相关的缓存数据都将失效。
方法:缓存放在一个引用表中,通过一个哈希值引用,这个哈希值包括了查询本身、要查询的数据库、客户端协议的版本等。
当查询语句中有些不确定数据,则不会被缓存。
如函数NOW()或CURRENT_DATE()的查询不会被缓存。
打开查询缓存对读和写都会带来额外消耗:
查询缓存操作是一个加锁排他操作,消耗很大。
如果查询缓存使用很大的内存,那缓存失效操作会成为严重性能瓶颈。
删除失效缓存是一个全局锁保护的,所有做该操作的查询等待这个锁。比如监测是否命中缓存,还有缓存失效检测都等待这个全局锁。
对哪些需要消耗大量资源的查询通常适合缓存的。
例如,汇总计算查询COUNT()。
1.对于复杂的SELECT语句都可使用查询缓存,2.还有多表JSON后还做排序和分页,这种消耗很大,但返回结果集却很小,非常适合缓存。
缓存未命中原因:
大多数查询被缓存了,但有大量缓存未命中:
缓存碎片、内存不足、数据修改都会造成缓存失效。
在 /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完成碎片整理。
注意: