zoukankan      html  css  js  c++  java
  • 关于MySQL的一致性读

    MySQL的一致性读

    toc

      数据可用性:正确性、完整性、一致性。这是我们进行数据备份时的要求,如果无法保证备份数据的可用性那么备份数据也就失去了意义。前两个性质很好理解,但是一致性具体是什么呢?

    一、什么是一致性读

    1.一致性的定义

    数据的一致性:指相关联的数据之间的逻辑关系是否正确。
    数据库的一致性:指数据库从一个一致性状态变成另一个状态。这期间数据可能会发生变化但是状态不会改变。

    2.对一致性的分析

    关于数据的一致性,举个例子当前时间中午11点,商城一台电脑价值5000元我的账户下刚好有5000元我是买得起的但是我没有买,中午12点我从账户取出1000元买其他东西此时余额为4000元已经无法购买电脑了。可以说现在买不起这台电脑但是不能说一直买不起,因为在12点的时候是有足够的钱的,也就是说不能拿我现在的余额放到11点时的逻辑关系中这是不一样的,11点时的逻辑关系中相对应的是11点时我的余额这才保证了数据的一致性。
    关于数据库的一致性,这个就比较好理解了还是一家银行,这个银行只有A,B两人,A存了2w,B存3w。A给B转了1w,此时A余额为1w,B余额为4w。这就是两个一致性状态的切换。因为对银行而言总额是不变的一直是5w。虽然里面AB的数据是有变化的。

    二、MySQL怎样保证数据的一致性

    数据的一致性在数据库备份中有比较明显的体现。我们在一个时间点开始备份怎样能保证所有数据在这个时间点后都不会发生改变呢?
    加锁:针对备份策略,将所有涉及的表都加上锁,保证在备份结束之前所有的表都不会被修改。这样不管这次备份持续多长时间,都可以确保数据始终一致在备份开始的时候。但是这种缺点也很明显,锁表对数据库影响比较大,这期间不能对数据库做任何写操作,只能读。
    快照:在备份开始时对目标数据做一个快照,因为快照记录了那个时刻所有数据的样子,所以在这个快照范围内所有的数据都具有一致性。如果存储引擎为innodb那么利用事务的隔离性就可以保证数据的一致性。也就实现了快照功能。事务可重读隔离级别可以实现数据的一致性,虽然可重读可能因为更新数据导致幻读,但是数据备份是个只读操作。所以只要保证备份操作放在一个单独的事务中即可,其他对数据库的操作是别的事务中的因为隔离性这个特点并不会影响其他事务。这样就不会出现幻读了也保证了在备份过程中,保证了数据的一致性读。

    三、可重读隔离级别的一致性读

    上面说的一致性读也被称为快照读。具体就是上面快照的含义。接下来我们来模拟一下这个场景。

    四、模拟测试

    1)需要将两个数据库会话中的事务隔离级别都设置为可重读,然后在两个会话中都开启两个事务。
    首先关闭两个会话的自动提交,设置会话隔离模式为可重读并开启一个事务。

    mysql> set autocommit=0;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> show variables like 'autocommit';
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    | autocommit    | OFF   |
    +---------------+-------+
    
    mysql> set session transaction isolation level repeatable read;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select @@transaction_isolation;
    +-------------------------+
    | @@transaction_isolation |
    +-------------------------+
    | REPEATABLE-READ         |
    +-------------------------+
    1 row in set (0.00 sec)
    
    mysql> start transaction;
    Query OK, 0 rows affected (0.00 sec)

    注意会话区别:
    会话一:

    会话二:

    2)两个会话分别查看测试表信息如下:
    会话一:

    会话二:

    3)在会话二中进行数据更新插入一条数据并提交:

    4)在会话一中查看数据情况,可以看到此时数据并没有变化,在会话一中插入数据提交后,看到会话二新插入的数据发生了幻读。

    解释:可以看到在会话二中做完插入操作查看这个测试表已经4条数据了,但是在会话一中看还是只有3条数据。这时我们在会话一做更新数据的操作,也就是插入数据然后提交却发现多出两条数据,这就是可重读级别下的幻读。
    (插入操作并不会起到更新全部数据的效果,只会更新自己插入的数据,起到作用的是commit,将全部数据更新出来包括会话二插入的数据。这里可以适用update做更新数据的操作。update语句并没有指定任何条件,相当于更新表中的所有行的对应字段,如果你指定了条件,并且没有更新到"隐藏"的行,那么可能无法看到幻读现象。)

    在备份操作中,我们所有的操作都是读操作并不涉及到更新数据,所以当我们把所有的备份操作都放到一个单独的‘事务’中,并且此事务将事务隔离级别设置为可重读,是不会出现幻读问题的,也就是达到了在某一时间点读取数据数据一致性的目的。


    那么是不是只要在可重读隔离模式下,启动一个事务就相当于给当时的数据库打了一个快照?答案是否定的,具体测试如下:

    1)仍然使用上面实验,如果是新打开的会话需要重新设置隔离模式以及关闭自动提交。查看此时两个会话查询测试表的信息如下:
    会话一:

    会话二:

    2)在会话一中启动一个事务,其余什么操作也不做,用来观察

    3)在会话二中做数据插入操作并提交。数据成功插入。

    4)在会话一中进行查询,可以看到数据被更新了,并不是我们想的那样被打了快照。

    那么这是为什么呢?第一个模拟实验是成功实现的,第二个却出现问题。而二者的差别在哪里?其实第一个实验中在启动会话后进行了一次查询也就是那个时候快照才真正的开始,而第二次实验我们并没有在会话开启后进行查询,所以启动会话并不是直接打快照。也就是说不是以start transaction语句开始的时间点作为"快照"建立的时间点。官方文档有这样的说明:
    If the transaction isolation level is REPEATABLE READ (the default level), all consistent reads within the same transaction read the snapshot established by the first such read in that transaction. You can get a fresher snapshot for your queries by committing the current transaction and after that issuing new queries.
    也就是说,当事务处于"可重读"隔离级别时,并不是事务开始时就代表快照建立,而是事务中的第一个查询语句执行时,快照点才会被建立。

    五、结论

      那么针对上述现象应该怎样才能在指定的时间点开启一个快照呢?难道要每次开启一个事务后都进行一次查询才能实现吗?MySQL已经为我们提供了另一种选择,在启动会话时指定参数即可。START TRANSACTION WITH consistent snapshot
      使用start transaction with consistent snapshot;命令启动事务,就表示启动事务的同时就建立快照,也就是说,只要事务开始,就能保证"一致性读"。





  • 相关阅读:
    【HDOJ】1243 反恐训练营
    Eclipse 点击 Run 自动生成 out 文件的错误
    经纬度转凯立德 K 码
    Android开发环境建立
    Android 学习过程中遇到的知识点
    Android
    Android
    素数距离问题
    取石子(一)
    素数求和问题
  • 原文地址:https://www.cnblogs.com/plutozzl/p/13433233.html
Copyright © 2011-2022 走看看