欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

mysql 索引

程序员文章站 2024-01-20 20:49:04
...

SQL执行顺序

1.手写:

SELECT DISTINCT
	<select_list>
FROM
	<left_table><join_type>
JOIN <right_table> ON <join_conditon>
WHERE
	<where_condition>
GROUP BY
	<group_by_list>
HAVING
	<having_condition>
ORDER BY
	<order_by_condition>
LIMIT <limit number>

2 .机读:

FROM
	<left_table><join_type>
JOIN <right_table> ON <join_conditon>
WHERE
	<where_condition>
GROUP BY
	<group_by_list>
HAVING
	<having_condition>
SELECT DISTINCT
	<select_list>
ORDER BY
	<order_by_condition>
LIMIT <limit number>

先来一波mysql关联

##AB交集
select * from tbl_emp a INNER JOIN tbl_dept b  ON a.deptId = b.id;
##A表为准
select * from tbl_emp a left JOIN tbl_dept b  ON a.deptId = b.id;
##除去B表的其余记录
select * from tbl_emp a left JOIN tbl_dept b  ON a.deptId = b.id where b.id is NULL;
#AB表相交的全部
#下面这种只适用于oracle
	#AB相交取全部:
	#Select<select_list> from TableA A full outer join TableB B on A.key=B.key
	#AB相交去交集:
	#Select <select_list> from TableA A full outer join TableB B on A.key = B.key where A.key is null or B.key is null

#mysql
select * from tbl_emp a left JOIN tbl_dept b  ON a.deptId = b.id
UNION
select * from tbl_emp a RIGHT JOIN tbl_dept b on a.deptId = b.id;

select * from tbl_emp a left JOIN tbl_dept b  ON a.deptId = b.id where b.id is null
UNION
select * from tbl_emp a RIGHT JOIN tbl_dept b on a.deptId = b.id where a.deptId is null;

什么是索引

Mysql官方对索引的定义:索引(Index)是帮助Mysql高效获取数据的数据结构
可以得到的索引的本质: 索引是数据结构,索引的目的在于提高查询效率,可以类比字典

你可以理解为"排好序的快速查找数据结构".

(重要)在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,
这样就可以在这些数据结构上实现高级查找算法,这种数据结构,就是索引.下图就是一种可能的索引方式

	0x07	| 	1	|	34|					  34
	0x56	|	2	|	77|			22					 89
	0x6A	|	3	|	5 |
	0xF3	|	4	|	91|		5		23			 77		 91
	0x90	|	5	|	22|
	0x77	|	6	|	89|
	0xd1	|	7	|	23|

左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址,为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别
包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在一定的复杂度内获取到相应的数据,从而快速的检索出符合条件的记录.
数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,
这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高级查找算法,这种数据结构就是索引

  1. 一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上.
  2. 我们平时所说的索引,如果没有特别指明,都是指B树(多路搜索树,并不一定是二叉的)结构组织的索引.其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引,唯一索引默认都是使用B+树索引,统称索引,当然除了B+树这种类型的索引之外,还有哈希索引(hash index)等.

优势:

类似大学图书馆建书目索引,提高数据检索的效率,降低数据库的IO成本 通过索引列对数据进行排序,降低数据排序的成本,降低, CPU的消耗.

劣势:

实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以所以列也是要占用空间的
虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行insert,update,delete
因为更新表时,Mysql不仅仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段, 都会调整因为更新所带来的键值变化后的索引信息.
索引只是提高效率的一个因素,如果你的mysql有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询

单值索引:即一个索引只包含单个列,一个表可以有多个单列索引,一张表建的索引不要超过5个

--创建
create [UNIQUE] INDEX indexName ON mytable(columnname(length));--如果是CHAR,VARCHAR类型.
--删除
drop index[indexname] on mytable;
--查看
show index from table_name\G;
show index from tbl_emp;

有四种方式来添加数据表的索引;

//该语句添加一个主键,这意味着索引值必须是唯一的,且不能为NULL
alter table tbl_name add primary key (column_list);
//这条语句创建索引的值必须是唯一的(除了NULL外,NULL可能会出现多次).
alter table tbl_name add unique index_name(column_list);
//这条语句创建索引的值必须是唯一的(除了NUll外,NULL可能会出现多次).
alter table tbl_name add index index_name(cloumn_list);
//该语句指定了索引为FULLTEXT,用于全文检索
alter table tbl_name add FULLTEXT index_name(column_list);

