查询执行基础知识
show processlist来查看当前有何进程
查询缓存
Mysql的cache功能的原理是,如果开启mysql的缓存功能,mysql自动将select语句按一定的规则生成二进制的结果集并对一个key缓存到内存中,如果下次用户的请求跟这条select语句一样的话就会直接调用内存中的数据,而不用再去重新执行sql语句了,所以只有select语句跟缓存中的select完完全全一样才行。
另外,缓存起来的select语句涉及到的table只要有变动缓存就会自动删除,比如insert update delete alter table等等,所以缓存适合数据很少变动的table.
query_cache_type有三个值,On off demand 如果为demand 的话,加了sql_cache的sql语句会被缓存起来,不加的不会被缓存,若想取消已经被换成的语句可以用sql_no_cache
具体语法
select sql_cache id ,name from customer;将这条语句缓存起来
query_cache_size 缓存大小 ,不要太大,256M就已经很大了
query_cache_limit 设置每个缓存的最大值,比如一个缓存的结构是10万条记录,是缓存还是不缓存取决于这个值的大小,缺省好像是1M
reset_query_cache 从查询缓存中移除所有的查询结果
flush_query_cache 清理碎片
Qcache_queries_in_cache 搜索缓存中结果集的查询数目
Qcache_inserts 被加入到缓存中的查询数目
Qcache_hits 缓存命中数目
Qcache_lowmem_prunes 因缺少内存而被从缓存中删除的查询数目
Qcache_not_cached 没有被缓存的查询数目(不能被缓存的,或由于query_cache_type)
Qcache_free_memory 缓存中空闲的内存总数
show status like 'qcache' 查看所有以qcache开头的
SQL语句基础知识
sql语句有很多,这里我就不一一讲述每个语法的具体用法了,直接用实例来说明,我认为sql语句中用的最多的要数表联结了,然后count也比较多,groupby是比较麻烦的,很多人弄不清。
现在着重说一下group by
group by一直是我比较头疼的,现在终于自认为的明白了。
这篇文章写的很好,可以做参考。http://www.5iphp.com/node/62
说一下我对group by的理解,我认为凡是有group by的语句,都会先执行group by,然后在执行其他条件,然后还需要说明一点的是having的使用,Having是针对分组以后的语句进行处理的,而where是对所有的数据进行处理.
一个很重要的知识点,不要对索引进行运算,比如
SELECTID FROMT WHERENUM/2=100
应改为: SELECTID FROMT WHERENUM=100*2
SELECTID FROMT WHERENUM/2=NUM1 如果NUM有索引应改为: SELECTID FROMT WHERENUM=NUM1*2
如果NUM1有索引则不应该改。
不要对索引进行格式转化,不要对索引字段使用函数,不要对索引字段进行多字段连接
myisam和innodb进行count搜索的时候,只有在全局搜索的时候myisam比innodb
sql语句优化:
1 避免使用*,即使搜索出所有列也不要使用*,直接使用列名
2 where 中连接条件的顺序,select empmo,ename,salary,eptno FROM employee WHERE salary>1000 AND salary<3000
修改select empmo,ename,salary,eptno FROM employee WHERE salary < 3000 AND salary > 1000
很容易理解,原则就是尽量减少检索的数据
3 避免使用OR关键字 ,建议使用IN,因为OR不会使用索引
4 避免使用!=,目前不知道原因,建议改成SELECT tealD,teaname,salary FROM T_teacher WHERE salary < 3000 OR salary
< 3000
5 在相关子查询中使用exists代替in关键字,因为数据库首先会执行in关键字的查询然后将查询结果放在一个临时表中在执行
主查询的,而带有exists的查询会先进行主查询的sql,在通过子查询匹配数据
6 使用like ,实现模糊查询。SELECT stuID,sutNAME,age,sex,birthday,birth FROM T_student WHERE stuID LIKE '%100%'
修改成SELECT stuID,sutNAME,age,sex,birthday,birth FROM T_student WHERE stuID LIKE 's100%'
因为第一种不会使用索引,注意。
7 避免使用having,如果可以通过使用where 就尽量使用where
8 使用存储过程,存储过程是经过编译的存放在服务器端的sql语句,不需要再进行额外的编译 解析,一般大型项目使用
9 规范sql语句
10 任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等号右边。
11 不要再索引列上进行运算,否则会导致无法使用索引
12 count(column)和count(*)是两个完全不同的概念,第一个是查找该字段中所有值不为空的数量,第二个是查找所有字段的数量
MYSQL 支持的索引类型
mysql的索引是为了加快数据操作的,所以是存在于存储引擎层的。
B-tree特点
1 以B+树的结构来存储数据
2 能够加快数据查询的速度
3 更适合进行范围查找(因为它是进行顺序存储的)
什么情况下能够使用到B树索引
1 全职匹配的查询 如 order_sn = '345498790149'
2 匹配最左前缀的查询 这里指的是联合索引的情况下第一列使用到了索引
3 匹配列前缀的查询 order_sn like '2435%'
4 匹配范围查询 order_sn < 34354465656 and order_sn > 1213243546546
5 精确匹配做前列并范围匹配另外一列 即联合索引的情况下,左边是精确匹配,右边是部分匹配可以使用到索引
6 只访问索引的查询(覆盖索引)
B树索引的限制
1 如果不是按照索引的最左咧开始查找,则无法使用索引
2 不能跳过左边的列,和上面不同。这里我们可以举一个三个列做成的索引来理解。
3 not in 和 <> 操作无法使用索引
4 如果查询中某个列使用了范围查询,那么这个列右侧的所有列都将无法使用索引
HASH索引 的特点 < memory 默认使用hash,也支持B树。INNODB也支持hash,但不是我们自己建立而是存储引擎根据B树索引根据索引使用情况自适应建立>
1 hash索引是基于hash表实现,只有查询条件精确匹配到hash索引中的所有列时才会使用。也就是说使用hash索引的前提是精确匹配--等值查询。对于hash索引中的所有列,存储引擎会为每一行计算一个hash码,hash索引中存储的就是hash码,所以hash索引只能进行等值查询。
hash索引的限制
1 hash索引必须进行二次查找。因为索引中存储的是hash码,所以不会直接查找数据,先找到hash码,然后通过指针找到具体的数据
2 hash索引无法用于排序
3 只支持全键值的匹配查找,不支持部分索引查找和范围查找
使用索引的3个优势
1 使用索引可大大减少存储引擎需要扫描的数据量
2 因为索引是按照顺序存储的,所以可以帮助我们进行排序以避免使用临时表
3 所以可以把随机I/O变顺序I/O
索引的劣势
1 增加写操作的成本。innodb为此引入插入缓存的概念,将多次操作汇总成一次处理
2 过多的索引会增加查询优化器的选择时间,也会影响查询的效果
建立索引的原则
1 索引列不能使用表达式或函数
2 索引的大小限制,innodb是767个字节。myisam是1000个字节。所以进行字符串前缀进行索引。但要注意进行前缀索引时不能使索引的选择性太差(重复性太高)
3 在合适的情况下联合索引比给每一个列都添加索引要好
联合索引的建立原则:1 经常使用到的列放到优先 2 选择性高的优先(即唯一性高,比如性别列就非常低不适合建索引)3 宽度小的优先(宽度小意味者在一页中可以存储更多的索引)
4 覆盖索引
使用索引来优化查询
如果explain中的type列为index,说明使用索引扫描来进行优化排序。使用索引进行优化排序需要满足的条件
1 索引的列顺序和order by子句的顺序完全一致
5 使用B树索引模拟hash索引(针对列的长度比较大的字段,建立索引前缀导致选择性比较差的情况)
比如使用md5处理想要优化的字段(重新建立一个字段,值为原来字段的md5处理后的值),然后给这个字段建立索引,就可以模拟hash索引了。当然了这种情况下只能使用全职查询
6 利用索引优化锁
减少锁定的行数 在存储引擎层过滤掉我们不需要的行(因为innodb是行级索,如果存储引擎层不能过滤掉所有行仍然需要锁定所有行通过where条件进行过滤)
sql 语句优化
获取有性能问题的sql的几种方法
1 终端客户
2 通过慢查询日志
3 实时获取存在性能问题的sql,比如当前服务器压力突增时
slow_query_log 启动停止慢查询日志 可以通过脚本定时的开关控制该参数以达到记录某时间段内记录日志的目的
slow_query_log_file 指定慢日志的存储路径及文件名
long_query_time 现在可以精确到微秒,但目前单位是秒,所以我们可以设置0.0001秒这种值
long_queries_not_using_indexes 记录所有未使用索引的sql
常用的慢日志分析工具
自带工具:mysqldumpslow
汇总除查询条件外其他完全相同的sql,并将分析结果按照参数中所指定的顺序输出
mysqldumpslow -s order 指定按照那种排序方式输出结果
pt_query_digest
如何实时获取有性能问题的sql?
1 利用information_schema 下的processlist表
sql的解析预处理及生成执行计划
mysql服务器处理一条sql需要经过的步骤:
1 首先接受sql->
2 服务器检查是否可以在查询缓存中命中该sql->
3 服务器进行解析,预处理,再由优化器生成对应的执行计划-》
4 根据执行计划,调用存储引擎API来查询数据-》
5 done
其中第2 3 4步骤中都会对性能造成影响,下面先说一下mysql查询缓存的影响
在解析一个查询语句之前,如果查询缓存是打开的,mysql优先检查该sql是否存在于查询缓存中,通过一个队大写小敏感的哈希查找来实现。因为hash查找只能进行全值匹配,所以请求的sql和数据表中所有信息必须完全一致,是所有信息必须完全一致,包括数据结构,存储数据,大小写,字段顺序等等。对于一个读写频繁的系统使用查询缓存很可能会降低查询处理的效率。建议不适用查询缓存
query_cache_type 是否打开查询缓存,参数值包括 ON ,OFF,DEMAND
query_cache_size
query_cache_limit 设置查询缓存可用存储的最大值,超过这个结果将不被缓存
如何获取mysql在处理一个查询时在各个阶段消耗的时间
profile
实际运用中的索引使用情况
(1) SELECT COUNT(1) AS c FROM company_business WHERE industry_id=13;
这里的industry_id上建立了索引,表数据大约有110万条,很明显这里会用到索引,但这里处理时间也不是很快用了大约13毫秒左右,以下是它的实际索引使用情况
耗时比较长的原因是因为检索的行数因为业务逻辑的需要比较大,所以耗时较长,如果我们给搜索的条件换一下,industry_id = 15,检索的条数明显减少的情况下,耗时也减少了90%
重点不在这里,如果我修改sql语句为 SELECT COUNT(1) AS c FROM company_business WHERE industry_id=13 AND AUDIT_TAG = 2;
可知的是audit_tag上没有加索引,实际索引使用情况是下面这样
几乎是一模一样的,但在我测试的结果是,这种情况的效果要差很多,非常非常慢
这里牵扯到的东西很明显不仅仅是索引这么简单了,首先我们得知已经使用到了索引,但仍然非常慢(未完待续)
(2)mysql在乎的是检索的行数,不是字段,也就是
EXPLAIN SELECT * FROM company_business WHERE industry_id = 13 LIMIT 100;
EXPLAIN SELECT COUNT(*) FROM company_business WHERE industry_id = 13;
这两条语句,很明显,where条件是一样的,首先可以肯定的是索引是一样的,但很明显耗时肯定是有比较大的区别的,因为前者是取100条数据,而后者只是取一个总数量,因为industry_id字段上建立了索引,count(*)会非常快。在extra中也会使用using index,这也是分析这两条语句唯一的不同