1.2.锁的持有周期
保证数据在并发下,保证对同一数据的修改正确性;
加锁:实际访问到某个待更新的行时,对其加锁(逐步加锁过程,并非一开始讲所有的锁一次性持有)
解锁:事务提交/回滚时或连接断开;
1.3.锁的力度
(1)数据库级别
(2)表级别
(3)页级别的锁
(4)行级别的锁
锁的力度越细,并发级别越高,实现也越难;
2.1.事务隔离级别
2.1.1.脏读:事务A对数据进行修改,事务B使用了该未被事务A提交的数据;
e.g.
演示:未出现脏读
//1.可以看见2边的窗口输出都是一样的;
//2.可以看见当事务未提交时,修改是无效的;
//3.事务提交
//查看修改mysql事务隔离级别
//show variables like ‘%isolation%’;
//set global transaction_isolation =‘read-uncommitted’;
//修改完成后记得退出再进来;
//演示:出现脏读
//1.设置存储引擎InnoDB级别未读未提交;
//2.重复上述
//3.可以看出,事务未提交但另一边数据却已经变化了;-》脏读
2.1.2.不可重复读:在一次事务中多次读取同一数据
//1.将InnoDB级别修改为read-commited,以演示不可重复读;
//可以看见,在同一事务中,多次读取的数据竟然不一样;
//2.修改事务隔离级别为可重复读,其可避免可可重复读
//可以看到同一事务中多次读取的结果是一样的;
2.1.3. 幻读:可以看出在同一事务中一张表的的数据并无太多变化;这是因为InnoDb引擎的可重读读事务隔离级别加上了MVCC以及Next-Key Locking来防止幻读;
未提交读:脏读,不可重复读,幻读
已提交读:不可重复读,幻读
可重复读:幻读
可串行化:
2.2.常见加锁操作
(1)insert, delete, update
(2)select … lock in share mode,
select … for update(显式加锁)
(3)alter table …/ Create index … on(DDL操作引入加锁)
(4)primary key, unique key唯一性约束检查(外键不提供技术支持)
(5)lock table… read/write. 显式加表级锁
(6)flush table with read lock;数据库级锁,
S:共享锁
X:排它锁
2.4.CRUD和锁的对照关系
(3)无索引字段的更新会去扫全表,逐步加锁
GAP锁:
实现可重复读,防止幻读的产生;
原则之二:为了减少GAP锁,减少GAP导致的死锁,尽可能选择Read Commited隔离级别;
适当减少Unique索引,能够减少GAP锁导致的死锁;高并发场景不建议使用唯一索引,即将数据库作为存储仓库,检查由软件完成;
事务越小越好,并发度越高;
复杂的事务场景,热点更新后置;
事务中,尽可能不做外部依赖调用,减少第三方调用,有的话,一定要设置尽量小的超时;如http, dubbo;
事务的MVCC
2.5.alter锁表
(1)alter添加索引不影响增删改查
(2)改编码阻塞增删改查
(3)长事务导致的锁表
(4)同时改表阻塞增删改查
mysql死锁
死锁产生:
1.多个并发事务
2.每个事务持有锁或排队等待锁
3.每个事务均需要再继续持有锁
4.事务间产生锁的循环等待,形成死锁;
死锁:请求与保持;循环等待;非剥夺;资源互斥;
//可以看出确实检测出了死锁;
e.g.
CREATE TABLE `sbtest4` (
`id` int NOT NULL AUTO_INCREMENT,
`k` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
e.g.
create table tt(
id int(11) not null auto_increment,
u int(11) not null default 0,
a int(11) default 0,
comments varchar(332) default null,
primary key (id),
unique key `uniq_u` (`u`),
key `idx_a` (`a`)
)engine =InnoDB auto_increment=8 default charset =utf8mb4;
// 可以看见当更新二级索引时死锁了;
e.g.
# gap死锁示例
create table t(
c1 int not null auto_increment,
c2 int not null default 0,
primary key (c1),
unique key uniq_c2(c2)
)engine=InnoDB,default charset =utf8mb4;
死锁和加锁顺序有关;
在mysql中,以不同的索引的过滤条件,来操作相同的记录(update,delete),很容易产生死锁;
RC隔离级别下,若出现死锁中出现Next Key(gap锁), 说明表中一定存在unique索引;
多语句事务产生的死锁,确保每条语句操作记录的顺序行,能极大地减少死锁;(如,批量更新前,先排序后更新);
(1)要分析死锁,必须深入业务,了解整个事务的逻辑,才能了解加锁过程;
(2)GAP很复杂,为了减少GAP锁,导致的死锁,尽可能使用read-committed隔离级别;
(3)适当地减少unique索引,能够减少gap锁导致的死锁;
(4)mysql中以不同索引过滤条件,来操作相同的记录,很容易产生死锁;
(5)RC级别下,若死锁中出现了Next Key(Gap锁),说明表中一定存在unique索引;
(6)多语句事务产生的死锁,确保每条语句记录顺序行,可极大减少死锁;
范式
1.第一范式
属性(列)不可分;
2.第二范式
属性依赖于主键 -> 分解;
3.第三范式
非主属性不依赖于其他非主属性;
docker解决不同操作系统下环境配置问题;