• 讨论 update A set number=number+ ? where id=?的原子性 (含数据库原理)


    结论:

    1. 本句本身具有原子性

    2. 当前读(包含update等写入操作)锁定数据,直到事务提交




    https://www.zhihu.com/question/46733729/answer/128582074

    阿里的mysql大咖给出了肯定的答案


    比如,表名A,字段名为 number,如下的SQL语句:
    
    语句1:update A set number=number+ 5 where id=1;
    语句2:update A set number=number+ 7 where id=1;
    
    假设这两条SQL语句同时被mysql执行,id=1的记录中number字段的原始值为 10,那么是否有可能出现这种情况:
    
    语句1和2因为同时执行,他们得到的number的值都是10,都是在10的基础上分别加5和7,导致最终number被更新为15或17,而不是22?
    
    


    不会,这个其实就是关系型数据库本身就需要解决的问题。
    首先,他们同时被MySQL执行,你的意思其实就是他们是并发执行的,而并发执行的事务在关系型数据库中是有专门的理论支持的-ACID,事务并行等理论,所有关系型数据库实现,包括Oracle,MySQL都需要遵循这个原理
    简单一点理解就是锁的原理。这个时候第一个update会持有id=1这行记录的排它锁,第二个update需要持有这个记录的排它锁的才能对他进行修改,正常的话,第二个update会阻塞,直到第一个update提交成功,他才会获得这个锁,从而对数据进行修改。
    也就是说,按照关系型数据库的理论,这两个update都成功的话,id=1的number一定会被修改成22。如果不是22,那就是数据库实现的一个严重的bug
    如果想要深入了解,可能你要预读一下数据库与事务处理 (豆瓣)了。
    粗浅一点的,可以先看一下如果有人问你数据库的原理,叫他看这篇文章和英文原文How does a relational database work

    ============================
    4.18更新

    有一种情况,会出现不“原子”的情况:

    调用sql的函数加上事务,且rc以上的隔离级别

    线程A              线程B

    开启事务

                         开启事务

    x=x+1

                         x=x+1

    commit

                           commit

    如果是事务情况下,B在A未提交事务的情况下,是读不到A线程x+1的结果的,那么实际还是累加的1,错误:

    以下的代码就会出错                (例子1)标准的防脏读          

        @Transactional(isolation = Isolation.READ_COMMITTED)
        public List<Map<String,Object>> listForDirtyReadOneFunction() {
            List<Map<String,Object>> map = jdbcTemplate.queryForList("select * from tao where col1 = 0");
    
    //        try {
    //            Thread.sleep(10000);
    //        } catch (InterruptedException e) {
    //            e.printStackTrace();
    //        }
    
            jdbcTemplate.update("update tao set col2 = 'xxxx' where col1 = 0");
    
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            return map;
        }

    真的是这样么,又反转了

    仔细来看这个例子:

    这个例子是一个典型防脏读的例子,读取是快照读,而我们本文讨论的update是当前读,不可同日而语

    例子2:查看当前读(含update)与rc隔离级别的关系,实践证明当前读在事务中会阻塞其它当前读,直到本事务提交
        @Transactional(isolation = Isolation.READ_COMMITTED)
        public List<Map<String,Object>> listForDirtyReadOneFunction() {
    
            jdbcTemplate.update("update tao set col3 = col3+1 where col1 = 0");
            List<Map<String,Object>> map = jdbcTemplate.queryForList("select * from tao where col1 = 0");
    
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            return map;
        }

    这个例子,无论如何col3都被累加了2次,故

    这个模型是错误的,实践下来是这样的:

    开启事务

                            开启事务

    x=x+1              阻塞

    sleep 10s              

    commit

                             x=x+1

                            sleep 10s

                              commit

    其中当中两句update,都是当前读,都使用lbcc先取得锁,继而读到最新的数据

    而快照读则符合我们对脏读一般的理解,使用mvcc读历史数据


    脏读定义:对于其它事务已执行未提交的数据,本事务的快照读读取到了,注意是快照读

    在rc以上隔离级别下,快照读使用mvcc读取历史数据,而update是当前读,读取时会读到最新数据,会给数据加锁后修改

    看下例子:

        @Transactional
        public void insertForDirtyReadAndIllusion () {
            jdbcTemplate.execute("insert into tao values (1,'d',0)");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        //    int a = 1/0;
        }
    // a先执行insert 睡觉 b执行此函数,查看是否能更改到a未提交但已插入的数据
        // 实践证明:读到且改到,随着a的睡眠而阻塞
        @Transactional(isolation = Isolation.READ_COMMITTED)
        public List<Map<String,Object>> updateReadForDirtyRead() {
            jdbcTemplate.update("update tao set col2 = 'update' where col1 = 1");
            return null;
        }

    这里得到一个很重要的结论   当前读(包含update等写入操作)锁定数据,直到事务提交

    再来看这个模型:

    根本不存在的,x=x+1未commit前,其它线程除了快照读,是读写不到x的


    附:


  • 相关阅读:
    硬盘安装Win 7系统Windows 7 系统硬盘安装教程(图解)
    修改phpMyAdmin导入SQL文件的大小限制
    金三银四面试季节之Java 核心面试技术点
    2015年校园招聘12家IT公司面试体验
    正则表达式小结
    【译文】NginScript – 为什么我们要实现自己的JS引擎?
    经典算法合集
    【高级JSE技术】线程池
    【高性能服务器】Tomcat剖析
    【高性能服务器】Nginx剖析
  • 原文地址:https://www.cnblogs.com/silyvin/p/9106666.html
Copyright © 2020-2023  润新知