Mysql讲解数据库并发控制知识

1.下载Mysql并安装,我喜欢不用安装的zip版,cd到bin目录下,先修改下mysql的密码.

mysqladmin -u root -p password mysql ,第一次运行并修改mysql的密码为mysql,注册mysqld服务,mysqld -install

2.四种数据不一致性的问题

1)       丢失或覆盖更新(lost update)

当两个或多个事务选择同一数据,并且基于最初选定的值更新该数据时,会发生丢失更新问题。每个事务都不知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。上面预定飞机票的例子就属于这种并发问题。事务1与事务2先后读入同一数据A=16,事务1执行A-1,并将结果A=15写回,事务2执行A-1,并将结果A=15写回。事务2提交的结果覆盖了事务1对数据库的修改,从而使事务1对数据库的修改丢失了。
(2)    脏读
一个事务读取了另一个未提交的并行事务写的数据。当第二个事务选择其它事务正在更新的行时,会发生未确认的相关性问题。第二个事务正在读取的数据还没有确认并且可能由更新此行的事务所更改。换句话说,当事务1修改某一数据,并将其写回磁盘,事务2读取同一数据后,事务1由于某种原因被撤销,这时事务1已修改过的数据恢复原值,事务2读到的数据就与数据库中的数据不一致,是不正确的数据,称为脏读。
例如,在下图中,事务1将C值修改为200,事务2读到C为200,而事务1由于某种原因撤销,其修改作废,C恢复原值100,这时事务2读到的就是不正确的“脏“数据了。
(3)    不可重复读(nonrepeatable read)
一个事务重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务修改过。即事务1读取某一数据后,事务2对其做了修改,当事务1再次读数据时,得到的与第一次不同的值。
(4)    幻像读
如果一个事务在提交查询结果之前,另一个事务可以更改该结果,就会发生这种情况。这句话也可以这样解释,事务1按一定条件从数据库中读取某些数据记录后未提交查询结果,事务2删除了其中部分记录,事务1再次按相同条件读取数据时,发现某些记录神秘地消失了;或者事务1按一定条件从数据库中读取某些数据记录后未提交查询结果,事务2插入了一些记录,当事务1再次按相同条件读取数据时,发现多了一些记录。

3.数据库的隔离级别

===========================================================================================
隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read)
=========================================================================================== 未提交读(Read uncommitted) 可能 可能 可能 已提交读(Read committed) 不可能 可能 可能 可重复读(Repeatable read) 不可能 不可能 可能 可串行化(Serializable ) 不可能 不可能 不可能 =========================================================================================== ·未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据 ·提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读) ·可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读 ·串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

3.新建一个表(testTx),测试Transaction,并插入一行数据

CREATE TABLE testTx (
id int(8) primary key auto_increment,
version int(8)
);
insert into testTx values (1,1);

mysql> SELECT @@GLOBAL.tx_isolation, @@tx_isolation;
+-----------------------+-----------------+
| @@GLOBAL.tx_isolation | @@tx_isolation |
+-----------------------+-----------------+
| REPEATABLE-READ | REPEATABLE-READ |
+-----------------------+-----------------+

查看Mysql的默认的隔离级别是REPEATABLE-READ.

为什么叫可重复读的隔离级别,就是因为在此种隔离级别下,可以避免不可重复读的问题。

T1 T2

set autocommit=0;

begin;//开始事务

set autocommit=0;

begin;//开始事务

mysql> select * from testTx;
+----+---------+
| id | version |
+----+---------+
| 1 | 1 |
+----+---------+


 
mysql> update testTX set version =2 where id =1;
Query OK, 1 row affected (0.05 sec)
Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from testisolatio;
+----+---------+
| id | version |
+----+---------+
| 1 | 2 |
+----+---------+
1 row in set (0.00 sec)
mysql> select * from testTx;
+----+---------+
| id | version |
+----+---------+
| 1 | 1 |
+----+---------+

【说明】
T2未提交,看到数据不变,无脏读。

 
   commit;
mysql> select * from testTx;
+----+---------+
| id | version |
+----+---------+
| 1 | 1 |
+----+---------+

【说明】
T2提交,看到数据依旧变,说明可以重复读。

 
 commit;  
mysql> select * from testTx;
+----+---------+
| id | version |
+----+---------+
| 1 | 2 |
+----+---------+

事务提交之后,看到数据改变

 
   

以上就是mysql的可重复读隔离模式下的两个事务的执行过程,我们再来看在REPEATABLE-READ模式下的幻读问题。

Session1 Session2
begin; begin;
mysql> select * from testTx;
+----+---------+
| id | version |
+----+---------+
| 1 | 2 |
+----+---------+
 
 

INSERT INTO teseTX VALUES (2, 1);

mysql> select * from testTx;
+----+---------+
| id | version |
+----+---------+
| 1 | 2 |
+----+---------+
Session2 未提交,依然是可重复读
 
  commit;
mysql> select * from testTx;
+----+---------+
| id | version |
+----+---------+
| 1 | 2 |
+----+---------+
Session2 已提交,数据依然和之前读出来一样.但是幻读问题依然有
 

INSERT INTO teseTX VALUES (2, 1);

ERROR 1062 (23000):
Duplicate entry '1' for key 1

(shit, 刚刚明明显示只有id=1的记录),这里出现了幻读

 
 commit;  
mysql> select * from testTx;
+----+---------+
| id | version |
+----+---------+
| 1 | 2 |
+----+---------+
| 2 | 1 |
+----+---------+
最终显示了
 

但是Mysql提供了在REPEATABLE-READ模式下解决幻读的问题。

http://dev.mysql.com/doc/refman/5.0/en/innodb-next-key-locking.html
To prevent phantoms, InnoDB uses an algorithm called next-key locking that combines index-row locking with gap locking. InnoDB performs row-level locking in such a way that when it searches or scans a table index, it sets shared or exclusive locks on the index records it encounters. Thus, the row-level locks are actually index-record locks. In addition, a next-key lock on an index record also affects the “gap” before that index record. That is, a next-key lock is an index-record lock plus a gap lock on the gap preceding the index record. If one session has a shared or exclusive lock on record R in an index, another session cannot insert a new index record in the gap immediately before R in the index order.

再看一个实验,要注意,表TesetTX里的id为主键字段。实验三:

   
begin; begin;

mysql> select * from testTX where id<2 for update;
+----+---------+
| id | version |
+----+---------+
| 1 | 2 |
+----+---------+
1 row in set (15.84 sec)

 
 

INSERT INTO teseTX VALUES (3, 1);
Query OK, 1 row affected

mysql> select * from testTX where id<2 for update;
+----+---------+
| id | version |
+----+---------+
| 1 | 2 |
+----+---------+
1 row in set (15.84 sec)
 
 

mysql> select * from testTX where id<=1 for update;

(waiting for lock ... then timeout)
 ERROR 1205 (HY000): Lock wait timeout exceeded;
 try restarting transaction

   commit;
 commit;  

mysql> select * from testTX;
+----+---------+
| id | version |
+----+---------+
| 1 | 2           |
+----+---------+

| 2 | 1           |
+----+---------+

| 3 | 1          |
+----+---------+
1 row in set (15.84 sec)

 
   

可以看到,用id<2加的锁,只锁住了id<2的范围,可以成功添加id为3的记录,但一旦想要获得select id<=1,就会阻塞,等待。

MySQL InnoDB的可重复读并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁度使用到的机制就是next-key locks。

上一篇:hdu 5446 Unknown Treasure lucas和CRT


下一篇:Android开发MVP模式解析