MySQL_索引、锁、事务特性、事务隔离级别

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_43251291/article/details/102601357

一、为什么要使用索引

二、什么样的信息能成为索引

三、索引的数据结构

四、密集索引和稀疏索引的区别

索引

1.如何定位并优化慢查询

Ⅰ.根据慢日志定位慢查询SQL

<1> 展示慢日志设置命令

show variables like '%quer%';

在这里插入图片描述
<2> slow_query_log: 慢日志, 将OFF改为ON

set global slow_query_log = on;

<3> long_query_time: 每次SQL执行10秒已经超级慢了, 改成1s, 1s即需优化

set global long_query_time = 1;

slow_query_log_file: 慢日志的全路径
<4> 查询系统的状态

show status like '%slow_queries';

在这里插入图片描述
<5> 关联数据库数据

select count(id) from person_info_large;
select name from person_info_large order by name desc;(数量过大会记录到慢查询日志文件)

打开慢日志

sudo  vim /usr/local/mysql/data/baidudeMacBook-Pro-slow.log

在这里插入图片描述

Ⅱ.使用explain等工具分析SQL

explain select name from person_info_large order by name desc;

分析显示如下
在这里插入图片描述
type
system>const>eq_ref>ref>fulltext>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>all
出现黄色标记的就需要优化,因为进行了全表扫描
extra
在这里插入图片描述

Ⅲ.修改SQL或者尽量让SQL走索引

在这里插入图片描述

explain select name from person_info_large order by name desc;
explain select account from person_info_large order by account desc;

如下图type extra已改善, key:account
在这里插入图片描述

select account from person_info_large order by account desc;

如果一定要按照名字来查询, 就在name上面加上索引, 这句是数据定义语言, 不会写入慢查询日志

alter table person_info_large add index idx_name(name);

在这里插入图片描述

explain select count(id) form person_info_large;

(默认唯一键, mysql尽可能使用最严格的的索引)
在这里插入图片描述

explain select count(id) form person_info_large force index (primary);

(强制走主键索引)
在这里插入图片描述

2.联合索引的最左匹配原则的成因

联合索引: 就是由多列组合的索引
在这里插入图片描述

explain select * form person_info_large where area = 'EJZszkSK' and title = 'ABdfGF' ;

(按照左边索引area查询)
在这里插入图片描述

explain select * form person_info_large where area = 'EJZszkSK';

(按照左边area索引查询)
在这里插入图片描述

explain select * form person_info_large where title = 'ABdfGF' ;

(全表查询,没有找到左侧索引)
在这里插入图片描述
最左匹配原则
1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配, 比如a = 3 and b = 4 and c > 5 and d = 6 如果建立(a, b, c, d)顺序的索引, d 是用不到索引的,如果建立(a, b, d, c)的索引则都是可以用到, a, b, d的顺序可以任意调整.
2.=和in可以乱序, 比如a = 1 and b = 2 and c = 3 建立(a, b, c)索引可以任意顺序, mysql的查询优化器会帮你优化成索引可以识别的实行

3.索引是建立的越多越好吗

数据量小的表不需要建立索引, 建立会增加额外的索引开销
数据变更需要维护索引,因此更多的索引意味着更多的维护
更多的索引意味着也需要更多的空间

锁模块常见问题

1.MyISAM与InNoDB关于锁方面的区别是什么

MyISAM默认用的是表级锁, 不支持行级锁
InnoDB默认用的是行级锁, 也支持表级锁
在这里插入图片描述
在这里插入图片描述
备注:一个tap表示一个新的session
MyISAM
session1:在这里插入图片描述
session2:
在这里插入图片描述
同时执行两个session, 结果session1执行之后,由于数据量大, 建立表锁, block其他的表进行更新, 执行finish才会执行session2的更新
MyISAM结论:
当数据被select的时候, 自动加上一个表级的读锁, 当对数据进行增删改的时候, 会自动加一个表级别的写锁, 当读锁未被释放的时候, 另一个session相对同一张表加一个写锁, 就会被阻塞, 直到所有的读锁被释放为止.
如何显示给表加上读锁呢?
lock tables person_info_MyISAM read | write;
释放读锁:
unlock tables;
读锁也叫共享锁, 写锁也叫排他锁
InnoDB
默认自动提交事务, 用的是2道锁, 加锁和解锁
下图设置仅支持当前session
在这里插入图片描述

