MySQL(七)之InnoDB存储引擎中的锁(未完待续)

锁:

锁是数据库系统与文件系统的区别的一个关键特性,锁机制用于管理对共享资源的并发访问。

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-winx64\bin>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不对应,所以相当于在插入一条记录时,隐式的给这条记录加了一把隐式锁。

 

 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:事务想要获取一张表中某几行的排他锁。 

 

意向锁是在添加行锁之前添加。

当再向一个表添加表级X锁的时候

  • 如果没有意向锁的话,则需要遍历所有整个表判断是否有行锁的存在,以免发生冲突
  • 如果有了意向锁,只需要判断该意向锁与即将添加的表级锁是否兼容即可。因为意向锁的存在代表了,有行级锁的存在或者即将有行级锁的存在。因而无需遍历整个表,即可获取结果

 

行锁:

行锁的算法:

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 

posted @ 2020-05-13 11:48  暖暖-木木  阅读(209)  评论(0编辑  收藏  举报