以下是最近学习MySQL的一些笔记,推荐一起阅读:
MySQL逻辑架构介绍
MySQL性能分析
MySQL索引优化
MySQL查询截取分析
MySQL锁机制
MySQL主从赋值
按照数据操作粒度划分:表锁/行锁
对数据操作的类型划分:读锁/写锁
读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会相互影响
写锁(排他锁):当前写操作没有完成前,阻断其他写锁和读锁
特点:偏向MyISAM引擎,开销小,加锁快;无死锁;锁定粒度大,发成锁冲突的概率高,并发度低。
偏向于读取
####使用表级锁
表级读锁(使用MyISAM引擎的表,因为MyISAM是表级锁):
lock table my_table read;
unlock tables;
锁定后:
表级写锁
lock table my_table write;
unlock tables;
锁定后:
MyISAM在读操作是会自动给表加读锁,在写操作时会自动给表加写锁
查看哪些表被加锁
show open tables;
查看锁争用情况
show status like 'table%';
MyISAM对锁的调度是写优先,因为写锁会同时阻塞表的读写,所以最好不要用于写多的场景,因此MyISAM偏向于读取
特点:偏向InnoDB引擎,开销大,加锁慢;会出现死锁;锁定粒度小,发成锁冲突的概率低,并发度高
InnoDB相比于MyISAM的不同:事务、行级锁
偏向于写入
脏读的重点是读另一个事务未提交的数据(假若那个事务RollBack, 则这数据就是无效的):某个事务已更新一份数据, 另一个事务在此时读取了同一份数据, 由于某些原因, 前一个RollBack了操作, 则后一个事务所读取的数据就会是不正确的
不可重复读的重点是修改: 同样的条件, 你读取过的数据, 再次读取出来发现值不一样了
幻读的重点在于新增或者删除: 同样的条件, 第1次和第2次读出来的记录数不一样
https://www.cnblogs.com/JohnABC/p/3521061.html
隔离级别 | 数据一致性级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
读未提交 | 最低级别,只能保证不读取物理损坏的数据 | YES | YES | YES |
读已提交 | 语句级 | NO | YES | YES |
可重复读 | 事务级 | NO | NO | YES |
可序列化 | 最高级别,事务级 | NO | NO | NO |
事务隔离越严格,并发度越小,消耗性能越高
MySQL默认事务隔离级别:可重复读(Repeatable read)
查看当前事务隔离级别
show variables like '%tx_isolation%';
select @@tx_isolation;
设置事务隔离级别
set [SESSION|GLOBAL] transaction isolation level read uncommitted;
set [SESSION|GLOBAL] transaction isolation level read committed;
set [SESSION|GLOBAL] transaction isolation level repeatable read;
set [SESSION|GLOBAL] transaction isolation level serializable;
如果使用了InnoDB引擎的表,就默认使用行级锁
在行级锁中:
由于Mysql 的行锁是通过索引实现的,如果遇到索引列失效的情况,则行锁会升级为表锁,从而造成修改其他行的事务阻塞
当 Where 查询条件中的字段没有索引时,更新操作会锁住全表
首先两个客户端均关闭autocommit
set autocommit=0;
假设有表user如下:
现在user表的username字段没有加索引,当username作为where中判断字段的时候,会触发全表扫描:
此时如果进行事务A,并且使用带where条件为username的更新update操作:
此时如果事务B想要修改同表中的另一行,按照行锁理论不会阻塞,实际会发生阻塞:
只有当事务A commit之后才,事务B才会提交(如果有等待过期时间,则事务B的update会取消),此时发生了行锁升级为表锁的现象。
现在演示索引失效导致行锁升级为表锁的情况:
首先给username添加索引index_username
alter table user add index index_username(username);
客户端A进行事务操作,修改记录1:
此时客户端B想要修改记录4,按照行锁理论,双方应该互不影响,但是此时客户端B产生阻塞:
此时即为索引失效导致了行锁升级为表锁的情况,只有在事务A结束之后事务B才能进行。
例如使用下面的SQL进行范围内更新:
update user set password='123' where id > 1 and id < 5;
则预期更新id=2,3,4号一共3条记录,但是此时表内容如下:
可以看到,3号id的记录缺失,出现了”间隙“
假设事务A执行上面的update更新操作:
事务B此时恰巧正在执行插入id=3的记录的操作,则会造成事务B也被阻塞,即使事务B是无辜的:
只有在事务A结束或者事务B超时之后才会解除阻塞,这就是间隙锁的情况
假设想要人为进行一些改动更新,并且此时有其他事务想要对这一行进行操作,如何把这一行锁定,从而保证人工操作进行顺利呢?
答案是使用for update的方式:
begin;
select * from table_name where id=? for update;
...
commit;
这样就锁定了id=?这一行,在提交commit之前,其他人想要对这一行进行修改都会阻塞
show status like 'innodb_row_lock%';
各参数含义:
加粗参数比较重要,越小越好;如果出现了异常,则可以使用show profile来进行检查
开销和加锁时间介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般