mysql索引结构:
1.BTreee索引
2.Hash索引
3.full-txt全文索引
4.R-Tree索引

哪些情况需要创建索引:

  1.主键自动建立唯一索引
  2.频繁作为查询条件的字段应该创建索引
  3.查询中与其他表关联的字段,外键关系建立索引
  4.频繁更新的字段不适合创建索引,因为每次更新不单单是更新了记录还会更新索引
  5.where条件里用不到的字段不创建索引
  6.单键/组合索引的选择问题,who?(在高并发下倾向创建组合索引)
  7.查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
  8.查询中统计或者分组字段

哪些情况不要创建索引:

1.表记录太少
2.经常增删改的表
3.数据重复且分布平均的表字段,因此应该只为最经常查询和最经常排序的数据列建立索引. 注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果,

性能分析:

1.Mysql Query Optimizer
(1).Mysql中有专门负责优化SELECT语句的优化器模块,
主要功能:通过计算分析系统中收集到的统计信息,为客户端请求的Query提供他认为最优的执行计划
(他认为最优的数据检索方式,但不见得是DBA认为是最优的,这部分最耗费时间)
(2).当客户端向Mysql请求一条query,命令解析器模块完成请求分类,区别出是select并转发给mysql query optimizer时,
mysql query optimizer首先会对整条query进行优化,处理掉一些常量表达式的预算,直接换算成常量,并对query中的查询条件进行简化和转换,
如去掉一些无用或显而易见的条件,结构调整等.然后分析query中的Hint信息(如果有),看显示Hint信息是否可以完全确定该query的执行计划,
如果没有Hint或Hint信息还不足以完全确定执行计划,则会读取锁涉及对象的统计信息,根据Query进行写相应的计算分析,然后再得出最后的执行计划.

Mysql常见瓶颈:
1.CPU:CPU在饱和的时候一般发生在数据装入内存或磁盘上读取数据时候
2.IO:磁盘I/O瓶颈发生在装入数据远大于内存容量的时候
3.服务器硬件的性能瓶颈:top,free,iostat和vmstat来查看系统的性能状态

EXPLAIN select * from tbl_emp;

id: select查询的***,包含一组数字,表示查询中执行select子句或操作表的顺序
三种情况:

  1. id相同,执行顺序由上之下
    mysql 索引

上面,id相同,都是1,执行顺序是从上到下,也就是先加载t1表,再加载t3,最后是t2,而不是我们写的sql那 样,t1,t2,t3

  1. id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
    mysql 索引
  2. id相同不同,同时存在
    mysql 索引

select_type:

1.simple-- 简单的select查询,查询中不包含子查询或者Union
2.PRIMARY-- 查询中若包含任何复杂的子部分,最外层查询则被标记为primary
3.subquery-- 在select或where列表中包含了子查询
4.derived-- 在from列表中包含的子查询被标记为DERIVED(衍生),Mysql会递归执行这些子查询,把结果放在临时表里.
5.UNION-- 若第二个select出现在union之后, 则被标记为union,若union包含在from子句的子查询中,外层select将被标记为:DERIVED
6.union result-- 从union表获取结果的select

table:表名

type:

all,index,range,ref,eq_ref,const,system,null
mysql 索引

从最好到最差依次是:
✔????system > const > eq_ref > ref > range > index > All
	1. system:表只有一行记录(等于系统表),这是const类型的特列,平时不会出现,这个也可以忽略不计
	2. const:表示通过索引一次就找到了,const用于比较primary key或者unique索引,因为只匹配一行数据,
索引很快如将主键置于where列表中,Mysql就能将该查询转换为一个常量
	4. eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见于主键或唯一索引扫描.
	5. ref:非唯一性索引扫描,返回匹配某个单独值的所有行,然而它可能会找到多个符合条件的行,
所以他应该属于查找和扫描的混合体
	6. range: 只检索给定范围的行,使用一个索引来选择行,key列显示使用了哪个索引一般就是在你的where语句中
出现了between,<、>,in等的查询,这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,
而结束于另一点,不用扫描全部索引
	7. index:	Full Index Scan,index与All区别为index类型只遍历索引树,这通常比All快,因为索引文件通常比数据文件小,
 (也就是说虽然all和index都是读全表,但index是从索引中读取的,而all是从硬盘中读的)
	8. all:		Full Table Scan,将遍历全表以找到匹配的行

