精华内容
下载资源
问答
  • Mysql数据库事务和锁的关系以及演化 一直认为事务的隔离基本实现方式就是锁,这次可以总结一下

    前言

    • 一直认为事务的隔离基本实现方式就是锁,这次可以总结一下
    • 最近阅读了很多大佬对于mysql事务隔离级别和锁相关的文章,终于茅塞顿开,特此总结

    mysql架构

    这个其实可以做一个专门的主题来讲,这里先过

    快照读/当前读

    快照读:select * from T;简单的select操作,属于快照读,不加锁。
    当前读:select * from table where ? lock in share mode;
    select * from table where ? for update;
    insert into table values (…);
    update table set ? where ?;
    delete from table where ?;
    特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。

    快照读 (snapshot read)与当前读 (current read)。快照读,读取的是记录的可见版本 (有可能是历史版本),不用加锁。当前读,读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。

    MVCC

    MVCC (Multi-Version Concurrency Control)
    简单理解:MVCC,多版本并发控制技术。在InnoDB中,在每一行记录的后面增加两个隐藏列,记录创建版本号和删除版本号。通过版本号和行锁,从而提高数据库系统并发性能。
    真实原理Mysql中的MVCC
    关于innodb中MVCC的一些理解

    S锁/X锁

    S锁:select * from table where ? lock in share mode;
    X锁:增删改都是,select * from table where ? for update;也是

    两阶段锁

    2PL就是将加锁/解锁分为两个完全不相交的阶段。加锁阶段:只加锁,不放锁。解锁阶段:只放锁,不加锁。

    隔离级别

    Serializable
    从MVCC并发控制退化为基于锁的并发控制。不区别快照读与当前读,所有的读操作均为当前读,读加读锁 (S锁),写加写锁 (X锁)。
    Serializable隔离级别下,读写冲突,因此并发度急剧下降,在MySQL/InnoDB下不建议使用。
    Repeatable Read (RR)
    针对当前读,RR隔离级别保证对读取到的记录加锁 (记录锁),同时保证对读取的范围加锁,新的满足查询条件的记录不能够插入 (间隙锁),不存在幻读现象。
    Read Committed (RC)
    针对当前读,RC隔离级别保证对读取到的记录加锁 (记录锁),存在幻读现象。
    还存在不可重复读现象
    Read Uncommited
    可以读取未提交记录(出现幻读/不可重复读/脏读)。此隔离级别,不会使用

    幻读

    上文提到的幻读现象,即一个事务读到另一个事务已提交的insert数据。
    网上很多地方说mysql的RR级别能解决幻读,这句话让我困扰好久。
    先来个例子:(RR,n非唯一索引,没有使用gap锁)
    例子1:t(id,n)

    时间点 事务1 事务2
    1 begin
    2 select * from t where n =1 ; begin
    3 insert into t values(2,1);
    4 commit;
    5 select * from t where n = 1;
    6 commit;

    请问上面第五步事务1查出来的结果有几条,是否包括事务2的那一条?
    答案是1条,不包括事务2的那一条。
    那就奇怪了,不是RR级别有幻读吗?这不就是幻读吗?
    其实这个只是还没达到幻读出现的条件。事务1的第2步和第5步,都是快照读,而且是同一个版本

    再来个例子:(RC,n非唯一索引,没有使用gap锁)
    例子2:t(id,n)

    时间点 事务1 事务2
    1 begin
    2 select * from t where n = 1; begin
    3 insert into t values(2,1);
    4 commit;
    5 select * from t where n = 1;
    6 commit;

    这种情况下,会出现幻读,事务1 的第2步和第5步拿到的结果不一样,第5步读取到了事务2已提交的数据。这是为什么呢?

    解答: 不同事务隔离级别下,快照读的区别: READ COMMITTED 隔离级别下,每次读取都会重新生成一个快照,所以每次快照都是最新的,也因此事务中每次SELECT也可以看到其它已commit事务所作的更改;REPEATED READ 隔离级别下,快照会在事务中第一次SELECT语句执行时生成,只有在本事务中对数据进行更改才会更新快照,因此,只有第一次SELECT之前其它已提交事务所作的更改你可以看到,但是如果已执行了SELECT,那么其它事务commit数据,你SELECT是看不到的。

    那么,RR级别情况下,怎么会出现幻读呢?看例子:
    例子3:t(id,n)

    时间点 事务1 事务2
    1 begin
    2 select * from t; begin
    3 insert into t values(1,1);
    4 commit;
    5 select * from t;
    6 update t set n = 3;
    7 select * from t;
    8 commit;

    这个实验发现,第7步和2,5步结果不一样,第七步能查到t(1,3)这条数据,而2,5步却查到的是空
    这就是出现了幻读,事务1中对数据进行了更改,所以,第7步生成的快照更新了,读取了事务2已提交的数据,这个幻读该如何处理呢?
    放心,mysql的gap锁可以解决这个问题。但是,InnoDB提供了next-key locks,但需要应用程序自己去加锁,这句话怎么理解呢?
    对于例子3,即使开启了间隙锁,也没有用,需要自己手动加锁,比如:

    SELECT * FROM child WHERE id > 100 FOR UPDATE;
    

    这样,InnoDB会给id大于100的行(假如child表里有一行id为102),以及100-102,102+的gap都加上锁。
    例子4:

    时间点 事务1 事务2
    1 begin
    2 SELECT * FROM t_bitfly WHERE id<=1 FOR UPDATE; begin
    3 INSERT INTO t_bitfly VALUES (2, ‘b’);
    4 SELECT * FROM t_bitfly;
    5 INSERT INTO t_bitfly VALUES (0, ‘0’);(waiting for lock …then timeout)ERROR 1205 (HY000):Lock wait timeout exceeded;try restarting transaction
    6 SELECT * FROM t_bitfly;
    7 commit;

    第5步,间隙锁生效,等待。

    此处见详细链接:MySQL的InnoDB的幻读问题

    不可重复读

    上面分析了幻读,再来分析一下不可重复读。不可重复读,顾名思义,就是不能重复读,神马意思呢?同一条数据,同一个事务中,两次读取的结果不一样,这样就不能重复读啦!这里指的是,读取到别的事务已提交的结果。
    为什么会这样呢?
    其实区别上面已经说了,RC级别下,每次快照读都是拿的最新的版本数据,所以会读到其他事物已提交的数据。

    很多人容易搞混不可重复读和幻读,确实这两者有些相似。但不可重复读重点在于update和delete,而幻读的重点在于insert。

    间隙锁/next-key锁/插入意向锁

    间隙锁,锁间隙的意思就是锁定某一个范围,间隙锁又叫gap锁,其不会阻塞其他的gap锁,但是会阻塞插入间隙锁,这也是用来防止幻读的关键。innodb自动开启,设置参数:
    innodb_locks_unsafe_for_binlog:默认值为0,即启用gap lock。

    next-key锁
    这个锁本质是记录锁加上gap锁。在RR隔离级别下(InnoDB默认),Innodb对于行的扫描锁定都是使用此算法,但是如果查询扫描中有唯一索引会退化成只使用记录锁。为什么呢?
    因为唯一索引能确定行数,而其他索引不能确定行数,有可能在其他事务中会再次添加这个索引的数据会造成幻读。

    因为InnoDB对于行的查询都是采用了Next-Key Lock的算法,锁定的不是单个值,而是一个范围。上面索引有1,3,5,8,11,被Next-Key Locking的区间为:
    (-∞,1),(1,3],(3,5],(5,8],(8,11],(11,+∞)

    插入意向锁
    可以看出插入意向锁是在插入的时候产生的,在多个事务同时写入不同数据至同一索引间隙的时候,并不需要等待其他事务完成,不会发生锁等待。假设有一个记录索引包含键值4和7,不同的事务分别插入5和6,每个事务都会产生一个加在4-7之间的插入意向锁,获取在插入行上的排它锁,但是不会被互相锁住,因为数据行并不冲突。

    这里要说明的是如果有间隙锁了,插入意向锁会被阻塞。
    

    死锁分析

    死锁原理

    死锁的发生与否,并不在于事务中有多少条SQL语句,死锁的关键在于:两个(或以上)的Session加锁的顺序不一致。而使用本文上面提到的,分析MySQL每条SQL语句的加锁规则,分析出每条语句的加锁顺序,然后检查多个并发SQL间是否存在以相反的顺序加锁的情况,就可以分析出各种潜在的死锁情况,也可以分析出线上死锁发生的原因。

    一个next-key引发死锁的案例

    模拟事件

    创建表student:

    CREATE TABLE `LockTest` (
       `order_id` varchar(20) NOT NULL,
       `id` bigint(20) NOT NULL AUTO_INCREMENT,
       PRIMARY KEY (`id`),
       KEY `idx_order_id` (`order_id`)
     ) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;
    

    例子4:

    时间点 事务1 事务2
    1 begin
    2 delete from LockTest where order_id = ‘D20’ begin
    3 delete from LockTest where order_id = ‘D19’
    4 insert into LockTest (order_id) values (‘D20’)
    5 insert into LockTest (order_id) values (‘D19’)
    6 commit commit;

    测试结果

    事务1 执行到insert语句会block住,事务2执行insert语句会提示死锁错误。

    原因分析

    • 1、首先看测试表的建表语句,id是主键索引,同时该主键是自增主键。order_id是普通索引。
    • 2、事务1执行delete from LockTest where order_id = ‘D20’;语句时,由于数据库的隔离级别是RR,因此此时事务1在主键id上获得了一个next-key lock,这个锁的范围是[16, +∞)。
      这个16就来自于AUTO_INCREMENT=16,因为LockTest目前是张空表。
    • 3、同理,事务2执行delete from LockTest where order_id = ‘D19’;语句时,由于数据库的隔离级别是RR,事务2在主键id上也获得了一个next-key lock,这个锁的范围是[16, +∞)。
      也就是说此时,事务1和事务2获得的锁是一样的。
    • 4、事务1继续执行insert into LockTest (order_id) values (‘D20’);语句,这个时候由于该语句企图往LockTest表insert一行id=16,order_id=D20的数据,
      但是由于在事务2的delete语句中,主键id上已经有了一个范围为[16, +∞)的锁,导致事务1此时想插入数据插不进去,被阻塞了。
    • 5、继续事务2的插入语句insert into LockTest (order_id) values (‘D19’); 该插入语句同样也想往LockTest表insert一行id=16,order_id=D19的数据,
      但是由于由于在事务1的delete语句中,主键id上已经有了一个范围为[16, +∞)的锁,导致事务2此时想插入数据插不进去,被阻塞了。
      此时,可以发现,事务1和事务2的锁是互相持有,互相等待的。所以innodb判断该事务遇到了死锁,直接将事务2进行了回滚。然后回头去看事务1,insert into LockTest (order_id) values (‘D20’);被成功执行。

    原因总结:间隙锁和插入意向锁的冲突,导致了阻塞

    解决方案

    • 方案一:隔离级别降级为RC,在RC级别下不会加入间隙锁,所以就不会出现毛病了,但是在RC级别下会出现幻读,可提交读都破坏隔离性的毛病,所以这个方案不行。
    • 方案二:隔离级别升级为可序列化,小明经过测试后发现不会出现这个问题,但是在可序列化级别下,性能会较低,会出现较多的锁等待,同样的也不考虑。
    • 方案三:修改代码逻辑,不要直接删,改成每个数据由业务逻辑去判断哪些是更新,哪些是删除,那些是添加,这个工作量稍大,小明写这个直接删除的逻辑就是为了不做这些复杂的事的,所以这个方案先不考虑。
    • 方案四:较少的修改代码逻辑,在删除之前,可以通过快照查询(不加锁),如果查询没有结果,则直接插入,如果有通过主键进行删除,在之前第三节实验2中,通过唯一索引会降级为记录锁,所以不存在间隙锁。

    参考文档

    展开全文
  • spring事务 JTA事务 mybatis事务 redis事务 jdbc事务(一次数据库连接范围而已) ...   框架事务和数据库事务关系?...数据库事务和锁的关系? 数据库事务的四大特性(原子性,一致性,隔离性,持久性...

    spring事务 JTA事务  mybatis事务  redis事务  jdbc事务(一次数据库连接范围而已)

    https://www.ibm.com/developerworks/cn/java/j-lo-jta/

     

    框架事务和数据库事务关系?

    所有框架的事务如spring的事务都是基于数据库事务的支持。

    数据库事务和锁的关系?

    数据库事务的四大特性(原子性,一致性,隔离性,持久性)中的隔离性是靠锁机制和MVCC(多版本并发控制)机制来实现的:

     

    mysql的锁按应用场景类型分为共享锁和排他锁,按加锁范围分为表锁和行锁(表锁由数据库服务器实现,行锁由存储引擎实现。)【共享锁和排他锁也叫乐观锁和悲观锁。有时候也叫读锁和写锁。java中1.5提供的concurrent包下的乐观锁是用CAS解决写的问题的。乐观锁一般适用于读写混合但读操作多的场景。】

    最高级别的串行化的事务隔离级别中会在每个读的数据行上加共享锁,虽然是共享锁但会产生大量超时现象,一般实际开发中不会用到。

    一个事务执行的任何过程中都可以获得锁,根据隔离级别来决定读/更改操作完成后立马释放锁还是等事务提交或回滚的时候才释放锁。

    例如:

    读未提交:

    更新时上的是共享锁,允许别的事务来访问。直到事务结束该锁释放。

    读已提交级别:(不可重复读本身就是合理的)

    读时上的共享锁读完后立马释放,其他事务更新时才有机会上排他锁(直到事务结束才释放)

    可重复读:

     

    【不管是编程语言还是数据库,提供锁机制都是为了支持对共享资源并发访问时保证一致性和完整性。】

    lock和latch都叫锁,latch要求锁定的时间非常短(操作缓冲池中用?),lock用来锁定的对象是数据库中的对象如表,页,行。被锁定的对象(行,页,表)仅在事务提交或回滚后释放。lock有死锁机制latch无。

     

     

    死锁:

    场景一:

    线程A锁住对象A,线程B锁住对象B,在两个线程都没释放自己锁住的对象的情况下,尝试获取对方线程占有的对象锁导致谁也获取不到出现死锁(避免死锁的专利)。

    /**
    * 线程Thread1率先占有了resource1, 继续运行时需要resource2, 但此时resource2却被线程Thread2占有了,
    * 因此只能等待Thread2释放resource2才能够继续运行; 同时,Thread2也需要resource1,
    * 它只能等待Thread1释放resource1才能够继续运行, 因此,Thread1和Thread2都处于等待状态, 
    * 谁也无法继续运行,即产生了死锁。
    */
    public class DeadLock {

    public static void main(String[] args) {
    dead_lock();
    }

    private static void dead_lock() {
    // 两个资源
    final Object resource1 = "resource1";
    final Object resource2 = "resource2";
    // 第一个线程,想先占有resource1,再尝试着占有resource2
    Thread t1 = new Thread() {
    public void run() {
    // 尝试占有resource1
    synchronized (resource1) {
    // 成功占有resource1
    System.out.println("Thread1 1:locked resource1");
    // 休眠一段时间
    try {
    Thread.sleep(50);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    // 尝试占有resource2,如果不能占有,该线程会一直等到
    synchronized (resource2) {
    System.out.println("Thread1 1:locked resource2");
    }
    }
    }
    };
    // 第二个线程,想先占有resource2,再占有resource1
    Thread t2 = new Thread() {
    public void run() {
    // 尝试占有resource2
    synchronized (resource2) {
    // 成功占有resource2
    System.out.println("Thread 2 :locked resource2");
    // 休眠一段时间
    try {
    Thread.sleep(50);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    // 尝试占有resource1,如果不能占有,该线程会一直等到
    synchronized (resource1) {
    System.out.println("Thread1 2:locked resource1");
    }
    }
    }
    };
    // 启动线程
    t1.start();
    t2.start();
    }
    }

    场景二:

    递归调用同一个方法(在一个线程中就不存在死锁问题,例如下面的recursive变成private就不存在问题)

    public class Test {  
        public void recursive(){  
            this.businessLogic();  
        }  
        public synchronized void businessLogic(){  
            System.out.println("处理业务逻辑");  
        System.out.println("保存到<a href="http://lib.csdn.net/base/mysql" class='replace_word' title="MySQL知识库" target='_blank' style='color:#df3434; font-weight:bold;'>数据库</a>");  
            this.recursive();  
        }  
    }  

    以上这段代码就是个能形成死锁的代码,事实上这个“synchronized”放在“businessLogic()”和“recursive()”都会形成死锁,并且是多线程的情况下就会锁住!他的逻辑顺序是先执行recursive()方法然后接下来执行businessLogic()方法同时将businessLogic()方法锁住,接下来程序进入businessLogic()方法内部执行完打印语句后开始执行recursive(),进入recursive()后准备执行businessLogic(),等等问题来了!之前执行的businessLogic()的锁还没有放开这次又执行到这里了,当然是过不去的了,形成了死锁!从这个例子我们总结出来一个规律就是在递归的时候在递归链上面的方法上加锁肯定会出现死锁(所谓递归链就是指recursive()链向businessLogic(),businessLogic()又链回recursive()),解决这个问题的方法就是避免在递归链上加锁,请看以下的例子
    public class Test {  
        public void recursive(){  
            this.businessLogic();  
        }  
        public  void businessLogic(){  
            System.out.println("处理业务逻辑");  
            this.saveToDB();  
            this.recursive();  
        }  
        public synchronized void saveToDB(){  
            System.out.println("保存到数据库");  
        }  
    }  
     
    saveToDB()不在这条递归链上面自然不会出现死锁,所以说在递归中加锁是件很危险的事情,实在逃不过要加锁就加在最小的粒度的程序代码上以减小死锁的概率。

     

    展开全文
  • 概述:数据库操作具有四个特性:原子性,隔离性,持久性,一致性。当多个线程操作同一个数据时候根据不同线程或者事务...数据库事务就是具有上述四个特性一种数据库操作逻辑,而解决不同事务并发问题

    原文:http://blog.csdn.net/dreamwbt/article/details/53371687

    概述:数据库操作具有四个特性:原子性,隔离性,持久性,一致性。当多个线程操作同一个数据的时候根据不同线程或者事务的动作和时机的不同会出现不同的并发问题,比如脏读,不可重读,幻读,丢失更新等。

    数据库事务就是具有上述四个特性的一种数据库操作逻辑,而解决不同事务的并发问题的时候就需要根据上述不同的问题设置不同的隔离级别,而不同的隔离级别底层用的就是不同的数据库锁机制,比如行锁,表锁,页锁,悲观锁,乐观锁等

    本文主要依据InnoDB引擎来理解四个事务隔离级别,以及脏读、不重复读、幻读的理解。

    MySQL InnoDB事务的隔离级别有四级,默认是可重复读REPEATABLE READ)。

    ·        未提交读(READUNCOMMITTED)。另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据(脏读)。

    ·        提交读(READCOMMITTED)。本事务读取到的是最新的数据(其他事务提交后的)。问题是,在同一个事务里,前后两次相同的SELECT会读到不同的结果(不重复读)。

    ·        可重复读(REPEATABLEREAD)。在同一个事务里,SELECT的结果是事务开始时时间点的状态,因此,同样的SELECT操作读到的结果会是一致的。但是,会有幻读现象(稍后解释)。

    ·        串行化(SERIALIZABLE)。读操作会隐式获取共享锁,可以保证不同事务间的互斥。

    四个级别逐渐增强,每个级别解决一个问题。

    ·        脏读,最容易理解。另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据。

    ·        不重复读。解决了脏读后,会遇到,同一个事务执行过程中,另外一个事务提交了新数据,因此本事务先后两次读到的数据结果会不一致。

    ·        幻读。解决了不重复读,保证了同一个事务里,查询的结果都是事务开始时的状态(一致性)。但是,如果另一个事务同时提交了新数据,本事务再更新时,就会惊奇的发现了这些新数据,貌似之前读到的数据是鬼影一样的幻觉。

    事务的四种隔离级别

    在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别。我们的数据库锁,也是为了构建这些隔离级别存在的。

    隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read)
    未提交读(Read uncommitted) 可能 可能 可能
    已提交读(Read committed) 不可能 可能 可能
    可重复读(Repeatable read) 不可能 不可能 可能
    可串行化(Serializable ) 不可能 不可能 不可能
    • 未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
    • 提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
    • 可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读
    • 串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
    展开全文
  •  1)事务的提交回滚:COMMIT/ROLLBACK  2)事务的开始结束  开始事务:连接到数据库,执行DML、DCL、DDL语句  结束事务: 1. 执行DDL(例如CREATE TABLE),DCL(例如GRANT),系统自动执行COMMIT语句 ...

     

    事务

    1. 说明

      一组SQL,一个逻辑工作单位,执行时整体修改或者整体回退。

    2.事务相关概念

      1)事务的提交和回滚:COMMIT/ROLLBACK

      2)事务的开始和结束

        开始事务:连接到数据库,执行DML、DCL、DDL语句

        结束事务: 1. 执行DDL(例如CREATE TABLE),DCL(例如GRANT),系统自动执行COMMIT语句

              2. 执行COMMIT/ROLLBACK

              3. 退出/断开数据库的连接自动执行COMMIT语句

              4. 进程意外终止,事务自动rollback

              5. 事务COMMIT时会生成一个唯一的系统变化号(SCN)保存到事务表

      3)保存点(savepoint): 可以在事务的任何地方设置保存点,以便ROLLBACK

      4)事务的四个特性ACID :

        1. Atomicity(原子性): 事务中sql语句不可分割,要么都做,要么都不做

        2. Consistency(一致性) : 指事务操作前后,数据库中数据是一致的,数据满足业务规则约束(例如账户金额的转出和转入),与原子性对应。

        3. Isolation(隔离性):多个并发事务可以独立运行,而不能相互干扰,一个事务修改数据未提交前,其他事务看不到它所做的更改。

        4. Durability(持久性):事务提交后,数据的修改是永久的。

      5) 死锁:当两个事务相互等待对方释放资源时,就会形成死锁,下面章节详细分析

    事务隔离级别

    1 .两个事务并发访问数据库数据时可能存在的问题

      1. 幻想读:

        事务T1读取一条指定where条件的语句,返回结果集。此时事务T2插入一行新记录并commit,恰好满足T1的where条件。然后T1使用相同的条件再次查询,结果集中可以看到T2插入的记录,这条新纪录就是幻想。

      2. 不可重复读取:

        事务T1读取一行记录,紧接着事务T2修改了T1刚刚读取的记录并commit,然后T1再次查询,发现与第一次读取的记录不同,这称为不可重复读。 

      3. 脏读:

        事务T1更新了一行记录,还未提交所做的修改,这个T2读取了更新后的数据,然后T1执行回滚操作,取消刚才的修改,所以T2所读取的行就无效,也就是脏数据。

    2.oracle事务隔离级别

    oracle支持的隔离级别:(不支持脏读)

    READ COMMITTED--不允许脏读,允许幻想读和不可重复读
    
    
    SERIALIZABLE--以上三种都不允许

    sql标准还支持READ UNCOMMITTED (三种都允许)和 REPEATABLE READ(不允许不可重复读和脏读,只允许幻想读)

    以上区别在下面章节事务建立,隔离级别分析中说明

    以上内容引用自:http://www.cnblogs.com/wishyouhappy/p/3698152.html

    1.什么是事务,事务的特性是什么?

    事务的任务便是使数据库从一种状态变换成为另一种状态,这不同于文件系统,它是数据库所特用的。它的特性有四个:TOM总结为ACID即
    原子性atomicity:语句级原子性,过程级原子性,事务级原子性
    一致性consistency:状态一致,同一事务中不会有两种状态
    隔离性isolation:事务间是互相分离的互不影响(这里可能也有自治事务)
    持久性durability:事务提交了,那么状态就是永久的

    所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。 由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。

    关于数据库死锁的检查方法

    一、数据库死锁的现象

    程序在执行的过程中,点击确定或保存按钮,程序没有响应,也没有出现报错。

    二、死锁的原理

    当对于数据库某个表的某一列做更新或删除等操作,执行完毕后该条语句不提

    交,另一条对于这一列数据做更新操作的语句在执行的时候就会处于等待状态,

    此时的现象是这条语句一直在执行,但一直没有执行成功,也没有报错。

     

    死锁的解决方法

    一般情况下,只要将产生死锁的语句提交就可以了,但是在实际的执行过程中。用户可能不知道产生死锁的语句是哪一句。可以将程序关闭并重新启动就可以了。经常在Oracle的使用过程中碰到这个问题,所以也总结了一点解决方法。

      数据库是一个多用户使用的共享资源,比如一个用户表t_user,两个浏览器前面的人登录了同个一个账号,把电话号码改了。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性(脏读,不可重复读,幻读等),可能产生死锁。为了解决这个问题,加锁是一个非常重要的技术,对实现数据库并发控制是一个好的方案。简单说,当一个执行sql语句的事务想要操作表记录之前,先向数据库发出请求,对你访问的记录集加锁,在这个事务释放这个锁之前,其他事务不能对这些数据进行更新操作。

    行级锁和表级锁是根据锁的粒度来区分的,行记录,表都是资源,锁是作用在这些资源上的。如果粒度比较小(比如行级锁),可以增加系统的并发量但需要较大的系统开销,会影响到性能,出现死锁,,因为粒度小则操作的锁的数量会增加;如果作用在表上,粒度大,开销小,维护的锁少,不会出现死锁,但是并发是相当昂贵的,因为锁定了整个表就限制了其它事务对这个表中其他记录的访问。

    锁包括行级锁、表级锁、悲观锁、乐观锁

    行级锁:一种它锁,防止另外事务修改此行;在使用以下语句时,Oracle会自动应用行级锁:INSERT、UPDATE、DELETE、SELECT … FOR UPDATE [OF columns] [WAIT n | NOWAIT];SELECT … FOR UPDATE语句允许用户一次锁定多条记录进行更新.使用commit或者rollback释放锁。 特点:开锁大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。适合于有大量按索引更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理系统。

    表级锁:5种

     共享锁(SHARE) - 锁定表,对记录只读不写,多个用户可以同时在同一个表上应用此锁,在表没有被任何DML操作时,多个事务都可加锁,但只有在仅一个事务加锁的情况下只有此事务才能对表更新;当表已经被更新或者指定要更新时(select for update),任何事务都不能加此锁了。

    共享行排他(SHARE ROW EXCLUSIVE) – 比共享锁更多的限制,禁止使用共享锁及更高的锁,在表没有被任何DML操作时,只有一个事务可以加锁,可以更新,书上说别的事务可以使用select for update锁定选中的数据行,可是实验后没被验证。

       排他(EXCLUSIVE) – 限制最强的表锁,仅允许其他用户查询该表的行。禁止修改和锁定表

     行共享 (ROW SHARE) – 禁止排他锁定表,与行排他类似,区别是别的事务还可以在此表上加任何排他锁。(除排他(exclusive)外)

       行排他(ROW EXCLUSIVE) – 禁止使用排他锁和共享锁,其他事务依然可以并发地对相同数据表执行查询,插入,更新,删除操作,或对表内数据行加锁的操作,但不能有其他的排他锁(自身是可以的,没发现有什么用)

     

     悲观锁:

       Pessimistic Lock正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守悲观态度,事务每次去操作数据的时候都假设有其他事务会修改需要访问的数据,所以在访问之前都要求上锁,行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁,因此,在整个数据处理过程中,将数据处于锁定状态。

     一个典型的倚赖数据库的悲观锁调用: select * from account where name=”Erica” for update 这条sql 语句锁定了account 表中所有符合检索条件(name=”Erica”)的记录。 本次事务提交之前(事务提交时会释放事务过程中的锁),外界无法修改这些记录。

     

    乐观锁:

       Optimistic Lock,和悲欢锁相反,事务每次去操作数据之前,都假设其他事务不会修改这些需要访问的数据 ,所以 在访问之前不要求上锁,只是在进行更新修改操作的时候判断一下在访问的期间有没有其他人修改数据 了。它适用于多读的应用类型,冲突真的发生比较少的时候就比较好,这样省去了开销的开销,可以提高吞吐量;但如果是真的经常要发生冲突的,那每次还要去判断进行retry,反倒降低的性能,这个时候悲欢锁比较好。数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

     

    1.利润表t_profit中有一个 version字段,当前值为1;而总资产余额字段(balance)为$10000

           2.操作员A读出version=1,从总资产减除2000,10000-2000=8000.

           3.A还没操作结束,此时操作员B也读出version=1,总资产减除5000,10000-5000=5000.

           4.A操作完成,把version加1,修改为2,把总资产减2000后提交更新数据库,更新成功

           5.B操作了,也加version加1,修改为2,把总资产减5000后提交更新数据库,此时发现version已经为2了,如B修改后加1的version一样,不满足乐观锁策略:"提交的版本必有大于记录当前的版本才能执行"。因此B的操作请求被驳回,这样就避免了B就version=1的旧数据修改的结果覆盖了A操作的结果的可能。如没有乐观锁,那A减去2000后剩余8000,但B操作的时候是用10000-5000剩余5000的,如果B的提交成功,总资产余额就是5000,但实际情况应该是8000-5000=3000的。出现总资产表记录和实际支出不一致。

     

     

    复制代码

    SQL> select * from t_book;
     
    BOOKID BOOKNAME                                           PUBLISH
    ------ -------------------------------------------------- --------------------------------------------------
    1      OracleTestForMine                                  ShenZhenPublish
    2      JavaOKa                                            JiXieGongYePublish
     
    SQL> savepoint a1;
     
    Savepoint created
     
    SQL> delete from t_book where bookid='2';
     
    1 row deleted
     
    SQL> select * from t_book;
     
    BOOKID BOOKNAME                                           PUBLISH
    ------ -------------------------------------------------- --------------------------------------------------
    1      OracleTestForMine                                  ShenZhenPublish
     
    SQL> rollback to a1;
     
    Rollback complete
     
    SQL> select * from t_book;
     
    BOOKID BOOKNAME                                           PUBLISH
    ------ -------------------------------------------------- --------------------------------------------------
    1      OracleTestForMine                                  ShenZhenPublish
    2      JavaOKa                                            JiXieGongYePublish
     

    复制代码

     

     

    展开全文
  • 数据库事务和锁

    2018-06-21 14:12:00
    显式事务和隐式事务 模式 排它exclusive lock 共享shared lock 更新 兼容共享,不兼容更新、排它 意向 目的:在较高粒度级别有效监测不兼容锁定请求,防止授予不兼容的锁。 架构 其他...
  • 初步理解数据库锁和事务的关系

    千次阅读 2017-07-13 16:10:24
    写项目的时候遇到spring事务和数据库锁的问题,抽空整理一下: 先不去考虑共享锁还是排他锁,总之对update语句或者select ...for update都会加锁。当然这里select语句的where条件比如是id(主键)或者加索引的...
  • 数据库的事务和锁

    2013-09-29 13:37:53
    数据库的事务和锁 第一个 数据库的事务,事务定义:事务是一系列操作集合,这些操作要么全做,要么全不做。在关系数据库管理系统中,事务是数据库应用程序基本逻辑处理单元,它可以是一条SQL语句,一组...
  • 数据库事务和锁(转)

    2013-07-03 13:50:11
     关系数据库有四个显著特征,即安全性、完整性、并发性监测性。数据库的安全性就是要保证数据库中数据安全,防止未授权用户随意修改数据库数据,确保数据安全。在大多数数据库管理系统中,主要是通过...
  • 数据库-事务和锁

    2019-09-27 00:25:22
    数据库-事务和锁 事务 所谓事务是用户定义一个数据库操作系列,这些操作要么全部执行,要么全部不执行,是一个不可分割工作单位。例如在关系数据库中,一个事务可以是一条sql语句、一组sql语句或...
  • spring事务本质上使用数据库事务,而数据库事务本质上使用数据库锁,所以spring事务本质上使用数据库锁,开启spring事务意味着使用数据库锁; 那么事务的隔离级别与锁有什么关系呢?本人认为事务的隔离级别是通过...
  • 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性隔离性,一般使用加锁这种方式。同时数据库又是个高并发应用,同一时间会有大量并发访问,如果加锁过度,会极大降低并发处理能力。所以对于...
  • Innodb中的事务隔离级别和锁的关系 前言: 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式。同时数据库又是个高并发的应用,同一时间会有大量的并发访问,如果...
  • spring事务与数据库事务之间的关系 spring事务本质上使用数据库事务,而数据库事务本质上使用数据库,所以spring事务本质上使用数据库,开启spring事务意味着使用数据库 spring事务是对数据库事务的封装,...
  • 首先悲观锁和乐观是基于业务逻辑来讲,他们数据库的锁是不同概念,数据库的锁是实现数据库事务的机制。 乐观就在并发情况下,避免覆盖更新一种机制,是程序员自己通过添加一个version字段实现,每次...
  • 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性隔离性,一般使用加锁这种方式。同时数据库又是个高并发应用,同一时间会有大量并发访问,如果加锁过度,会极大降低并发处理能力。所以对于...
  • Innodb中的事务隔离级别和锁的关系 事务中的加锁方式 * MySQL中锁的种类 Read Committed(读取提交内容) Repeatable Read(可重读) * 不可重复读和幻读的区别 悲观锁和乐观锁 MVCC在MySQL的InnoDB中的实现 ...
  • 数据库事务和锁(一)

    千次阅读 2004-01-16 10:17:00
    简述 关系数据库有四个显著特征,即安全性、完整性、并发性监测性。数据库的安全性就是要保证数据库中数据安全,防止未授权用户随意修改数据库数据,确保数据安全。在大多数数据库管理系统中,主要是...
  • 数据库的事务

    2019-11-05 18:51:39
    最近复习到了数据库的内容,查了好多资料,觉得都特别抽象,下面内容除了做一些总结,尽可能举一些自己尝试...事务和程序是两个概念,一般地讲,一个程序中包含若干个事务。 事务是恢复和并发控制基本单位。 ...
  • MySQL事务和锁的关系

    千次阅读 2018-08-06 22:10:43
    而他们之间的关系可以定义为:是实现事务其中一个特性的机制。 事务是一个针对数据质量产生的概念。在高并发或者数据库出现突然断电的情况下,事务所包含的特性就能应对可能会出现的差错。事务有4个特性:Atomic ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 954
精华内容 381
关键字:

数据库事务和锁的关系