• update 修改单表的多个字段,造成数据混乱


    1、问题描述

    今天 QQ群里在讨论一个问题,在某个环境里面,需要修改单个表的多个字段,造成了数据混乱,跟理想修改的数据不一致。

    1.1 模拟问题现象

    # 注意: 创建的表没有主键,且 t1 表是 innodb 引擎
    
    root@localhost [keme]>create table t1 (a int,b int,primary key(a));
    Query OK, 0 rows affected (0.02 sec)
    root@localhost [keme]>insert into  t1  values (1,10),(2,20),(3,30);
    Query OK, 3 rows affected (0.02 sec)
    Records: 3  Duplicates: 0  Warnings: 0
    
    root@localhost [keme]>select * from t1;
    +------+------+
    | a    | b    |
    +------+------+
    |    1 |   10 |
    |    2 |   20 |
    |    3 |   30 |
    +------+------+
    
    # 进行修改多个字段
    root@localhost [keme]>update t1 set a=a+1,b=a+10 where a=2;
    Query OK, 1 row affected (0.01 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    
    root@localhost [keme]>select * from t1;
    +------+------+
    | a    | b    |
    +------+------+
    |    1 |   10 |
    |    3 |   13 |
    |    3 |   30 |
    +------+------+
    3 rows in set (0.00 sec)
    

    修改的条件是a=2,为啥修改的结果是(3,13),不应该是(3,12)吗?

    是不是很多人以为修改的结果是(3,12),是不是感觉数据是乱的,如果一条数据改回正常还是挺简单,关键数据量很多改的就费劲了啊,时间还长。

     1.2 问题故障原因

    因为 update  是当前读,读取的是记录数据的最新版本,就是  

    update t1 set a=a+1,b=a+10 where a=2;
    update a=a+1 因为要做当前读  现在a =2 +1 后  a=3 ,保证最新值
    在做  b=a+10  
    也需要读到a值的最新的值 还要加锁, 现在a的值已经变成3 ,  在加10不就变成13
    

     

    1.3 解决故障

    # 我先改回原来数据
    root@localhost [keme]>update t1 set a=2,b=20 where a=3 and b=13;
    
    
    # 可以把 a 列暂存在一个临时变量里
    root@localhost [keme]>select a into @a from t1 where a=2;
    
    # 修改数据
    root@localhost [keme]>update t1 set a=@a+1,b=@a+10 where a=@a;
    
    #验证结果 是不是 (3,12)
    

    2、问题总结

    我们需要知道一些相关原理: 快照读,当前读

    2.1、快照读(select)

    执行select的时候,innodb默认会执行快照读,快照读,也就是读取快照的数据,数据虽然是一致的,但是数据是历史数据。

    快照读:只是简单的 select ,不包括 select ... lock in share mode, select ... for update

    2.2、当前读

    select ... lock in share mode

    select ... for update

    insert

    update

    delete

    当你执行这几个操作的时候默认会执行当前读,也就是会读取最新的记录,也就是别的事务提交的数据你也可以看到。

    update 执行当前读,然后把返回的数据加锁,之后执行update。

    加锁是防止别的事务在这个时候对这条记录做什么,默认加的是排他锁,也就是只允许读,其他都不可以,这样就可以保证数据不会出错了。

    3、延伸阅读

    上面的情况是:

    • innodb 引擎
    • 表中无主键

    如果表中有主键,或者是其他引擎,正确操作,请看下面的大神文章

    https://imysql.com/2008_06_17_sth_about_update_duplicate_key
  • 相关阅读:
    jpa summary
    web service 参考文档
    jap 事务总结
    jap 事务
    PriorityQueue优先队列深入解析(含源码分析,方法使用)
    【LeetCode】703.数据流中的第K大元素(二叉搜索树解法,java实现)
    做移动端的你竟然不知道Flex布局?快来看看!
    【LeetCode】450.删除二叉搜索树中的节点(java实现,详细图解)
    #linux vscode 保存总提示“Retry as sudo”
    java 面向对象(十八):包装类的使用
  • 原文地址:https://www.cnblogs.com/keme/p/13101115.html
Copyright © 2020-2023  润新知