14.5.2.4 Locking Reads 锁定读: 如果你查询数据然后插入或者修改相关数据在相同的事务里, 常规的SELECT 语句不能给予足够的保护。 其他事务可以修改或者删除你刚查询相同的记录,InnoDB 支持2个锁定读的类型提供额外的安全: /******************* 测试MYSQL RR 的重复读: CREATE TABLE `t3` ( `sn` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增编号', `phoneNo` int(11) DEFAULT NULL, PRIMARY KEY (`sn`)); mysql> show create table t3; +------- +-------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------+ | Table | Create Table | +------- +-------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------+ | t3 | CREATE TABLE `t3` ( `sn` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增编号', `phoneNo` int(11) DEFAULT NULL, PRIMARY KEY (`sn`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 | +------- +-------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------+ 1 row in set (0.00 sec) Session 1: mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from t3; +----+---------+ | sn | phoneNo | +----+---------+ | 1 | 11 | | 2 | 22 | +----+---------+ 2 rows in set (0.00 sec) mysql> select * from t3; +----+---------+ | sn | phoneNo | +----+---------+ | 1 | 11 | | 2 | 22 | +----+---------+ 2 rows in set (0.00 sec) Session 2: Database changed mysql> delete from t3 where sn=2; Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) 此时session 1看到的还是2条 表明RR是重复读 1.SELECT ... LOCK IN SHARE MODE 设置一个共享锁锁定在任何你要读的记录,其他session 只能读取记录, 因为不能修改它们直到你的事务提交。 如果任何其他记录被其他事务改变 没有被提交,你的查询等待直到那个事务借宿 然后使用最新的数据 mysql> select * from t3; +----+---------+ | sn | phoneNo | +----+---------+ | 1 | 11 | | 2 | 22 | +----+---------+ 2 rows in set (0.00 sec) mysql> select * from t3 LOCK IN SHARE MODE; +----+---------+ | sn | phoneNo | +----+---------+ | 1 | 11 | | 2 | 99 | +----+---------+ 2 rows in set (4.78 sec) 看到的是最新的数据: mysql> select * from t3; +----+---------+ | sn | phoneNo | +----+---------+ | 1 | 11 | | 2 | 22 | +----+---------+ 2 rows in set (0.00 sec) mysql> select * from t3 LOCK IN SHARE MODE; +----+---------+ | sn | phoneNo | +----+---------+ | 1 | 11 | | 2 | 99 | +----+---------+ 2 rows in set (0.00 sec) 2. mysql> select * from t3 where sn=2 for update --看到的也是最新的记录 -> ; +----+---------+ | sn | phoneNo | +----+---------+ | 2 | 99 | +----+---------+ 1 row in set (4.26 sec) mysql> mysql> select * from t3; +----+---------+ | sn | phoneNo | +----+---------+ | 1 | 11 | | 2 | 22 | +----+---------+ 2 rows in set (0.00 sec) 对于所有搜索遇到, SELECT ... FOR UPDATE locks 锁定记录和任何相关的index entries, 相当于你执行一个update语句对于那些记录。 其他事务是被堵塞的来更新那些记录,做 SELECT ... LOCK IN SHARE MODE, 或者从读取的数据在某些事务隔离级别。 一直读忽略任何锁设置在记录上(老的记录不能被锁定,它们是应用undo log重构的) 当 LOCK IN SHARE MODE and FOR UPDATE 查询的索引锁是被释放放事务是被提交或者回滚 锁定记录用于更新使用SELECT FOR UPDATE 只使用与当autocommit 被关闭(或者开始事务使用START TRANSACTION or by setting autocommit to 0. 如果自动提交启用,匹配的记录不会被锁定) 使用例子: 假设你需要插入一条新的记录到一个表child,确保child记录有一个parent 记录 在表parent里。 你的应用代码可以确保完整性 首先, 使用一个一致性读来查询表PARENT 校验parent记录的存在, 你可以安全的插入child 记录到表CHILD? 不行,因为一些其他会话可以删除父表记录在你查询和你插入,不会要你知道 为了避免这个潜在的问题,执行SELECT 使用LOCK IN SHARE MODE: SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE; 在LOCK IN SHARE MODE 查询返回parent 'Jones',你可以安全的增加child 记录到CHILD 表 然后提交事务。 任何事务尝试获得一个排它锁在可应用的记录在PARENT 表等待直到你完成为止, 也就是说直到数据在所有的表是一个一致性的状态 另外一个例子, 考虑一个整数计数字段在表CHILD_CODES, 用于分配一个唯一的表示到每个表被增加到表CHILD。 不会使用一致性读或者一个共享模式读来读取当前值 在这里,LOCK IN SHARE MODE 不是一个好的解决方法 因为如果2个用户读取counter 在相同时间, 实现读和增量计数器,首先执行一个counter的锁定读 使用FOR UPDATE, 然后增加counter SELECT counter_field FROM child_codes FOR UPDATE; UPDATE child_codes SET counter_field = counter_field + 1; 一个SELECT ... FOR UPDATE 读取最新的可用的记录, 设置排它锁在它读取的每行。