一般来说,得保证查询至少达到range级别,最好能达到ref


system const举例
其实就是 单行 单表 这样查询速度的确最快
mysql 索引

eq_ref举例
mysql 索引


ref举例
mysql 索引
上面截图,为t1表的两个字段col1,col2创建了一个索引,然后查询t1表col1字段的count
最后进行一个查询


range举例
range 范围查询 比全表查要好一些
mysql 索引


index举例
mysql 索引


ALL举例
mysql 索引


possible_keys:

显示可能应用在这张表中的索引,一个或多个.

查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用.

key:

实际使用的索引,如果为null,则没有使用索引

查询中若使用了覆盖索引,则该索引仅出现在key列表中

覆盖索引:select 后面接的字段(下面为col1,col2)和创建的复合索引的个数和顺序一模一样
mysql 索引
理论上,应该用不到索引(possible_keys为null)
实际上,select的列刚好和复合索引一样,就扫描索引了(key为idx_col_col2)

key_len:

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度,在不损失精确性的情况下,长度越短越好

key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的.

mysql 索引


ref:

显示索引的哪一列被使用了,如果可能的话,是一个常数,哪些列或者常量被用于查找索引列上的值;
mysql 索引


mysql 索引

rows:

根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数;

extra:

包含不适合在其他列显示但十分重要的信息
1.Using filesort :说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取.
			mysql中无法利用索引完成的排序操作称为"文件排序"
2.Using temporary: 使用了临时表保存中间结果,Mysql对查询结果排序时使用临时表,常见于排序order by 和分组查询 group by.
3.Using index:表示相应的select操作中使用了覆盖索引(Covering Index), 避免访问了表的数据行,效率不错!
			如果同时出现using where,表明索引被用来执行索引键值的查找;
			如果没有同时出现using where ,表明索引用来读取数据而非执行查找动作.
4.using where :表明使用了where条件过滤
5.using join buffer使用了连接缓存
6.impossible where: where子句的值总是false,不能用来获取任何元组
7.select tables optimized away: 在没有group by子句的情况下,基于索引优化MIN/Max操作或者
			对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,
			查询执行计划生成的阶段即完成优化.
8.distinct: 优化distinct操作,在找到第一匹配的元组后即停止找同样值的动作

两表相关联 left join 右边建索引 RIGHT JOIN 左边建索引,三表相同

mysql 索引

索引失效(应该避免):

1.全值匹配我最爱
2.最佳左前缀法则: 复合索引,要遵守最左前缀法则,指的是查询从索引的最左前列开始并且 不跳过索引中的列
3.不在索引列上做任何操作(计算,函数,(自动or手动)类型转换),会导致索引失效而转向全表扫描
4.存储引擎不能使用索引中范围条件右边的列
5.尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select *
6.mysql在使用不等于(!= 或者<>)的时候无法使用索引会导致全表扫描
7.is null, is not null也无法使用索引
8.like以通配符开头('%abc...')mysql索引失效会变成全表扫描的操作 :  把百分号写在最右边,
9.字符串不加单引号索引失效:varchar必须要有单引号
10.少用or,用它来连接时会索引失效

mysql 索引
mysql 索引
mysql 索引
复合索引,第一个索引必须要有,不然其他索引失效,这就是最佳左前缀法则,带头大哥不能死.中间兄弟不能断,类似火车

show index from tbl_dept;
select @@sql_mode ;
SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));
--截取字符串left(name,4)
desc staffs;查看表描述
--解决like'%字符串%'时索引不被使用得当方法?
--用覆盖索引(复合索引)避免全表扫描

--只要type为range一定是用到了范围
--kk%也用到了范围

explain select * from tbl_dept t 
show profile;
--开启慢查询日志
show VARIABLES LIKE '%slow_query_log%';
//默认情况下long_query_time的值为10秒
show VARIABLES LIKE 'long_query_time%';
//设置慢的阈值时间
set GLOBAL long_query_time=3;
--需要重新连接或新开一个会话才能看到修改值
show GLOBAL VARIABLES LIKE 'long_query_time';

select sleep(4);
set global slow_query_log=1;
mysqldumpslow --HELP
show tables;
--创建函数,假如报错:This function has none of DETERMINISTIC...
#由于开启过慢查询日志,因为我们开启了bin-log,我们就必须为我们的function指定一个参数
SHOW VARIABLES like 'log_bin_trust_function_creators';
set GLOBAL log_bin_trust_function_creators=1
select now() from dual;


