表记录有两种读取方式。
快照读:读取的是快照版本。普通的SELECT就是快照读。通过mvcc来进行并发控制的,不用加锁。 当前读:读取的是最新版本。UPDATE、DELETE、INSERT、SELECT … LOCK IN SHARE MODE、SELECT … FOR UPDATE是当前读。 快照读情况下,InnoDB通过mvcc机制避免了幻读现象。而mvcc机制无法避免当前读情况下出现的幻读现象。因为当前读每次读取的都是最新数据,这时如果两次查询中间有其它事务插入数据,就会产生幻读。
下面举个例子说明下:
1、首先,user表只有两条记录, user_id user_name user_pwd 1 张三 123456 2 李四 123456 2、事务a和事务b同时开启事务start transaction; 3、事务a插入数据然后提交;
insert into user(user_name, user_password, user_mail, user_state) values('tyson', 'a', 'a', 0);
4、事务b执行全表的update;
update user set user_name = 'a';
5、事务b然后执行查询,查到了事务a中插入的数据。(下图左边是事务b,右边是事务a。事务开始之前只有两条记录,事务a插入一条数据之后,事务b查询出来是三条数据) 以上就是当前读出现的幻读现象。
MySQL是如何避免幻读?
在快照读情况下,MySQL通过mvcc来避免幻读。 在当前读情况下,MySQL通过next-key来避免幻读(加行锁和间隙锁来实现的)。 next-key包括两部分:行锁和间隙锁。行锁是加在索引上的锁,间隙锁是加在索引之间的。
Serializable隔离级别也可以避免幻读,会锁住整张表,并发性极低,一般不会使用。
共享锁和排他锁
SELECT 的读取锁定主要分为两种方式:共享锁和排他锁。
select * from table where id<6 lock in share mode;--共享锁
select * from table where id<6 for update;--排他锁
这两种方式主要的不同在于LOCK IN SHARE MODE多个事务同时更新同一个表单时很容易造成死锁。
申请排他锁的前提是,没有线程对该结果集的任何行数据使用排它锁或者共享锁,否则申请会受到阻塞。在进行事务操作时,MySQL会对查询结果集的每行数据添加排它锁,其他线程对这些数据的更改或删除操作会被阻塞(只能读操作),直到该语句的事务被commit语句或rollback语句结束为止。
SELECT... FOR UPDATE 使用注意事项:
- for update 仅适用于innodb,且必须在事务范围内才能生效。
- 根据主键进行查询,查询条件为like或者不等于,主键字段产生表锁。
- 根据非索引字段进行查询,会产生表锁。
评论