mysql-SQL调优

本文主要为有一定的sql基础,但对sql是否有使用到索引等问题的调优方式还不大清楚的人,指出一个使用的方式。

本文并非纯原创,是结合前人的博文,自己总结写一篇博文。

mysql join集合用法:https://blog.csdn.net/lukabruce/article/details/80568796

索引、分库分表、sql的写法等,都会影响到sql性能。

概念:

1. 数据库事务ACID特性

数据库事务的4个特性:
原子性(Atomic):
 事务中的多个操作,不可分割,要么都成功,要么都失败; All or Nothing.
一致性(Consistency): 事务操作之后, 数据库所处的状态和业务规则是一致的; 比如a,b账户相互转账之后,总金额不变;
隔离性(Isolation): 多个事务之间就像是串行执行一样,不相互影响;
持久性(Durability): 事务提交后被持久化到永久存储.

3. MySQL 中RC和RR隔离级别的区别

MySQL数据库中默认隔离级别为RR,但是实际情况是使用RC 和 RR隔离级别的都不少。好像淘宝、网易都是使用的 RC 隔离级别。那么在MySQL中 RC 和 RR有什么区别呢?我们该如何选择呢?为什么MySQL将RR作为默认的隔离级别呢?

4. RC 与 RR 在锁方面的区别

1> 显然 RR 支持 gap lock(next-key lock),而RC则没有gap lock。因为MySQL的RR需要gap lock来解决幻读问题。而RC隔离级别则是允许存在不可重复读和幻读的。所以RC的并发一般要好于RR;

2> RC 隔离级别,通过 where 条件过滤之后,不符合条件的记录上的行锁,会释放掉(虽然这里破坏了“两阶段加锁原则”);但是RR隔离级别,即使不符合where条件的记录,也不会释放行锁和gap lock;所以从锁方面来看,RC的并发应该要好于RR;另外 insert into t select ... from s where 语句在s表上的锁也是不一样的。

5.查看数据库有哪些行被锁以及事务

二、mysql行锁、表锁

对于myisam的表select 是会锁定表的 ,会导致其他操作挂起,处于等待状态。
对于innodb的表select 是不会锁表的。其实这里使用到了快照。快照这里不作讨论。

常用innodb,select ... for update,注意一定要where + id 或者唯一索引字段过滤,不然会导致表锁。

优化心得:

1.服务器优化,例如max_connection改大,connection_timeout修改

2.客户端连接优化,连接池复用

3.架构层面。

    1)使用缓存(例如redis)。

    2)再不行开集群,读写分离。

    3)即使集群分离读写,假设数据量上亿怎么也慢,那就分库分表

4.配置slow_query_log,用于分析执行时间超过设定时长的sql,但也会耗一定性能;可配合bin目录下mysqldumpslow工具分析统计慢查询语句。

sql优化:

1.分页查询倘若实在过于复杂,可以先查出分页数据, 然后再直接查指定的分页数据详情列。

2.case when语句,写在where前对性能影响不太大,但写在where后面会很耗时间

3.mysql大数据量使用limit分页,随着页码的增大,查询效率越低下。

    select * from product limit 10, 20   0.016秒
    select * from product limit 100, 20   0.016秒
    select * from product limit 10000, 20   0.094秒

    select * from product limit 400000, 20   3.229秒

   利用表的覆盖索引来加速分页查询
   我们都知道,利用了索引查询的语句中如果只包含了那个索引列(覆盖索引),那么这种情况会查询很快。

SELECT * FROM product WHERE ID > =(select id from product order by id limit 866613, 1) limit 20 或者

   SELECT * FROM product a JOIN (select id from product order by id limit 866613, 20) b ON a.ID = b.id

   查询时间为0.2秒! 

select id from collect where vtype=1 limit 90000,10;   //加了 search(vtype,id) 这样的复合索引,才会快

2.索引原理

mysql innodb现在主要用的就是B+ Tree结构的索引,B+Tree 是绝对平衡的树,它需要知道某条数据该往左往右走,才能很好的减少主存与硬盘间的io操作,加快查询速度。(注意区分:B树不等同于平衡二叉树,它是一种多路查找树,和B树不同的是它的数据都是存储于叶子结点的,且叶子结点之间由指针相连,只是平衡树的一种。)

mysql索引的存储结构一般使用B+树,其实它还有hash结构存储,hash结构查找数据的复杂度低o(1),而B+树一般只有o(log n),那么为什么选择b+树?

分析:

1.为什么不用二叉树呢? 
因为我们要考虑磁盘IO的影响,它相对于内存来说是很慢的。数据库索引是存储在磁盘上的,当数据量大时,就不能把整个索引全部加载到内存了,只能逐一加载每一个磁盘页(对应索引树的节点)。所以我们要减少IO次数

2.为什么不使用hash?

