zoukankan      html  css  js  c++  java
  • 我对数据库事务的理解(MYSQL中)

    -- 设置数据库事务为手动的提交
    SET @@AUTOCOMMIT = 0;

    -- 查看是否被修改
    SELECT @@autocommit;

    -- 查看当前的编码格式
    SELECT @@character_set_results;




    -- 账户表
    CREATE TABLE account(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(20),
    balance INT
    );


    INSERT INTO account(NAME,balance) VALUES('mzy',5000);
    INSERT INTO account(NAME,balance) VALUES('jacky',5000);


    SELECT * FROM account;
    UPDATE account SET balance=5000;
    DELETE FROM account;

    -- 设置为手动提交之后,必须手动commit
    COMMIT;


    -- 关于事务的四种特性中的隔离性的理解:
    -- 1.首先调整数据库事务隔离性的级别
    -- 隔离级别 脏读 不可重复读 幻读
    -- Serializable         否          否           否
    -- Repeatable Read      否          否           是
    -- Read Committed       否          是           是
    -- Read Uncommitted     是          是           是


    -- mysql默认的话: Repeatable Read
    -- oracle默认的话:Read Committed










    -- 修改事务的等级:最低的级别Read Uncommitted:
    SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    -- 1. 测试脏读
    -- 查看当前的级别:
    SELECT @@global.tx_isolation;


    脏读的理解:
    一个事务读到了另一个事务未提交更新的数据!
    假如这个事务未commit,只是当前操作可以看见,
    但是未提交事务!

    例如具体业务中:
    买家点击货物(假定价格100元),进入付款的界面了,
    但是没有执行最后的提交付款(COMMIT),但是也没有
    取消订单(ROLLBACK)【注意是相当于保持这个操作】。
    但是通知卖家查看余额的时候,就会发现余额多了100元,
    卖家一发货,就相当于损失了100元。


    SET @@AUTOCOMMIT = 0; -- 首先设置事务提交为手动


    -- 我先给卖家jacky打2000,让他发货
    UPDATE account SET balance=balance-2000 WHERE NAME='mzy';
    UPDATE account SET balance=balance+2000 WHERE NAME='jacky';


    -- jacky查余额增加了2000
    SELECT * FROM account WHERE NAME='jacky';


    -- 然后我取消订单进行混滚了
    ROLLBACK;












    -- 修改事务的等级:倒数第二级别Read Committed:
    -- read committed 比 read uncommitted 多了防止脏读
    SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
    -- 2. 测试不可重复读
    -- 查看当前的级别:
    SELECT @@global.tx_isolation;


    不可重复读:
    一个事务得到了另一个事务已提交更新(update)的数据!

    需要和上面的脏读区分出来的是,我们的脏读是站在用户的角度来看的;
    但是不可重复读,是站在银行的角度来讲的,在银行的需求中,当前的
    报备和日志需要统一!!
    什么意思呢?
    比如在一次银行对用户的存款进行查账的操作中,操作的数据
    保持一致!
    例如:我们当前的正在查A用户,查到A用户的存款有3000元,
    但是在我们开始记录的那一个时刻点,A用户又进行存款的操作;
    A用户又存了2000元;如果没有预防不可重复读的话,那么在一次
    事务的操作中,如果需要多次记录结果:第一次记录结果就为3000元;
    第二次记录结果就为5000元,造成两个记录的结果不同;

    意思就是,两个并发进行的事务不能相互影响,这种修改的结果,只能在
    当前事务结束了之后,存入,修改!

    脏读是必须避免的,但是不可重复读,有些时候也是需要的;

    -- 不可重复读的测试应该是两个窗口才能进行:不可重复读原理是并发并行的事务;


    -- 模拟:窗口一
    -- jacky存入2000元
    UPDATE account SET balance = banlance + 2000 WHERE account.`NAME` = 'jacky';




    -- 模拟:窗口二(在窗口一的update操作前打开)
    -- 在没有防止不可重复读的时候,这里的结果应该是修改了之后的结果
    -- 在防止了不可重复读之后,这里的结果应该是修改前的,
    -- 当退出当前事务之后,再次进入事务,才是修改后的结果。
    SELECT account.`balance` FROM account WHERE account.`NAME` = 'jacky';







    -- 修改事务的等级:倒数第三级别REPEATABLE READ:
    SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
    -- 3.测试幻读
    -- 查看当前的级别:
    SELECT @@global.tx_isolation;


    幻读:
    一个事务读到另一个事务,新插入(insert)的数据。
    mysql中看不到幻读,但是能防止幻读?
    只能看最高级别防止了幻读后倒推!

    SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;


    -- 假如,现在银行中新加了一个用户,也是并发的事务;
    -- 其中如果是显示添加前的mzy和jacky两个账户就是防止了幻读
    -- 如果计算上了新加的用户,就是幻读


    -- 窗口一:
    SELECT COUNT(account.`NAME`) FROM account;




    -- 窗口二:
    -- mysql中看不到幻读
    INSERT INTO account VALUES(....); -- 直接卡住,不会让你插入
    -- 因为如果你插入的话,就会影响到上面的查询




    -- 倒推:如果没有防止幻读的话,级别不是SERIALIZABLE,而是REPEATABLE READ
    -- 就会在一次查询中并发的两个事务,产生幻读首先查询count(*)为2
    -- 在insert之后,查询就为3


    -- 和防止可重复读不同的是,可重复读运行用户修改(update),但是另一个事务当前不会
    -- 发生变化,在重新开启的时候才会发生变化。


    -- 防止幻读就很简单粗暴,当在查询的时候,就禁止用户开户(insert)


    -- 隔离等级:但是安全性往往会带来性能上的缺失
    -- 我觉得在一般的业务中防止脏读就可以了,可重复读和幻读都是可以接受的。
  • 相关阅读:
    dhl: ASP.NET MVC1.0 的图片(文件)上传功能
    一些不错的sql语句
    ASP.NET使用Memcached高缓存实例
    教你加快Win7 的启动速度
    写个VS2008使用单元测试NUnit的方法,希望对大家有帮助
    C#中ToString格式大全
    JQuery UI accordion学习笔记
    dhl:Sql表子查询
    windows 7 怎么在文件夹内开启图片预览功能
    教你如何将 优酷网等视频网站的视频外链时自动播放
  • 原文地址:https://www.cnblogs.com/mzywucai/p/11053490.html
Copyright © 2011-2022 走看看