zoukankan      html  css  js  c++  java
  • MySQL---事务知识,你搞明白没有?

    MySQL - 事务

    在学习事务这一概念前,我们需要需要构思一个场景

    场景构思

    假设该场景发生于一个银行转账背景下,月中,又到了发工资的日子。学校打算给A老师发放一个月的工资。(此处,我们假设转账都是由人工操作的),整个过程本应该如下:

    1. 学校财务核对A老师工资单

    2. 确认学校账上还有这么多钱

    3. 向银行提出转账申请,银行扣除学校财务卡上的指定金额

    4. 银行向A老师工资卡打入指定金额

    5. 银行汇报双方交易完成

      但是,当这个过程执行完第3步的时候,突然大断电!整个电力系统进入瘫痪。待电力系统回复之后,银行并不会继续执行4、5步甚至连1,2,3步的操作记录都丢失了。此时出现了如下的问题:

    • 学校认为,工资已经发出

    • A老师认为,学校还没有发工资

    • 银行认为,从来就没有发生过转账的事情

      其实整个过程可以用一个词来描述:数据库中的数据产生了“不一致性”

    一致性

    上述背景中设计到了一个概念,叫做不一致性,这是和一致性相对的概念。那么,什么是一致性呢?

    一致性的意思是,在一系列数据库行为的前后两个时间点上,数据是正确对应的。放在上面的例子来看,就是操作前后,两个账户的总金额是一样的,这样就保证不会凭空的丢失掉不该丢失掉的金钱。

    原子操作

    为了保证数据的一致性,我们可以将一系列会破坏一致性的操作声明为原子操作。被声明为原子操作的那些操作要么一起完成,要么一起失败,这样我们就避免了类似断电这类情况导致的数据不一致性。

    事务

    那么如何才能实现MySQL中的原子操作呢?

    MySQL以及大多数关系型数据库都提供了一个叫做事务的技术。我们可以声明一个事务的开始,在确认提交或者指明放弃前的所有操作,都先在一个叫做事务日志的临时环境中进行操作。待操作完成,确保了数据一致性之后,那么我们可以手动确认提交,也可以选择放弃以上操作。

    注意: 一旦选择了提交,那么便不能再利用放弃操作来撤销更改了。

    案例分析

    ​ 我们首先创建我们本次案例需要使用的表,并给一些测试数据

     
    mysql> SHOW DATABASES;#查看有多少数据库
    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | mysql              |
    | performance_schema |
    | python             |
    | sys                |
    +--------------------+
    5 rows in set (0.00 sec)
    mysql> USE `python`;#切换数据库
    Database changed
    mysql> SELECT DATABASE();#查看当前使用的数据库
    +------------+
    | DATABASE() |
    +------------+
    | python     |
    +------------+
    1 row in set (0.00 sec)
    mysql> CREATE TABLE `account` (
        ->   `id` int PRIMARY KEY AUTO_INCREMENT,
        ->   `name` VARCHAR(20) NOT NULL,
        ->   `balance` DECIMAL(12,2)
        -> );
    Query OK, 0 rows affected (0.04 sec)
    mysql> INSERT INTO `account`(`name`, `balance`)
        ->   VALUES ('TanzhouEDU', 10000000),('Tuple', 10000)
        -> ;
    Query OK, 2 rows affected (0.70 sec)
    Records: 2  Duplicates: 0  Warnings: 0

    断电案例(非原子操作)

    首先,查看现有的账户情况

     
    mysql> SELECT * FROM `account`;
    +----+------------+-------------+
    | id | user       | balance     |
    +----+------------+-------------+
    |  1 | TanzhouEDU | 10000000.00 |
    |  2 | Tuple      |    10000.00 |
    +----+------------+-------------+
    2 rows in set (0.00 sec)

    现在,学校财务开始向A老师发工资

    mysql> UPDATE `account`
        ->   SET `balance` = `balance` - 10000
        ->   WHERE `user` = 'TanzhouEDU'
        -> ;
    Query OK, 1 row affected (0.05 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    mysql> SELECT * FROM `account`;
    +----+------------+------------+
    | id | user       | balance    |
    +----+------------+------------+
    |  1 | TanzhouEDU | 9990000.00 |
    |  2 | Tuple      |   10000.00 |
    +----+------------+------------+
    2 rows in set (0.00 sec)

    此时,发生了断电,再登录数据库以后,钱已经消失了。

    事务案例(原子操作)

    MySQL中,事务操作包括4个:

    • START TRANSACTION:开始一个新的事务

    • COMMIT:提交当前事务,做出永久改变

    • ROLLBACK:回滚当前事务,放弃修改

    • SET autocommit = {0 | 1}:对当前会话禁用(0)或启用(1)自动提交模式

      我们利用事务机制来重复上述断电的情况。看看是否能够有所帮助。

    mysql> SELECT * FROM `account`;
    +----+------------+------------+
    | id | user       | balance    |
    +----+------------+------------+
    |  1 | TanzhouEDU | 9990000.00 |
    |  2 | Tuple      |   10000.00 |
    +----+------------+------------+
    2 rows in set (0.00 sec)
    mysql> START TRANSACTION;
    Query OK, 0 rows affected (0.00 sec)
    mysql> UPDATE `account`
        ->   SET `balance` = `balance`-1000
        ->   WHERE `user` = 'TanzhouEDU'
        -> ;
    Query OK, 1 row affected (0.01 sec)
    mysql> SELECT * FROM `account`;
    +----+------------+------------+
    | id | user       | balance    |
    +----+------------+------------+
    |  1 | TanzhouEDU | 9989000.00 |
    |  2 | Tuple      |   10000.00 |
    +----+------------+------------+
    2 rows in set (0.00 sec)
    mysql> exit
    Bye
    tuple@MyVM:~$ mysql -utuple -p123456
    mysql> USE `python`;
    Database changed
    mysql> SELECT * FROM `account`;
    +----+------------+------------+
    | id | user       | balance    |
    +----+------------+------------+
    |  1 | TanzhouEDU | 9990000.00 |
    |  2 | Tuple      |   10000.00 |
    +----+------------+------------+
    2 rows in set (0.00 sec)

    我们可以看到,数据在断电后,自动恢复到了数据修改前的样子,它相当于如下一个操作过程。

     
    mysql> START TRANSACTION;
    Query OK, 0 rows affected (0.00 sec)
    mysql> UPDATE `account`
        ->   SET `balance`=`balance`-1000
        ->   WHERE `user`='TanzhouEDU'
        -> ;
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    mysql> SELECT * FROM `account`;
    +----+------------+------------+
    | id | user       | balance    |
    +----+------------+------------+
    |  1 | TanzhouEDU | 9989000.00 |
    |  2 | Tuple      |   10000.00 |
    +----+------------+------------+
    2 rows in set (0.00 sec)
    mysql> ROLLBACK;
    Query OK, 0 rows affected (0.02 sec)
    mysql> SELECT * FROM `account`;
    +----+------------+------------+
    | id | user       | balance    |
    +----+------------+------------+
    |  1 | TanzhouEDU | 9990000.00 |
    |  2 | Tuple      |   10000.00 |
    +----+------------+------------+
    2 rows in set (0.00 sec)

    接着,我们来做一次正确的操作,真正的给A老师发一次工资。

    mysql> START TRANSACTION;
    Query OK, 0 rows affected (0.00 sec)
    mysql> UPDATE `account`
        ->   SET `balance` = `balance`-10000
        ->   WHERE `user` = 'TanzhouEDU'
        -> ;
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    mysql> UPDATE `account`
        ->   SET `balance` = `balance`+10000
        ->   WHERE `user`= 'Tuple'
        -> ;
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    mysql> SELECT * FROM `account`;
    +----+------------+------------+
    | id | user       | balance    |
    +----+------------+------------+
    |  1 | TanzhouEDU | 9980000.00 |
    |  2 | Tuple      |   20000.00 |
    +----+------------+------------+
    2 rows in set (0.00 sec)
    mysql> COMMIT;
    Query OK, 0 rows affected (0.03 sec)
    mysql> ROLLBACK;
    Query OK, 0 rows affected (0.00 sec)
    mysql> SELECT * FROM `account`;
    +----+------------+------------+
    | id | user       | balance    |
    +----+------------+------------+
    |  1 | TanzhouEDU | 9980000.00 |
    |  2 | Tuple      |   20000.00 |
    +----+------------+------------+
    2 rows in set (0.00 sec)

    通过上面的例子可以看出,一旦commit了,那么rollback还是断电都不能反悔了。

  • 相关阅读:
    gitlab搭建
    java数组
    安裝nextcloud
    Spring的定时任务@Scheduled(cron = "0 0 1 * * *")
    java内存结构(下)
    java内存结构(上)
    多线程的三个特性
    @RequestBody用法
    SwaggerAPI注解详解(转载)
    在jpanel中添加jbutton并自由设置按钮大小和位置
  • 原文地址:https://www.cnblogs.com/ivanpan/p/6653990.html
Copyright © 2011-2022 走看看