共享锁和排斥锁的兼容性

在这里插入图片描述

行级锁和表级锁的利弊

密度越细代价越高,表级锁直接加到表头,行级锁要扫描到具体行, InnoDB主键,查询辅助索引需要先查询主键再查到数据, MyISAM, 数据和文件分开, 索引保存的是数据文件的指针, 主键索引和辅助索引是独立的, 少量的增删改

MyISAM适合的场景

频繁执行全表count语句
对数据进行增删改的频率不高, 查询非常频繁
没有事务

InnoDB适合的场景

数据增删改查都相当频繁(增删改的时候只是某些行被锁,在大多数情况下都避免了阻塞, 避免锁住整张表)
可靠性要求比较高, 要求支持事务

数据库锁的分类

类型 分类锁
粒度 表级锁、行级锁、页级锁
锁级别 共享锁、排他锁
加锁方式 自动锁、显示锁
操作 DML锁、DDL锁
使用方式 乐观锁和悲观锁

乐观锁基于数据版本version机制实现, 数据更新一次,version加一,下次修改前查看version
InnoDB
session1 提交之前检查版本, s2先执行, v=1,此时这里会查询报错
在这里插入图片描述
session2 这个先执行将v变成1
在这里插入图片描述

2.数据库事务的四大特性 ACID

原子性(Atomic)

要么全部执行,要么全部失败回滚

一致性(Consistency)

保持数据库从一个一致的状态转为另一个一致的状态
A:1000 B:1000 不管AB如何转账, 最后AB的总数一定是2000

隔离性(Isolation)

多个事务并发执行时, 一个事务的执行, 不应该影响其他事务的执行

持久性(Durability)

一个事务地提交应该永久的保存, 系统故障, 确保已经交的事务不能丢失和提交事物的恢复
InnoDB会将所有对页面的操作写入一个专门的文件(read do log file),并在数据库启动时,在此文件进行操作

3.事务隔离级别以及级别下的并发访问问题

事务并发访问引起的问题以及如何避免

更新丢失–mysql所有事务隔离级别在数据库层面上均可避免

在这里插入图片描述

脏读READ-UNCOMMITTED–READ-COMMITTED事务隔离级别以上可避免

备注: 对一行做操作

select @@tx_isolation;   # 查看事务隔离级别, 确认当前所有事务已经commit
select session transaction isolation level read uncommitted;   # 设置隔离级别
start transaction;    # 开启事务

step1
session1:

update account_innodb set balance = balance - 100 where id = 1;
select * form account_innodb where id = 1;

value: balance = balance(1000) - 100= 900 没有提交 未执行commit
session2:

select * form account_innodb where id = 1;

balance= 900 读到了session1未commit的数据
step2
session1:

rollback;   # 由于某种原因回滚, 结果值返回 1000

session2:

update account_innodb set balance = balance +200 where id = 1;

balance = 900 +200 -->1100 实际是应该是回滚的数据 1000 + 200 = 1200
脏读, 本事务读到其他事务未提交的数据

避免脏读:

select session transaction isolation level read committed;  # 设置隔离级别

不可重复读READ-COMMITTED – REPEATABLE-READ事务隔离级别以上可避免

备注: 对一行做操作

select @@tx_isolation;    # 查看事务隔离级别, 确认当前所有事务已经commit
select session transaction isolation level read committed;   # 设置隔离级别
start transaction;    # 开启事务

step1
session1:读

select * from account_innodb where id = 1;    # value=1300

session2:

update account_innodb set balance = balance + 300;
select * from account_innodb where id = 1; 
commit;

step2
session1:

select * from account_innodb where id = 1;         # value=1600

