zoukankan      html  css  js  c++  java
  • MySql事务隔离级别

    在讲mysql事物隔离级别之前,我们先简单说说mysql的锁和事务。

    一:数据库锁

    因为数据库要解决并发控制问题。在同一时刻,可能会有多个客户端对同一张表进行操作,比如有的在读取该行数据,其他的尝试去删除它。为了保证数据的一致性,数据库就要对这种并发操作进行控制,因此就有了锁的概念。

    锁的分类

    从对数据库操作的类型分

    读锁(共享锁):针对同一块数据,多个读操作可以同时进行而不会互相影响。由读表操作加上的锁,加锁后其他用户只能获取该表或行的共享锁,不能获取排它锁,也就是说只能读不能写。

    写锁(排它锁):当当前写操作没有完成之前,它会阻断其他写锁和读锁。由写表操作加上的锁,加锁后其他用户不能获取该表或行的任何锁。

    从锁定的数据范围分

    表锁:锁定某个表。

    行锁 :锁定某行。

    为了尽可能 提高数据库的并发度,每次锁定的数据范围越小越好。理论上每次只锁定当前操作的数据的方案会得到最大的并发度,但是管理锁是很耗费资源的事情。因此数据库系统需要在高并发响应和系统性能两方面进行平衡,这样就产生了“锁粒度”的概念。

    锁粒度

    表锁:管理锁的开销最小,同时允许的并发量也最小的锁机制。MyIsam存储引擎使用的锁机制。当要写入数据时,把整个表都锁上,此时其他读、写动作一律等待。在MySql中,除了MyIsam存储引擎使用这种锁策略外,MySql本身也使用表锁来执行某些特定动作,比如alter table.

    行锁:可以支持最大并发的锁策略。InnoDB和Falcon两张存储引擎都采用这种策略。

    MySql是一种开放的架构,你可以实现自己的存储引擎,并实现自己的锁粒度策略,不像Oracle,你没有机会改变锁策略,Oracle采用的是行锁。从大到小,mysql服务器仅支持表级锁,行锁需要存储引擎完成。粒度越精细,并发性越好。即行锁的并发性最好,但需要存储引擎的支持。

    二:事务

    从业务角度出发 ,对数据库的一组操作要求保持4个特征:

    Atomicity:原子性。

    Consistency:一致性。

    Isolation:隔离性。

    Durability:持久性。

    为了更好地理解ACID,以银行账户转账为例: 

    1 START TRANSACTION;

    2 SELECT balance FROM checking WHERE customer_id = 10233276;

    3 UPDATE checking SET balance = balance - 200.00 WHERE customer_id = 10233276;

    4 UPDATE savings SET balance = balance + 200.00 WHERE customer_id = 10233276;

    5 COMMIT;

    原子性:要么完全提交(10233276的checking余额减少200,savings 的余额增加200),要么完全回滚(两个表的余额都不发生变化)。

    一致性:这个例子的一致性体现在 200元不会因为数据库系统运行到第3行之后,第4行之前时崩溃而不翼而飞,因为事物还没有提交。 

    隔离性:允许在一个事务中的操作语句会与其他事务的语句隔离开,比如事务A运行到第3行之后,第4行之前,此时事务B去查询checking余额时,它仍然能够看到在事务A中被减去的200元,因为事务A和B是彼此隔离的。在事务A提交之前,事务B观察不到数据的改变。

    持久性:这个很好理解。

    事务跟锁一样都会需要大量工作,因此你可以根据你自己的需要来决定是否需要事务支持,从而选择不同的存储引擎。 

    三:事务隔离级别

    SQL标准定义了4中隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

    Read Uncommitted(读取未提交内容) 

    在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。  

    Read Committed(读取提交内容)

    这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。 

    Repeatable Read(可重读)

    这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。 不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时, 另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。 InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。 

    Serializable(可串行化)  

    这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

     这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:

    脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

    幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

    不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

    案例:

    Read Uncommitted(读取未提交内容) 

    在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

    1.分别在A、B两个客户端执行: 

    2
    3. A: 
    4. root@(none) 10:54>SET GLOBAL tx_isolation='READ-UNCOMMITTED';
    5. Query OK, 0 rows affected (0.00 sec)
    6
    7. root@(none) 10:54>SELECT @@tx_isolation;
    8+------------------+
    9| @@tx_isolation |
    10+------------------+
    11| READ-UNCOMMITTED |
    12+------------------+
    131 row in set (0.00 sec)
    14
    15. root@(none) 10:54>use test;
    16Database changed
    17. root@test 10:55>begin;
    18. Query OK, 0 rows affected (0.00 sec)
    19
    20. root@test 10:55>select * from test1;
    21+------+
    22| a |
    23+------+
    24| 1 |
    25| 2 |
    26| 3 |
    27| 4 |
    28+------+
    294 rows in set (0.00 sec)
    30
    31. B上: 
    32. root@test 10:58>select @@tx_isolation;
    33+------------------+
    34| @@tx_isolation |
    35+------------------+
    36| READ-UNCOMMITTED |
    37+------------------+
    381 row in set (0.00 sec)
    39
    40. root@test 10:58>
    41. root@test 10:58>begin;
    42. Query OK, 0 rows affected (0.00 sec)
    43
    44. root@test 10:58>insert into test.test1 values (999);
    45. Query OK, 1 row affected (0.00 sec)
    46
    47. root@test 10:58>select * from test.test1;
    48+------+
    49| a |
    50+------+
    51| 1 |
    52| 2 |
    53| 3 |
    54| 4 |
    55| 999 |
    56+------+
    575 rows in set (0.00 sec)
    58. 此处B客户端并未commit;
    59
    60. 再查看A客户端: 
    61. root@test 10:58>select * from test1;
    62+------+
    63| a |
    64+------+
    65| 1 |
    66| 2 |
    67| 3 |
    68| 4 |
    69| 999 |
    70+------+
    715 rows in set (0.00 sec)
    72

    73. 此处A可以看到新的记录了。  

    Read Committed(读取提交内容)

    这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。 这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。 

    1.在A客户端:

    2. root@(none) 11:10>SET GLOBAL tx_isolation='READ-COMMITTED';
    3. Query OK, 0 rows affected (0.00 sec)
    4
    5. root@(none) 11:10>SELECT @@tx_isolation;
    6+----------------+
    7| @@tx_isolation |
    8+----------------+
    9| READ-COMMITTED |
    10+----------------+
    111 row in set (0.00 sec)
    12
    13. root@(none) 11:10>
    14. root@(none) 11:10>begin;
    15. Query OK, 0 rows affected (0.00 sec)
    16
    17. root@(none) 11:10>select * from test.test1;
    18+------+
    19| a |
    20+------+
    21| 1 |
    22| 2 |
    23| 3 |
    24| 4 |
    25+------+
    26
    27. 在B客户端执行:
    28. root@test 11:11>begin;
    29. Query OK, 0 rows affected (0.00 sec)
    30
    31. root@test 11:11>select * from test.test1;
    32+------+
    33| a |
    34+------+
    35| 1 |
    36| 2 |
    37| 3 |
    38| 4 |
    39+------+
    404 rows in set (0.00 sec)
    41
    42. root@test 11:11>
    43. root@test 11:11>delete from test.test1 where a=1;
    44. Query OK, 1 row affected (0.00 sec)
    45
    46. root@test 11:12>select * from test.test1;
    47+------+
    48| a |
    49+------+
    50| 2 |
    51| 3 |
    52| 4 |
    53+------+
    54
    55. 此时查询A客户端:
    56
    57. root@(none) 11:12>select * from test.test1;
    58+------+
    59| a |
    60+------+
    61| 1 |
    62| 2 |
    63| 3 |
    64| 4 |
    65+------+
    66. 此处看出A客户端无变化,在B客户端执行commit后再查看A客户端:
    67
    68. root@(none) 11:13>select * from test.test1;
    69+------+
    70| a |
    71+------+
    72| 2 |
    73| 3 |
    74| 4 |
    75+------+
    76
    77. 可以看到A客户端的数据已经变了。已提交读只允许读取已提交的记录,但不要求可重复读。
    78. 用MVCC来说就是读取当前行的最新版本。 

     Repeatable Read(可重读)

    这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。 不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。 简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。 InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。 

    1.在A客户端上:

    2. root@(none) 11:17>SET GLOBAL tx_isolation='REPEATABLE-READ';
    3. Query OK, 0 rows affected (0.00 sec)
    4
    5. root@(none) 11:17>
    6. root@(none) 11:17>
    7. root@(none) 11:17>SELECT @@tx_isolation;
    8+-----------------+
    9| @@tx_isolation |
    10+-----------------+
    11| REPEATABLE-READ |
    12+-----------------+
    131 row in set (0.00 sec)
    14
    15. root@(none) 11:17>BEGIN;
    16. Query OK, 0 rows affected (0.00 sec)
    17
    18. 在B客户端上:
    19. root@test 11:20>select @@tx_isolation;
    20+-----------------+
    21| @@tx_isolation |
    22+-----------------+
    23| REPEATABLE-READ |
    24+-----------------+
    251 row in set (0.00 sec)
    26
    27. root@test 11:20>insert into test.test1 values (555);
    28. Query OK, 1 row affected (0.00 sec)
    29
    30. root@test 11:20>commit;
    31. Query OK, 0 rows affected (0.00 sec)
    32
    33. root@test 11:21>
    34. root@test 11:21>select * from test.test1;
    35+------+
    36| a |
    37+------+
    38| 2 |
    39| 3 |
    40| 4 |
    41| 555 |
    42+------+
    434 rows in set (0.00 sec)
    44. 此处在B客户端上已经commit.
    45
    46. 然后查看A客户端:
    47
    48. root@(none) 11:22>SELECT * FROM test.test1;
    49+------+
    50| a |
    51+------+
    52| 2 |
    53| 3 |
    54| 4 |
    55+------+
    563 rows in set (0.00 sec)
    57
    58. root@(none) 11:22>commit;
    59. Query OK, 0 rows affected (0.00 sec)
    60
    61
    62. root@(none) 11:22>SELECT * FROM test.test1;
    63+------+
    64| a |
    65+------+
    66| 2 |
    67| 3 |
    68| 4 |
    69| 555 |
    70+------+
    714 rows in set (0.00 sec)
    72
    73. 在A客户端上提交后可以看到新数据。
    74. 也就是说在可重复读隔离级别只能读取已经提交的数据,并且在一个事务内,读取的数据就是事务开始时的数据。 

     Serializable(可串行化)

    这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。 

    该类型在A客户端操作test.test1表时会锁定该数据,如果B客户端想要操作test.test1就需要等待A客户端释放。 

  • 相关阅读:
    JavaScript自定义事件
    用Java构建一个简单的WebSocket聊天室
    PHP实现支付宝小程序用户授权的工具类
    jq ajax超时设置
    gulp使用笔记
    vue学习—组件的定义注册
    echarts设置线条粗细
    求js数组的最大值和最小值
    js删除数组中的 "NaN"
    jq方法(end)
  • 原文地址:https://www.cnblogs.com/vanl/p/5542076.html
Copyright © 2011-2022 走看看