MySQL 的查询缓存
工作原理
- 缓存SELECT操作的结果集和SQL语句;
- 新的SELECT语句,先去查询缓存,判断是否存在可用的记录集
触发缓存
- 与缓存的SQL语句,是否完全一样,区分大小写
- 简单认为存储了一个key-value结构,key为sql,value为sql查询结果集
缓存配置参数
show variables like 'query_cache%';
query_cache_type:
- 0 -– 不启用查询缓存,默认值;
- 1 -– 启用查询缓存,
- 只要符合查询缓存的要求,客户端的查询语句和记录集都可以缓存起来,供其他客户端使用,
- 加上 SQL_NO_CACHE将不缓存
- 2 -– 启用查询缓存,
- 只要查询语句中添加了参数:SQL_CACHE,且符合查询缓存的要求,客户端的查询语句和记录集,则可以缓存起来,供其他客户端使用
query_cache_size:
- 允许设置query_cache_size的值最小为40K,默认1M,
- 推荐设置 为:64M/128M;
query_cache_limit:
- 限制查询缓存区最大能缓存的查询记录集,默认设置为1M
命令可查看缓存情况
show status like 'Qcache%';
不会缓存的情况
- 当查询语句中有一些不确定的数据时,则不会被缓存。如包含函数NOW(),CURRENT_DATE()等类似的函数,或者用户自定义的函数,存储函数,用户变量等都不会被缓存
- 当查询的结果大于query_cache_limit设置的值时,结果不会被缓存
- 对于InnoDB引擎来说,当一个语句在事务中修改了某个表,那么在这个事务提交之前,所有与这个表相关的查询都无法被缓存。因此长时间执行事务,会大大降低缓存命中率
- 查询的表是系统表(select * from mysql.user;)
- 查询语句不涉及到表(select 1;)
为什么mysql默认关闭了缓存开启??
- 在查询之前必须先检查是否命中缓存,浪费计算资源
- 如果这个查询可以被缓存,那么执行完成后,MySQL发现查询缓存中没有这个查询,则会将结果存入查询缓存,这会带来额外的系统消耗
- 针对表进行写入或更新数据时,将对应表的所有缓存都设置失效。
- 如果查询缓存很大或者碎片很多时,这个操作可能带来很大的系统消耗
使用业务场景
以读为主的业务,数据生成之后就不常改变的业务.
比如门户类、新闻类、报表类、论坛类等
执行计划
Mysql的查询优化器是基于成本计算的原则。他会尝试各种执行计划。数据抽样的方式进行试验
- 使用等价变化规则:基于联合索引,调整条件位置等
5 = 5 and a > 5
改写成a > 5
a < b and a = 5
改写成b > 5 and a = 5
- 优化count 、min、max等函数
- min函数只需找索引最左边
- max函数只需找索引最右边
- myisam引擎count(*)
- 覆盖索引扫描
- 子查询优化
- 提前终止查询
- 用了limit关键字或者使用不存在的条件
select * from user where id=-1;
- IN的优化
- 先进性排序,再采用二分查找的方式
- 这里表述的是 in (1,2,3,4) ,而不是in (子查询)
- in 中包含子查询的 写法,不建议使用,会导致驱动表全表扫描
如何查看执行计划
mysql> explain select * from person where id in(select id from person where name='guaoran') \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: person
partitions: NULL
type: ref
possible_keys: PRIMARY,idx_name
key: idx_name
key_len: 302
ref: const
rows: 1
filtered: 100.00
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: person
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: my_demo.person.id
rows: 1
filtered: 100.00
Extra: NULL
2 rows in set, 1 warning (0.00 sec)
mysql> explain select * from my_innodb where id in(select phonenum from my_archive where age=20) \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: <subquery2>
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
filtered: 100.00
Extra: Using where
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: my_innodb
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 8
ref: <subquery2>.phonenum
rows: 1
filtered: 100.00
Extra: Using where
*************************** 3. row ***************************
id: 2
select_type: MATERIALIZED
table: my_archive
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 999999
filtered: 10.00
Extra: Using where
3 rows in set, 1 warning (0.00 sec)
执行计划-id
select查询的序列号,标识执行的顺序
- id相同,执行顺序由上至下
- id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
- id相同又不同即两种情况同时存在,id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行
执行计划-select_type
查询的类型,主要是用于区分普通查询、联合查询、子查询等
SIMPLE
:简单的select查询,查询中不包含子查询或者unionPRIMARY
:查询中包含子部分,最外层查询则被标记为primary- SUBQUERY/MATERIALIZED:
SUBQUERY
表示在select 或 where列表中包含了子查询MATERIALIZED
表示where 后面in条件的子查询
UNION
:若第二个select出现在union之后,则被标记为union;UNION RESULT
:从union表获取结果的select
执行计划-table
查询涉及到的表
- 直接显示表名或者表的别名
<unionM,N>
由ID为M,N 查询union产生的结果<subqueryN>
由ID为N查询生产的结果
执行计划-type
访问类型,sql查询优化中一个很重要的指标,结果值从好到坏依次是: system > const > eq_ref > ref > range > index > ALL
-
system:表只有一行记录(等于系统表),const类型的特例,基本不会出现,可以忽略不计
-
const:表示通过索引一次就找到了,const用于比较primary key 或者 unique索引
explain select * from my_innodb where id =1 \G
-
eq_ref:唯一索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键 或 唯一索引扫描
explain select * from users u,user_address a where a.userId = u.id\G
-
ref:非唯一性索引扫描,返回匹配某个单独值的所有行,本质是也是一种索引访问
explain select * from users where age=5\G
-
range:只检索给定范围的行,使用一个索引来选择行
-
index:Full Index Scan,索引全表扫描,把索引从头到尾扫一遍
-
explain select depart from person \G
-
-
ALL:Full Table Scan,遍历全表以找到匹配的行
执行计划-possible_keys/key/rows/filtered
possible_keys
: 查询过程中有可能用到的索引key
: 实际使用的索引,如果为NULL,则没有使用索引rows
: 根据表统计信息或者索引选用情况,大致估算出找到所需的记录所需要读取的行数filtered
: 它指返回结果的行占需要读到的行(rows列的值)的百分比。(有效率命中率)- 表示返回结果的行数占需读取行数的百分比,filtered的值越大越好
执行计划-Extra
十分重要的额外信息
-
Using filesort :
mysql对数据使用一个外部的文件内容进行了排序,而不是按照表内的索引进行排序读取mysql> explain select * from cm_customer_person order by name desc \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: cm_customer_person partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 37009 filtered: 100.00 Extra: Using filesort 1 row in set, 1 warning (0.00 sec)
-
Using temporary:
使用临时表保存中间结果,也就是说mysql在对查询结果排序时使用了临时表,常见于order by 或 group by
-
Using index:
表示相应的select操作中使用了覆盖索引(Covering Index),避免了访问表的数据行,效率高
-
Using where :
表示使用了where过滤条件
-
select tables optimized away:
基于索引优化MIN/MAX操作或者MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段在进行计算,查询执行计划生成的阶段即可完成优化
-
Using index condition
Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行;
使用慢查询
在 linux 上进行 慢查询分析 ,可以输出到 file 文件中和 数据库表中
## 查看慢查询的配置
show variables like 'slow_query_log';
## 开启慢查询日志
set global slow_query_log=on;
## 自定义设置慢查询日志文件
set global slow_query_log_file = '/guaoran/mysql/data/slow_query_log_demo.log';
## sql 查询要不要记录慢查询
set global log_queries_not_using_indexes=on;
## 单位 秒 ,当查询时间超过0.1s就记录慢查询日志
set global long_query_time = 0.1;
## 慢查询信息
show global status like '%slow%';
## 使用自带工具分析慢查询日志
mysqldumpslow -s c /guaoran/mysql/data/slow_query_log_demo.log
# perl mysqldumpslow -s c /usr/local/mysql/data/localhost-slow.log
## 查看所有操作的记录
show variables like '%general%';
## 打开、关闭记录所有的日志操作记录
set global general_log=on;
set global general_log=off;
## 查看慢查询日志输出的格式 该输出包含 慢日志查询和general_log 的输出
show variables like '%log_output%';
## 设置慢查询输出的 格式,table 的话,可以在mysql.slow_log 和 mysql.general_log 中查看
set global log_output='FILE,TABLE';
查询用时最多的20 条慢SQL
mysqldumpslow -s t -t 20 -g 'select' /var/lib/mysql/localhost-slow.log
Count 代表这个SQL 执行了多少次;
Time 代表执行的时间,括号里面是累计时间;
Lock 表示锁定的时间,括号是累计;
Rows 表示返回的记录数,括号是累计。