注意: session1事务开启未提交, 同一id的值没做任何更新操作,两次读取的结果不一样. 如果以第一次查询结果作增删变更, 会造成数据紊乱
避免不可重复读:

select session transaction isolation level repeatable read;  # 设置隔离级别

变更到此事务级别, 可以避免session1未提交的第二次读取, 读取不到其他事务update的数据,但是自己update commit是根据其他事务update commit之后的数据来更新的

幻读REPEATABLE-READ – SERIALIZABLE事务隔离级别以上可避免

备注: 对数据库所有记录(整表)作操作

select @@tx_isolation;   # 查看事务隔离级别, 确认当前所有事务已经commit
select session transaction isolation level repeatable read;  # 设置隔离级别
start transaction;   # 开启事务

step1
session1:

select * from account_innodb where id = 1;   # value = 1600
update account_innodb set balance = 1000;

session2:

select * from account_innodb where id = 1;   # value = 1600
update account_innodb set balance = balance + 400;
select * from account_innodb where id = 1;   # 未提交查询value = 2000

step2
session1:

select * from account_innodb where id = 1;  

value = 1600(不管step1 session2 commit与否, value都是1600)
可重复读,本次事务内,未提交,读的值都是一样的

update account_innodb set balance = balance - 100 where id = 1;   # value = 1900

update的值会读取step1 session2 update commit 后的值进行增删

step
session1: # 当前读获取整张表的数据, 当前读是读取的当前事务提交的最新数据

select * from account_innodb lock in share mode; (加一个共享锁, 显示三条数据)
update account_innodb set balance = 1000;

注意:当session2insert一条数据后,执行此操作, 会显示更新4条数据的结果,实际只有三条, 不加共享锁, 会更新成4条
session2:

insert into account_innodb values(4,"Newman",500);

InnoDB默认行锁,但是会等待session1执行操作完成才会执行session2

串行化SERIALIZABLE

备注: 对一行做操作

select @@tx_isolation;   # 查看事务隔离级别, 确认当前所有事务已经commit
select session transaction isolation level serializable;  # 设置隔离级别
start transaction;   # 开启事务

step
session1:

select * from account_innodb;

此事务级别所有的操作都会加上锁,即使不标注lock也会自动上锁

update account_innodb set balance = 1000;
commit;

session2:

insert into account_innodb values(5,"Oldman",600); 

语句被block,需要等待session1commit或者rollback才能执行, 避免幻读的发生,此时session1表内数据均已上锁

commit;

事务隔离级别性能表

事务隔离级别 更新丢失 脏读 不可重复读 幻读
READ-UNCOMMITTED 读未提交 v v v
READ-COMMITTED 读提交 v v
REPEATABLE-READ 重复读 v
SERIALIZABLE 串行化

Oracle默认: READ-COMMITTED
MySQL默认: REPEATABLE-READ
不可重复读和幻读看起来非常类似, 不可重复读侧重于对同一数据的修改,幻读侧重于新增或者删除

4.InnoDB可重复读隔离级别下如何避免幻读

表象: 快照读(非阻塞) --伪MVCC
内在: next-key锁(行锁+gap锁)
当前读和快照读
当前读: select…lock inshare mode, select…for update
当前读: update、delete、insert(不管是共享锁还是排他锁,都是当前读,因为读取的都是最新版本,并且保证其他并发事务不能修改当前记录,对读取的记录加锁)

5.RC、RR级别下的InnoDB的非阻塞读如何实现

数据行里的DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID字段
undo日志
read view

语法

关键语法

GROUP BY
满足"SELECT子句中的列名必须为分组列或列函数"
列函数对于group by子句定义的每组各返回一个结果
在这里插入图片描述
在这里插入图片描述
HAVING
通常与GROUP BY子句一起使用
WHERE过滤行, HAVING过滤组
出现在同一sql的顺序: WHERE>GROUP BY>HAVING
在这里插入图片描述
在这里插入图片描述
COUNT, SUM, MAX, MIN, AVG

猜你喜欢

转载自blog.csdn.net/weixin_43251291/article/details/102601357