数据库事务级别与并发场景详解

版权声明:欢迎转载 https://blog.csdn.net/antony1776/article/details/82347107

纸上得来终觉浅,绝知此事要躬行!

一 思考

  1. 事务是解决什么问题的?
  2. 为了解决这些问题,事务使用了哪些策略或手段?
  3. 脏读,幻读,不可重复读,概念理解

二 基础知识

1. 设置事务等级

  • read uncommitted:小名等不及,总能看到最新数据,无论改动是否提交
  • read commited:小名老实人,目不斜视,只看提交后的更新
  • repeatable read:小名一根筋,它查看过的数据,每次查询结果不变,不管其它事务是否提交修改
  • serializable:小名等等等,别人改了,它就等改动提交后再看;它看了,别人必须等它提交了再改
select @@tx_isolation;
set session transaction isolation level read uncommitted;
set session transaction isolation level read committed;
set session transaction isolation level repeatable read;
set session transaction isolation level serializable;

2. 数据操作

create database test;
use test;

create table person (
    name varchar(30),
    age int
);

insert into person values('xiaoerhei', 18);
update person set age=age+1 where name='xiaoerhei';
select * from person;

3. 查看数据库状态

show processlist;
show open tables from test;

三 并发场景梳理

事务级别
- ru : read uncommitted
- rc : read committed
- rr : repeatable read
- sz : serializable

事务组合: 在 2 个并发事务的情况下,共有 16 种不同并发事务的组合

    [
        (ru, ru), (ru, rc), (ru, rr), (ru, sz),
        (rc, ru), (rc, rc), (rc, rr), (rc, sz),
        (rr, ru), (rr, rc), (rr, rr), (rr, sz),
        (sz, ru), (sz, rc), (sz, rr), (sz, sz)
    ]

一般情况下,业务接口的事务级别都是一样的,比如 spring 中进行事务管理时,相同的 service 接口,一般配置为相同的事务级别。

因此,[ (ru, ru), (rc, rc), (rr, rr), (sz, sz)] 这四种事务组合更常见。

事务流程说明:
- 事务名称: A,B
- $ : start transaction
- U : update/insert/delete
- Q : select
- R :rollback
- # : commit

设计时,事务的内容要尽量小而美,而不是大而全。事务越大,并发时流程的可能性越多,问题越复杂。下面仅列举一些典型的例子。

示例:

    $(A, B) -> U(A) -> Q(B) -> #(A, B)
    $(A, B) -> Q(A) -> U(B) -> #(A, B)
    $(A, B) -> U(A) -> U(B) -> #(A, B)


    $(A, B) -> Q(B) -> U(A) -> Q(B) -> #(A, B)
    $(A, B) -> Q(B) -> U(A) -> #(A) -> Q(B) -> #(B)
    $(A, B) -> Q(B) -> U(A) -> #(B) -> R(A) -> #(A)


    $(A, B) -> U(A) -> U(B) -> R(A) -> #(B)

    $(A) -> U(A) ->$(B) -> Q(B) -> #(A, B)
    $(A) -> Q(A) ->$(B) -> U(B) -> #(A, B)

备注: $(A, B) -> U(A) -> Q(B) -> #(A, B) 的意思是,事务同时开启,事务 A 更新某数据 D,然后事务 B 查询 D 的信息,然后事务提交。

结果说明:
- SD : sql done, 执行 sql 完成;
- SB : sql block, 执行 sql 阻塞一定时间后完成;
- SE : sql error, 执行 sql 阻塞超时异常;
- DR : dirty read,脏读
- UR : unrepeatable read,不可重复读
- PR : phantom read,幻读
- OD : old data, 老数据

备注

sql 异常是否会引起事务终结并回滚,是可以配置的,SET XACT_ABORT = ON/OFF

四 示例分析

法无常法,因地制宜,要结合实际的应用场景去分析利弊,进而判断是否满足需求

scenario (ru, ru) (ru, rc) (ru, rr) (ru, sz)
$(A, B) -> U(A) -> Q(B) -> #(A, B)
$(A, B) -> Q(A) -> U(B) -> #(A, B)
$(A, B) -> U(A) -> U(B) -> #(A, B)
$(A, B) -> Q(B) -> U(A) -> Q(B) -> #(A, B)
$(A, B) -> Q(B) -> U(A) -> #(A) -> Q(B) -> #(B)
$(A, B) -> Q(B) -> U(A) -> #(B) -> R(A) -> #(A)
$(A, B) -> U(A) -> U(B) -> R(A) -> #(B)
( A ) > U ( A ) > (B) -> Q(B) -> #(A, B)
( A ) > Q ( A ) > (B) -> U(B) -> #(A, B)

1 (ru, rc)

  1. $(A, B) -> U(A) -> Q(B) -> #(A, B)

    • B 读取数据的实时性会受到影响。
    • 尽管 A 已经修改了数据,但是 B 要等到 A commit 之后才能看到更新。
  2. $(A, B) -> Q(B) -> U(A) -> #(A) -> Q(B) -> #(B)

    • A 更新并提交后,B 再次查询被更新了的数据
    • B 两次查询的结果不一致
  3. $(A, B) -> U(A) -> U(B) -> #(A, B)

    U(A)

        insert into person values('sven', 30);

    U(B)

        update person set age=age+1;
    • 这种情况下,sven 的年龄是不会被加 1 的。
    • 如果 A,B 对同一数据进行更改时,B 会阻塞。

2 (ru, rr)

  1. $(A, B) -> Q(B) -> U(A) -> #(A) -> Q(B) -> #(B)

    • 尽管 A 已经提交了更新,B 两次查询的结果仍然一致

3 (ru, sz)

  1. $(A, B) -> U(A) -> Q(B) -> #(A, B)

    • 数据被 A 更新后,B 查询该数据时会阻塞,直到 A 事务提交

(未完待续 …)

猜你喜欢

转载自blog.csdn.net/antony1776/article/details/82347107
今日推荐