随机产生字符串

CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN
	DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
	DECLARE return_str VARCHAR(255) DEFAULT '';
	DECLARE i INT DEFAULT 0;
	WHILE i < n DO
		SET return_str=CONCAT(return_str,SUBSTRING(chars_str, FLOOR(1 + RAND() * 52), 1));
		SET i = i+1;
	END WHILE;
	RETURN return_str;
END

随机产生部门编号

CREATE FUNCTION rand_num() RETURNS INT(5)
BEGIN
	DECLARE i INT DEFAULT 0;
	SET i = FLOOR(100 + RAND()*10);
	RETURN i;
END

存储过程创建

CREATE PROCEDURE `insert_emp`(IN START INT(10),IN max_num INT(10))
BEGIN
DECLARE i INT DEFAULT 0;-- 0开始
-- set autocommit=0 把autocommit设置成0
SET autocommit = 0;
REPEAT -- 重复,循环
SET i = i + 1;-- 每一次加一
	INSERT INTO emp(empno,ename,job,mgr,hiredate,sal,comm,deptno)
	VALUES((START+i),rand_string(6),'SALESMAN',0001,CURDATE(),2000,400,rand_num());
UNTIL i = max_num -- 等于最大的max_num退出
END REPEAT;
COMMIT;
END

---------------------------------------------------------------------------------------------
CREATE  PROCEDURE `insert_emp`(IN START INT(10),IN max_num INT(10))
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO emp(empno,ename,job,mgr,hiredate,sal,comm,deptno)
		VALUES((START+i),rand_string(6),'SALESMAN',0001,CURDATE(),2000,400,rand_num());
UNTIL i = max_num
END REPEAT;
COMMIT;
END

--------------------------------------------------------------------------------------------
CREATE PROCEDURE `insert_dept`(IN START int,IN max_num int)
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO dept(deptno,dname,loc) VALUES((START+i),rand_string(10),rand_string(8));
UNTIL i = max_num
END REPEAT;
COMMIT;
END
DELIMITER;
CALL insert_dept(100,10);

show VARIABLES like '%sql_mode'

调用存储过程

select * from dept;
CALL insert_dept(100001,500000);

select * from emp;
CALL insert_emp(100001,200000);

show VARIABLES like 'profiling';
set profiling = on;

查看执行sql的时间

show PROFILES;
show STATUS
select * from emp GROUP BY id%10 limit 500000

诊断sql

show PROFILE cpu,block io for QUERY 111;
select * from mylock for update;
show OPEN tables; -- 查看是否上锁了
lock table mylock read, book write;
UNLOCK TABLES;
-- 获得表mylock的READ锁定
lock table mylock read;
-- 当前session可以查询该表记录
select * from mylock;
-- 当前session不能查询其他没有锁定的表
select * from book;
-- 当前session中插入或者更新锁定的表都会提示错误
select * from mylock;
insert into mylock(name) values('e');



-- 其他session插入后者更新锁定表会一直等待获得锁
insert into mylock(name) vlaues('e');
-- 其他session也可以查询该表的记录
select * from mylock;
-- 其他session可以查询或者更新未锁定的表
update staffs set name = '22' where id = 1;
update mylock set name='a2' where id =1;

lock table mylock write;

MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行增删改操作前,

会自动给涉及的表加写锁.
Mysql的表级锁有两种模式:
表共享读锁(Table Read Lock)
表独占写锁(Table Write Lock)

对MyISAM表进行操作,会有以下情况:
1.对MyISAM表的读操作(加读锁),不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求.
只有当读锁释放之后才会执行其他进程的写操作,
2.对MyISAM表的写操作(加写锁),会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其他进程的读写操作
简而言之,就是读锁会阻塞写,但是不会堵塞读,而写锁则会把读和写都堵塞.


如何分析表锁定
可以通过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定
SQL: show status like ‘table%’;
这里有两个状态变量记录Mysql内部表级锁定的情况,两个变量说明如下:
Table_locks_immediate: 产生表级锁定的次数,表示可以立即获取锁的查询次数,每立即获取锁值加1
Table_locks_waited:出现表级锁定争用而发生等待的次数(不能立即获取锁的次数,每等待一次锁值加1),
此值高说明存在着较严重的表级锁争用情况.