而且数据库中的索引一般是在磁盘上,数据量大的情况可能无法一次装入内存,B+树的设计可以允许数据分批加载,同时树的高度较低,提高查找效率。

这和业务场景有关。如果只选一个数据,那确实是Hash更快。但是数据库中经常会选择多条,这时候由于B+树索引有序,并且又有链表相连,它的查询效率比Hash就快很多了。

一般可以知道,not in  <> is null这些不走索引,不用死记其实很简单,就是因为它在使用b+树索引的时候,无法得知该从树的左边还是右边的路线找起,因而只能全表搜索,效率低,下面是更多的验证,其中联合索引的底层我还不了解。

就举个栗子,大家都知道索引列的数据长度太长也会影响索引的效率,但实际是为什么呢?

其实就是因为mysql使用的b+tree索引,所有的数据都是放在叶子节点上的,所以当索引列所占大小太大的时候,会导致每一个叶子结点存储不了太多的数据,导致出现更多的叶子,更多的叶子会导致树的层数增多,层数深了,查找时走io的次数也增多的,所以就效率低。

2.运行调优

IDEA工具推荐使用XRebel,可能快速定位哪些sql效率过低

试验

指令:

explain extended select * from tablename;
show warnings;

使用explain extended指令explain可以帮助我们分析select语句,从而可以针对性地去做优化。

show warnings则是可以查看到我们编写的sql是如何被优化的,可以很直观地查到自己的sql是否优良。

下面是一张测试表:

CREATE TABLE `t_stu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

输入explain extended xxx指令为结果一

show warnings; 指令为结果二

结果二很明了可以看到我们编写的sql被自动补上了全列名

解析

那么对于结果怎么分析呢?

对于结果一:(这个总结是引用自上面所提到的博文)

 

这里总结几个可能容易理解错误的技巧:

  1. 通常来说把可为NULL的列改为NOT NULL不会对性能提升有多少帮助,只是如果计划在列上创建索引,就应该将该列设置为NOT NULL。

  2. 对整数类型指定宽度,比如INT(11),没有任何卵用。INT使用32位(4个字节)存储空间,那么它的表示范围已经确定,所以INT(1)和INT(20)对于存储和计算是相同的。

  3. UNSIGNED表示不允许负值,大致可以使正数的上限提高一倍。比如TINYINT存储范围是-128 ~ 127,而UNSIGNED TINYINT存储的范围却是0 - 255。

  4. 通常来讲,没有太大的必要使用DECIMAL数据类型。即使是在需要存储财务数据时,仍然可以使用BIGINT。比如需要精确到万分之一,那么可以将数据乘以一百万然后使用BIGINT存储。这样可以避免浮点数计算不准确和DECIMAL精确计算代价高的问题。

  5. TIMESTAMP使用4个字节存储空间,DATETIME使用8个字节存储空间。因而,TIMESTAMP只能表示1970 - 2038年,比DATETIME表示的范围小得多,而且TIMESTAMP的值因时区不同而不同。

  6. 大多数情况下没有使用枚举类型的必要,其中一个缺点是枚举的字符串列表是固定的,添加和删除字符串(枚举选项)必须使用ALTER TABLE(如果只只是在列表末尾追加元素,不需要重建表)。

  7. schema的列不要太多。原因是存储引擎的API工作时需要在服务器层和存储引擎层之间通过行缓冲格式拷贝数据,然后在服务器层将缓冲内容解码成各个列,这个转换过程的代价是非常高的。如果列太多而实际使用的列又很少的话,有可能会导致CPU占用过高。

  8. 大表ALTER TABLE非常耗时,MySQL执行大部分修改表结果操作的方法是用新的结构创建一个张空表,从旧表中查出所有的数据插入新表,然后再删除旧表。尤其当内存不足而表又很大,而且还有很大索引的情况下,耗时更久。当然有一些奇技淫巧可以解决这个问题,有兴趣可自行查阅。

索引建立依据 

 

回表:innodb查询条件是二级索引时,查到B+树的叶子节点没有整条的数据(innodb二级索引树叶子只存放该索引键值和主键键值),所以还得到主键索引的B+树再去查找一遍,这就是回表。

覆盖索引:同上,但特例是select 二级索引字段 from XX,因为查询列只有二级索引字段,索引在二级索引B+树查找的时候,叶子节点已经有想要的数据了,不再回表。

 mysql是否用索引,主要依据基于成本的优化器决定的。比较智能,所以有些死套的规则并不符合。

例如查询条件带 不等于 , not in。挺多人说会导致索引失效,但不一定。

举个例,假设自增主键表,select  * from table where id != 1; 就会走索引,因为优化器发现这种情况,只要找到id=1的叶子节点,因为叶子节点是有序的,所以直接找出1之后的叶子节点数据即可,这就是走索引了,所以最好要灵活判断。

发布了21 篇原创文章 · 获赞 9 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/a5552157/article/details/83104922