锁:
锁是数据库系统与文件系统的区别的一个关键特性,锁机制用于管理对共享资源的并发访问。
latch:闩锁(轻量级锁),要求锁定的时间必须非常短。分为(互斥锁,读写锁)对象是线程。
lock:对象是事务,用来锁定数据库中的对象,如表,行,页分为(共享锁,排他锁)
Lock:
共享锁S(读锁):允许事务读取一行数据,select 不加锁
对于普通 SELECT 语句,InnoDB 不会加任何锁 将查找到的数据加上一个S锁,允许其他事务继续获取这些记录的S锁,不能获取这些记录的X锁(会阻塞) 将查找到的数据加上一个X锁,不允许其他事务获取这些记录的S锁和X锁。
排他锁X(写锁):允许事务删除或更新一行数据 ,加锁select * from table for update;不允许其他事务获取记录的X锁
DELETE:删除一条数据时,先对记录加X锁,再执行删除操作。
INSERT:插入一条记录时,会先加隐式锁 隐式锁来保护这条新插入的记录在本事务提交前不被别的事务访问到。
UPDATE :如果被更新的列,修改前后没有导致存储空间变化,那么会先给记录加X锁,再直接 对记录进行修改。
如果被更新的列,修改前后导致存储空间发生了变化,那么会先给记录加X锁,然后将记录删掉,再Insert一条新记录。
测试:
SELECT VERSION();
SHOW INDEX FROM mycity;
表结构:
CREATE TABLE `mycity` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `tname` varchar(50) DEFAULT NULL, `state` varchar(128) DEFAULT NULL, `country` varchar(128) DEFAULT NULL, `hash` varchar(128) DEFAULT NULL, `is_deleted` varchar(10) DEFAULT NULL, `version` int(6) DEFAULT NULL, PRIMARY KEY (`id`), KEY `IDX_Name` (`tname`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
本地连接mysql
mysql-5.6.30-winx64in>mysql -u root -p
事务隔离级别:
SELECT @@tx_isolation;
update:
session1:
session2:
update 加 X锁,其他线程可以select ,但是insert ,delete,update 都会被阻塞
session1 commit:
session2 delete执行成功
insert:
session1:
session2:
insert 加隐式锁,隐式锁:一个事务插入一条记录后,还未提交,这条记录会保存本次事务id,而其他事务如果想来读取这个记
录会发现事务id不对应,所以相当于在插入一条记录时,隐式的给这条记录加了一把隐式锁。
不明白开始---
-- 查看 GAP 锁状态
SHOW VARIABLES LIKE 'innodb_locks_unsafe_for_binlog';
-- 查看自增锁 模式
SHOW VARIABLES LIKE 'innodb_autoinc_lock_mode';
0、1、2,分别对应”传统模式”, “连续模式”, “交错模式”。
传统模式:涉及auto-increment列的插入语句加的表级AUTO-INC锁,只有插入执行结束后才会释放锁。这是一种兼容MySQL 5.1之前版本的策略。
连续模式:可以事先确定插入行数的语句(包括单行和多行插入),分配连续的确定的auto-increment值;对于插入行数不确定的插入语句,仍加表锁。这种模式下,事务回滚,auto-increment值不会回滚,换句话说,自增列内容会不连续。
交错模式:同一时刻多条SQL语句产生交错的auto-increment值。
end ---?
delete:
session1:
session2:
delete 加X锁,只能select 不能insert update ,delete
select .. from for update
session 1
session2:
select * from update 表锁
select .. from where id =X for update 行锁 where 条件为聚集索引
session1:
session2:
唯一索引 where for update 行锁
CREATE UNIQUE INDEX unidx_state ON mycity(state);
session2:
普通索引 for update 行锁(符合条件的记录都会加锁)
CREATE INDEX idx_country ON mycity(country);
session1:
session2
country=‘北京’的有两列 普通索引 会锁定符合条件的列
总结:查询使用的是普通索引时,会对满足条件的索引记录都加上锁,同时对这些索引记录对应的聚集索引上的项也加锁?。
无索引for update 表锁:
session1:
session2:
查询的时候没有走索引,会对表中所有的记录以及间隙加锁。
意向锁:事务希望在更细粒度上进行加锁操作。
意向共享锁IS:事务想要获取一张表中某几行的共享锁。
意向排他锁IX:事务想要获取一张表中某几行的排他锁。
行锁:
行锁的算法:
Record Lock:单个记录上的锁
Gap Lock:间隙锁,锁定一个范围,但不锁定记录本身。为了防止同一事务的两次当前读,出现幻读的情况。
间隙锁(LOCK_GAP,LOCK_ORDINARY)
LOCK_GAP比如 1,2,4(2-4就是之间就是间隙锁 锁中间的资源)锁定一个范围,但不包含记录本身,是为了防止同一事务的两次当前读,出现幻读的情况。
Next-Key Lock:锁定一个范围,包含锁定记录本身,解决幻读
LOCK_ORDINARY:锁定一个范围,并且锁定记录本身,对于行的查询,都是采取改方法,主要目的是解决幻读的问题。
锁带来的问题:
1 脏读:脏页(缓冲池中已经被修改的页,还没刷新到磁盘中,数据库实例内存中的页和磁盘上页的数据不一致),脏读:事务对缓冲池中行记录的修改,并且还没有提前,被其他事务读取到称为脏读
2 不可重复度:在一个事务内多次读取同一个集合,A事务还没有提交,B事务对数据做了DML,因此在第一个事务A的两次读取中数据不一致
3 丢失更新:A更新未提交,B也更新,最后A提交,B提交,A提交的数据被覆盖(是数据库的任何隔离级别都会会出现丢失,所有需要在应用层面加锁)
悲观锁
悲观锁用的就是数据库的行锁,认为数据库会发生并发冲突,直接上来就把数据锁住,其他事务不能修改,直至 提交了当前事务。
乐观锁
var localVersion=select version from tableA where code='123';
//localVersion=1
线程A set money=money-10,version=localVersion+1 where version=localVersion and code='123';
线程B set money=money-10,version=localVersion+1 where version=localVersion and code='123' ;
线程A和B同时只能有一个更新成功,当线程A更新成功 version=2 ,线程B where条件不成立,更新失败
另外 money=money-10 等于原子操作
读锁(将查找的数据加上一个S锁,允许其他事务继续获取这些记录的S锁,不能获取这些记录的X锁 会阻塞)
共享锁: 一个线程在持有锁时,其他的线程可以查询被锁的数据,但是不能修改,不能删除。实现方式
SELECT * FROM mycity LOCK IN SHARE MODE
其他事务只能 加读锁 即 只能 执行select 语句 ,没法执行写锁 select * for update(写锁)
排他锁:一个线程在持有锁时,其他的线程不能查询,不能更新,不能删除被锁的数据,直到锁被释放.
SELECT * FROM mycity FOR UPDATE