mysql索引优化和锁
IO操作与索引
IO操作上数据库性能的瓶颈之一,每次进行IO操作都需要消耗时间和资源。
核心:尽量减少 IO 操作的次数
读取次数少且读取量少是优化IO操作的核心目标。采用分块读取和局部性原理。
- 分块读取:将磁盘上的数据划分为若干块,每次读取一块数据,减少了单次IO操作的数据量。好处是,如果我们只需要重新某个块中的时间就不需要读取整个表或索引的时间,从而减少IO操作次数
- 局部性原理:是指着某一次IO操作中,很有可能会连续读取到相邻的数据块。这是因为数据库索引的数据通常是按照一定的顺序存储在磁盘上的。当我们查询某个索引时,由于数据的有序性,磁盘预读机制帮助我们预先将相邻的数据块读入内存,提高查询效率。
索引失效原因
常见失效情况:
- **条件不符合的索引使用:**如果查询条件不符合索引的定义,数据库无法使用索引进行定位,会导致索引失效。例如,如果我们在一个整型字段上建立了索引,查询条件中使用了字符串比较,索引就无法发挥作用。
- 使用函数或运算符:在查询条件中使用函数或运算符可能导致索引失效。因为数据库无法再索引树中执行这些函数或运算符操作,所以无法使用索引进行定位。
- 数据发布不均匀:如果数据分布不均匀,即没些值的重复率非常高,索引的选择性就会降低,导致索引失败。这种情况数据库可能选择的是全表扫码不是索引。
- 索引列参与计算:如果索引参与计算操作,比如加减乘除运算,索引也会失效。索引树是无法直接进行这些操作。
索引优化
1、如何索引查看
查看一条SQL的 SELECT的查询计划,可使用:
EXPLAIN SELECT * FROM table_name WHEREcondition;
返回一张表格,包含了查询计划的详细信息:
2、关键数据解析
- **id:**每个操作步骤在执行计划中都有一个唯一的id,按照从大到小的顺序递减。id越大,执行的优先级越高
- select_type: 表示每个操作步骤的类型,常见的类型有SIMPLE、PRIMARY、SUBQUERY、DERIVED等。不同的联系都应不同的查询操作方式
序号 | 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 |
7 | DEPENDENT SUBQUERY(相关子查询) | 在SELECT或WHERE列表中包含了子查询,子查询基于外层 |
8 | UNCACHEABLE SUBQUREY | 无法被缓存的子查询 |
-
**table:**表示重新操作所涉及到表名
-
**partitions:**如果查询涉及到分区表,该字段表示实际访问的分区数
-
**type:**表示MySQL这执行时所使用的访问方式,常见的类型有ALL、index、range、ref等。一般来说,type的值越小越好
-
**possible_keys:**表示在执行查询时可能使用到的索引
-
**key:**表示MySQL实际选择使用的索引
-
**Key_len:**表示MySQL这声音索引时索引的长度,单位数字节。一般来说,key_len 越小越好
-
**ref:**表示MySQL在使用索引进行查询时所使用的列或常量
-
**rows:**表示MySQL在执行查询时估计需要扫描的行数
-
**filtered:**表示MySQL在执行查询后过滤的行数所占的比例,范围是0-100。一般说,filtered的值越小越好
-
**Extra:**表示MySQL在执行查询时的额外信息,常见的信息也Using where、Using index等
3、如果优化索引查询计划
常见优化方法:
- **索引优化:**关注possible_keys、key和key_len字段,确保MySQL选择了合适的索引,并且索引长度够短
- **表关联优化:**关注type字段,尽量避免使用全表扫描(type为ALL),考虑添加或优化查询条件
- **子查询优化:**关注select_type字段,尽量避免使用子查询,可以考虑索引JOIN操作替代
- **分区表优化:**关注partitions字段,确保查询操作尽量只访问必要分区,避免全表扫描
- **SQL语句优化:**关注rows(行)字段和filtered(过滤)字段,尽量减少扫描的行数和过滤的行数,可以通过优化查询条件。添加合适的索引等方式实现
不同类型的锁全面解析
常见锁的类型分两种:乐观锁、悲观锁
1、乐观锁
- 如果事务冲突很少发生,不会主动加锁。
- 实现方式通常使用的:数据版本号和时间戳
- MySQL常见的乐观锁实现方式是版本号
- 先检无误提交,有误进行回滚或重试
乐观锁优势减少锁竞争,提高并发性能,缺点:增加冲突检测和处理的复杂性
2、悲观锁
一种并发控制策略。如果事务冲突经常发生,因此主动采取锁来保证事务的安全性。
悲观锁分两种 行锁 和 表锁 类型
锁的粒度 细粗程度:行锁 > 表锁
行锁
行锁是针对数据表中行的记录进行加锁的机制。实现更细粒度的并发控制
-
**共享锁(Shared Lock):**允许多个事务读取同一行数据,不能修改,电商项目读取库存,确保不会产生冲突
-
**排他锁(Exclusive Lock):**是最严格锁类型,控制读写操作。一个事务持有排他锁时,其他事务无法进行读写操作,电商项目使用排他锁进行更新操作,确保只有一个用户可以使用修改库存数据,避免并发写入导致数据不一致的问题
-
**记录锁(Record Lock):**是行锁的一种特殊形式,它是对数据表中某个记录进行加锁。记录锁只允许一个事务持有,其他事务无法读取和修改该记录。在电商项目中,记录锁可以用于订单表的加锁操作,保证每个订单只能被一个事务处理
-
**间隙锁(Gap Lock):**是行锁的一种特殊形式,它是对数据表中某个范围的间隙进行加锁。间隙锁的作用上防止其他事务在锁定的范围内插入新的记录。在电商项目中,间隙锁可以用于商品库存的范围查询操作,防止其他事务在查询过程中插入新的库存记录
-
临键锁(Next-Key Lock): 它是对一个键的范围进行加锁。临键锁的作用上防止其他事务在范围内插入新的记录或修改现有的记录。在电商项目中,临键锁可以用于商品的售卖操作,保证在购买商品时其他事务无法修改商品信息
表锁
对整个表进行加锁的机制。锁的粒度比行锁的大,控制并发的能力相当较菜
- **意向锁(Intention Lock)**是表锁的一种特殊形式,用于指示事务将对数据表中的某个行进行加锁。当一个事务持有行锁时,它必须首先获取意向锁。在电商项目中,意向锁可以用于商品订单表的加锁操作,表示事务将对该表进行 行级锁定
- **自增锁(Auto-Increment Lock)**它用用于控制对自增字段的访问。在自增锁模式下,只有一个事务可以递增自增字段的值。在电商项目中,自增锁可以用于订单号的生成操作,确保每个事务生成的订单号不会重复
锁模式的含义
- IX是意向排它锁(Intention Exclusive Lock)的缩写。它表示一个事务对某个表的某个行记录或范围进行排他锁操作。当事务要对某个行记录加排它锁时,必须先获取意向排它锁
- **X表示排它锁(Exclusive Lock)**它表示一个事务对某个表的某行记录进行排它锁操作。当事务要修改某个行记录时,必须先获取排它锁
- X,REC_NOT_GAP表示排它锁和不在间隙中的记录锁。它用于对某个表的行进行加锁操作,并且不会锁间隙。
- X,GAP表示排它锁和间隙锁。它用于对个某个表的间隙进行加锁操作,以防止其他事务在该间隙中插入新的记录
总结
MySQL的乐观锁和悲观锁是并发控制重要手段,用于保证数据的一致性。悲观锁通过加锁的方式控制对数据的访问,保证同一时间只有一个事务能够对数据进行修改。乐观锁则通过版本号和时间戳的方式进行冲突检测,避免数据的覆盖和冲突。还有各种行锁和表锁的类型和模式,用于精细控制对数据的访问权限
个人总结:
原作者:做架构师不做框架师 https://www.bilibili.com/read/cv24393734/?from=readlist 出处:bilibili