此外,MyISAM的读写锁调度是写优先的,这也会MyISAM不适合做写为主表的引擎,
因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远堵塞


复习:
事务是由一组SQL语句组成的逻辑处理单元,事务具有以下4个属性,通常简称为事务的ACID属性
1.原子性(Atomicity):事务是一个原子操作单元,其对数据的修改,要么全部执行,要么全都不执行.
2.一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态,这意味着所有相关的数据规则都必须应用于事务的修改,
以保持数据的完整性;事务结束时,所有的内部数据结构(如B树索引或双向链表)也都必须是正确的.
3.隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的"独立"环境执行,这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然
4.持久性(Durable):事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能保持


并发事务处理带来的问题:

1.更新丢失(Lost Update)
2.脏读(Dirty Reads)
3.不可重复读(Non-Repeatable Reads)
4.幻读(Phantom Reads)

更新丢失:

当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题–最后的更新覆盖了有其他事务所做的更新.
例如,两个程序员修改同一java文件,每程序员独立更改其副本,然后保存更改后的副本,这样就覆盖了原始文档,最后保存其更改副本的编辑人员覆盖前一个程序员所做的更改,
如果在一个程序员完成并提交事务之前,另一个程序员不能访问同一文件,则可避免此问题.


脏读 :

一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致的状态,这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些"脏"数据,
并根据此做进一步的处理,就会产生为提交的数据依赖关系.这种现象被形象的叫做脏读. 一句话:
事务A读到事务B已修改但尚未提交的数据,还在这个数据基础上做了操作,此时,如果事务B回滚,A读取的数据无效,不符合一致性要求


不可重复读:

一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变,或某些记录已经被删除了!这种现象叫做"不可重复读".
一句话 : 事务A读到了事务B已经提交的修改数据, 不符合隔离性


幻读:

一句话:事务A读取到了事务B提交的新增数据,不符合隔离性

幻读和脏读有点类似,
脏读是事务B里面修改了数据,
幻读是事务 B里面新增了数据


--查看当前数据库的事务隔离级别 : 
show variables LIKE 'tx_isolation';
show variables like 'transaction_isolation'\G;
create table test_innodb_lock (a int(11), b varchar(16))ENGINE=innodb;

什么是间隙锁

当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据的索引项加锁,
对于键值在条件范围内但并不存在的记录,叫做"间隙(GAP)",
InnoDB也会对这个"间隙"加锁,这种锁机制就是所谓的间隙锁(Next-Key锁). 危害:
因为Query执行过程中通过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个键值并不存在.
间隙锁有一个比较致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值也会被无辜锁定,
而造成在锁定的时候无法插入锁定键值范围内的任何数据,在某些场景下这可能会对性能造成很大的伤害.


面试题: 如何锁一行?

BEGIN;
	select...for update;
commit;

结论:

Innodb存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会要更高一些,
但是在整体并发处理能力方面要远远优于MyISAM的表级锁定,当系统并发量较高的时候,Innodb的整体性能和MyISAM相比就会
有比较明显的优势了.
但是,Innodb的行级锁定同样也有其脆弱的一面,当我们使用不当的时候,可能会让Innodb的整体性能表现不仅仅不能比MyISAM高,甚至
可能会更差.


show status like 'innodb_row_lock%';
--对各个状态量的说明如下:
Innodb_row_lock_current_waits: 当前正在等待锁定的数量;
Innodb_row_lock_time:	从系统启动到现在锁定总时间长度;
Innodb_row_lock_time_aavg:	每次等待所花平均时间;
Innodb_row_lock_time_max:		从系统启动到现在等待最长的一次所花的时间;
Innodb_row_lock_waits:系统启动后到现在总共等待的次数;
对于这五个状态量,比较重要的主要是
	Innodb_row_lock_time_avg(等待平均时长),
	Innodb_row_lock_waits(等待总次数)
	Innodb_row_lock_time(等待总时长)这三项.
尤其是当等待次数很高,而且每次等待时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待
,然后根据分析结果着手制定优化计划

优化建议:
1.尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁.
2.合理设计索引,尽量缩小锁的范围.
3.尽可能较少检索条件,避免间隙锁
4.尽量控制事务大小,减少锁定资源量和时间长度
5.尽可能低级别事务隔离

![在这里插入图片描述](https://img-blog.csdnimg.cn/20191114145330768.png