数据的事务是指做为单个逻辑工做单元执行的一系列操做,要么彻底执行,要么彻底不执行。html
事务必须具有四个特性:mysql
>set autocommit = 0 禁止自动提交 >start transaction; >update accout set money=money+100 where name="Jason"; >commit;
>set autocommit = 0 禁止自动提交 >start transaction; >update account set money=money-100 where name="justin"; >rollback;
若是没有隔离会发生这样几个问题:算法
一个事务处理过程里读取了另外一个未说起的的事务中的数据。sql
对于数据库中的某个数据,一个事务范围内屡次查询却能够返回不一样的数据值,这是因为在查询的间隔期间,另一个事务修改并提交了该数据。数据库
在一个事务中读取到了别的事务插入的数据,致使先后不同。好比:第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的所有数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,之后就会发生操做第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉同样。segmentfault
查看当前事务级别session
select @@tx_isolation;
修改事务的隔离级别并发
语法ide
set [global | session] transaction isolation level 隔离级别名称;
例如:高并发
set global transaction isolation level Repeatable read; # 修改全局的以后要从新创建会话才有效
隔离级别:Serializable | Repeatable read | Read committed |Read uncommitted
推荐博客:http://blog.chinaunix.net/uid-14010457-id-3956842.html
数据库为了保证四个特性,特别是一致性和隔离性,采用了加锁的方式。因为数据库是一个高并发的应用,同一时间有大量的并发访问。若是加锁过分的话,会极大地下降并发处理能力,因此对于加锁的处理,是数据库对于事务处理的精髓所在。
由于有大量的并发访问,为了预防死锁,通常应用中采用的是一次封锁的方案:就是在方法的开始阶段,已经预先知道须要用到那些数据,而后所有锁住,在方法执行以后,再所有解锁。
这种方案能够有效避免死锁发生,当时因为数据库操做在事务开始阶段并不知道具体会用到哪些数据,因此该方案不合适在数据库中使用。
两段锁协议将事务分红两个阶段:加锁阶段和解锁阶段
解锁阶段:当事务释放了一个封锁之后,事务进入解锁阶段,在该阶段只能进行解锁操做不能进行加锁操做。
注:这个方案没法避免死锁,可是能够保证事务调度的串行化(串行化在数据库恢复和备份时候很重要)。
对一整张表加锁,并发能力低下(即便有分读锁、写锁),通常在DDL处理时使用
下图时三个锁的位置关系图:
只锁住特定行的数据,并发能力强,MySQL通常都是用行锁来处理并发事务。
若是用到无索引的字段,那么MySQL会在存储引擎层面将全部的记录加锁,而后由MySQL Server过滤,若是不知足会调用unlock_row把不知足条件的记录释放锁(这里违背了二段锁协议)。# -----------------会话1--------------------------------------------------- mysql> create table t(id int,name varchar(10),key idx_id(id),primary key(name))engine =innodb; Query OK, 0 rows affected (0.03 sec) mysql> insert into t values(1,'a'),(3,'c'),(5,'e'),(8,'g'),(11,'j'); Query OK, 5 rows affected (0.01 sec) Records: 5 Duplicates: 0 Warnings: 0 mysql> select @@global.tx_isolation, @@tx_isolation; +-----------------------+-----------------+ | @@global.tx_isolation | @@tx_isolation | +-----------------------+-----------------+ | REPEATABLE-READ | REPEATABLE-READ | +-----------------------+-----------------+ 1 row in set, 2 warnings (0.01 sec) mysql> select * from t; +------+------+ | id | name | +------+------+ | 1 | a | | 3 | c | | 5 | e | | 8 | g | | 11 | j | +------+------+ 5 rows in set (0.01 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> delete from t where id=8; Query OK, 1 row affected (0.00 sec) """ 能够看到最后加了一个事务,对id=8的数据进行处理,然而没有提交 """ # -----------------会话2--------------------------------------------------- mysql> select @@global.tx_isolation, @@tx_isolation; +-----------------------+-----------------+ | @@global.tx_isolation | @@tx_isolation | +-----------------------+-----------------+ | REPEATABLE-READ | REPEATABLE-READ | +-----------------------+-----------------+ 1 row in set, 2 warnings (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into t(id,name) values(6,'f'); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t(id,name) values(5,'e1'); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t(id,name) values(7,'h'); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t(id,name) values(8,'gg'); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t(id,name) values(9,'k'); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t(id,name) values(10,'p'); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t(id,name) values(11,'iz'); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t(id,name) values(5,'cz'); Query OK, 1 row affected (0.00 sec) mysql> insert into t(id,name) values(11,'ja'); Query OK, 1 row affected (0.00 sec) """ 分析:由于会话1已经对id=8的记录加了一个X锁,因为是RR隔离级别,INNODB要防止幻读须要加GAP锁:即id=5(8的左边),id=11(8的右边)之间须要加间隙锁(GAP)。 这样[5,e]和[8,g],[8,g]和[11,j]之间的数据都要被锁。上面测试已经验证了这一点,根据索引的有序性,数据按照主键(name)排序,后面写入的[5,cz]([5,e]的左边) 和[11,ja]([11,j]的右边)不属于上面的范围从而能够写入。 另一种状况,把name主键去掉会是怎么样的状况?有兴趣的能够测试一下。 """
上面的例子中,当插入被会话1锁住的内容的时候,会有一个超时间,我用ctrl+c强制终止了。若是是在一个事务中,插入数据的时候超时了,会怎么办呢。
超时时间的参数:innodb_lock_wait_timeout ,默认是50秒。
超时是否回滚参数:innodb_rollback_on_timeout 默认是OFF。
在默认请求下,不会由于超时引起的异常而回滚,当参数innodb_rollback_on_timeout设置为ON时,则会。
接下来再看一个例子
# -----------------会话1--------------------------------------------------- mysql> create table tt(a int primary key)engine =innodb; Query OK, 0 rows affected (0.06 sec) mysql> insert into tt values(1),(3),(5),(8),(11); Query OK, 5 rows affected (0.01 sec) Records: 5 Duplicates: 0 Warnings: 0 mysql> select * from t; +------+------+ | id | name | +------+------+ | 1 | a | | 3 | c | | 5 | e | | 11 | j | +------+------+ 4 rows in set (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from tt where a = 8 for update; +---+ | a | +---+ | 8 | +---+ 1 row in set (0.00 sec) # -----------------会话2--------------------------------------------------- mysql> start transaction; Query OK, 0 rows affected (0.01 sec) mysql> insert into tt values(6); Query OK, 1 row affected (0.01 sec) mysql> insert into tt values(7); Query OK, 1 row affected (0.00 sec) mysql> insert into tt values(9); Query OK, 1 row affected (0.00 sec)
到这里应该会有些人有问题了,为啥这个例2和例1不同。
解释:
由于InnoDB对于行的查询都是采用了Next-Key Lock的算法,锁定的不是单个值,而是一个范围,按照这个方法是会和第一次测试结果同样。可是,当查询的索引含有惟一属性的时候,Next-Key Lock 会进行优化,将其降级为Record Lock,即仅锁住索引自己,不是范围。
注意:经过主键或则惟一索引来锁定不存在的值,也会产生GAP锁定。
注
若是不想出现那种阻塞的现象,能够显示的关闭GAP锁
1:把事务隔离级别改为:Read Committed,提交读、不可重复读。SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
2:修改参数:innodb_locks_unsafe_for_binlog 设置为1。
注:innodb_locks_unsafe_for_binlog最主要的做用就是控制innodb是否对gap加锁。该参数若是是enable的,则是unsafe的,此时gap不会加锁;反之,若是disable掉该参数,则gap会加锁。固然对于一些和数据完整性相关的定义,如外键和惟一索引(含主键)须要对gap进行加锁,那么innodb_locks_unsafe_for_binlog的设置并不会影响gap是否加锁。
牛人博客推荐:http://hedengcheng.com/?p=771
关于这方面的知识推荐一个思否的文章:http://www.javashuo.com/article/p-nocfhqye-eu.html