zoukankan      html  css  js  c++  java
  • 事物的锁机制和七种传播行为

    一、锁

      1、数据库和操作系统一样,是一个多用户使用的共享资源。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。加锁是实现数据库并 发控制的一个非常重要的技术。在实际应用中经常会遇到的与锁相关的异常情况,当两个事务需要一组有冲突的锁,而不能将事务继续下去的话,就会出现死锁,严重影响应用的正常执行。

      2、在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。当数据对象被加上排它锁时,其他的事务不能对它读取和修改。加了共享锁的数据对象可以被其他事务读取,但不能修改。数据库利用这两 种基本的锁类型来对数据库的事务进行并发控制。

      3、死锁

        (1)死锁的第一种情况

    一个用户A 访问表A(锁住了表A),然后又访问表B;另一个用户B 访问表B(锁住了表B),然后企图访问表A;这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,这就死锁就产生了。 

        (2)死锁的第二种情况 
    用户A查询一条纪录,然后修改该条纪录;这时用户B修改该条纪录,这时用户A的事务里锁的性质由查询的共享锁企图上升到独占锁,而用户B里的独占锁由于A 有共享锁存在所以必须等A释放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。这种死锁比较隐蔽,但在稍大点的项目中经常发生。如在某项目中,页面上的按钮点击后,没有使按钮立刻失效,使得用户会多次快速点击同一按钮,这样同一段代码对数据库同一条记录进行多次操作,很容易就出现这种死锁的情况。  

      4、死锁的产生4个必要条件:   

        1)互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放 
              2)占有且等待:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。 
              3)不可抢占条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用 
              4)循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。

       5、如何避免死锁

        (1)使用事务时,尽量缩短事务的逻辑处理过程,及早提交或回滚事务; 
        (2)设置死锁超时参数为合理范围,如:3分钟-10分种;超过时间,自动放弃本次操作,避免进程悬挂; 
        (3)所有的SP都要有错误处理(通过@error) 
        (4) 一般不要修改SQL SERVER事务的默认级别。不推荐强行加锁 
        (5)优化程序,检查并避免死锁现象出现; 

       6、锁模式

        (1)共享锁;

        (2)更新锁(U锁)

          两个步骤:

            2.1扫描获取Where条件时。这部分是一个更新查询,此时是一个更新锁。

            2.2如果将执行写入更新。此时该锁升级到排他锁。否则,该锁转变成共享锁。

              更新锁可以防止常见的死锁。

         (3)排他锁

    二、事物的七种传播行为 

      1、REQUIRED :需要事物:如果当前存在事物,就沿用当前事物,否则就新建一个事物

    @GetMapping("/REQUIRED")
    @Transactional
    public User REQUIRED(){
            User user = new User();
            user.setId(2);
            user.setName("andy");
            userService.insert(user);
            userService.insert(user);
            return userService.getById(2);
     }

      默认事务传播行为 REQUIRED ,如果有事务,那么加入事务,没有的话新创建一个

      结果: 创建事务,由于第二个插入语句失败,事务回滚,二个插入语句均失败。  

      2、SUPPORTS:支持事物:如果当前存在事物,就沿用当前事物,如果不存在,则继续用无事物的方式运行子方法

    @GetMapping("/SUPPORTS")
    @Transactional(propagation = Propagation.SUPPORTS)
    public User SUPPORTS(){
        User user = new User();
        user.setId(3);
        user.setName("andy");
        userService.insert(user);
        user.setId(2);
        userService.insert(user);
        return userService.getById(2);
    }

      事务传播行为 SUPPORTS 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行

      结果: 该controller 中的方法 SUPPORTS() 没有在其他事务方法中运行,所以会以非事物方法运行,所以3 插入成功,2 插入失败,如果 2 在 之前插入,那么 2,3 均  失败,因为 2 已经存在直接报RuntimeException

      3、MANDATORY:必须使用事物:如果当前没有事物,则抛出异常,如果存在当前事物,就沿用当前事物

    @GetMapping("/MANDATORY")
    @Transactional(propagation = Propagation.MANDATORY)
    public User MANDATORY(){
        User user = new User();
        user.setId(2);
        user.setName("andy");
        userService.insert(user);
        user.setId(3);
        userService.insert(user);
        return userService.getById(3);
    }

      事务传播行为 MANDATORY 必须在一个已有的事务中执行,否则抛出异常 (MANDATORY: 强制性的)

      4、REQUITES_NEW:创建新的事物:无论当前是否有事物,都会创建新的事物,这样新的事物就可以有新的锁和隔离级别等特性,与当前事物相互独立

    @GetMapping("/REQUIRES_NEW")
    @Transactional
    public User REQUIRES_NEW(){
        User user = new User();
        user.setId(2);
        user.setName("andy");
        userService.insert2(user);
        user.setId(2);
        userService.insert(user);
        return userService.getById(2);
    }

      事务传播行为 REQUIRES_NEW  不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行以前的事务

      5、NO_SUPPORTED:不支持事物,当前存在事物时,将事物挂起,运行方法

    @GetMapping("/NOT_SUPPORTED")
    @Transactional(propagation = Propagation.REQUIRED)
    public User NOT_SUPPORTED(){
        User user = new User();
        user.setId(2);
        user.setName("andy");
        userService.insert2(user);
        user.setId(2);
        userService.insert2(user);
        return userService.getById(2);
    }

      以非事务方式执行操作,如果当前存在事务,就把当前事务挂起

      6、NEVER:不支持事物:如果当前存在事物,则抛出易常,否则还有无事物执行

    @GetMapping("/NEVER")
    @Transactional
    public User NEVER(){
        User user = new User();
        user.setId(2);
        user.setName("andy");
        userService.insert(user);
        user.setId(3);
        userService.insert(user);
        return userService.getById(3);
    }

      事务传播行为 NEVER 不能在一个事务中执行,就是当前必须没有事务,否则抛出异常

      7、NESTED:在当前方法调用子方法时,如果子方法发生异常,只回滚子方法执行过的SQL,而不回滚当前的方法的事物。

    @GetMapping("/NESTED")
    @Transactional(propagation = Propagation.NESTED)
    public User NESTED(){
       return null;
    }

     

        

  • 相关阅读:
    内网Windows Server时间自动同步
    处理sqlserver数据
    virtualenv使用
    vue过渡动画效果
    vue视图
    vue组件
    Vue实例
    vue介绍
    Bootstrap布局
    Bootstrap组件
  • 原文地址:https://www.cnblogs.com/tinghao/p/11796715.html
Copyright © 2011-2022 走看看