设为首页 加入收藏

TOP

(三)分布式数据库tidb-隔离级别详解(一)
2019-10-09 19:58:34 】 浏览:53
Tags:分布式 数据库 tidb- 隔离 级别 详解

tidb隔离级别详解:

1.TiDB 支持的隔离级别是 Snapshot Isolation(SI),它和 Repeatable Read(RR) 隔离级别基本等价,详细情况如下:

  ● TiDB 的 SI 隔离级别可以克服幻读异常(Phantom Reads),但 ANSI/ISO SQL 标准中的 RR 不能。
  所谓幻读是指:事务 A 首先根据条件查询得到 n 条记录,然后事务 B 改变了这 n 条记录之的 m 条记录或者增添了 m 条符合事务 A 查询条件的记录,导致事务 A 再次发起请求时发现有 n+m 条符合条件记录,就产生了幻读。
  ● TiDB 的 SI 隔离级别不能克服写偏斜异常(Write Skew),需要使用 Select for update语法来克服写偏斜异常。写偏斜异常是指两个并发的事务读取了两行不同但相关的记录,接着这两个事务各自更新了自己读到的那行数据,并最终都提交了事务,如果这两行相关的记录之间存在着某种约束,那么最终结果可能是违反约束的。
  例如,值班表有两列,姓名以及值班状态,0 代表不值班,1 代表值班 
姓名 值班状态
张三 0
李四 0
王五 0
  有这样一个事务,它的逻辑是判断当前无人值班,则分配一个值班人。当该程序顺序执行时,只会分配一个值班人。但当它并行执行时,就可能出现多人同时为值班状态的错误。
  ● TiDB 在默认配置下(tidb_disable_txn_auto_retry=0)不能克服丢失更新异常(LostUpdates)。
  所谓丢失更新是指:两个事务 A,B 读取相同记录并更新同一列的值,若 A 先于 B 提交事务,当 B 事务提交后 A 再次查询时发现自己的更新丢失了。
   2. 显式事务中 DML 语句返回的 affected rows 不可信与所有使用了乐观锁机制的分布式数据库(PXC, MGC, MGR 等)一样,在显式执行的事务中(设置为非自动提交 autocommit=0,或使用 begin 语句显式声明事务开始),DML操作所返回的 affected rows 并不保证与最终提交事务时所影响的数据行数一致。
    如下案例,事务 B 在并发中丢失了它的更新: 

  这是由于在显式执行的事务中 DML 操作与提交操作分开被执行,在事务提交过程中,如果由于事务冲突,找不到 TiKV,网络不稳定等原因而发生了重试,TiDB 将获取新的时间戳

重新执行本事务中的 DML 操作,原本的 SI 隔离级别在重试后会产生类似 RC 隔离级别的不可重复读与幻读异常现象。 由于重试机制在内部完成,如果最终本事务提交成功,用户一般是无法感知到是否发 生了重试的,因此不能通过 affected rows 来作为程序执行逻辑的判断条件。 而隐式事务中(以单条 SQL 为单位进行提交),语句的返回是提交之后的结果,因此 隐式事务中的 affected rows 是可信的。
  3. 避开丢失更新影响的应用开发方法 TiDB 使用了乐观锁机制,乐观锁仅在提交时才会进行冲突检测和数据上锁。在使用了 非 select for update 的 SQL 语句时,TiDB 会对提交时遇到冲突而发生退避的事务进行自动重 试(由 tidb_disable_txn_auto_retry 变量控制,默认行为是自动重试),当事务达到退避次数限 制(默认 10 次)依然不能成功提交时,事务会被回滚。 发生了退避的事务会重新获取时间戳,重新执行事务中的增删改语句,这样设计是为 了规避上一次造成提交失败的原因(包括但不限于锁冲突),但也因此导致了并发事务可能出 现丢失更新异常。 可以通过妥善的应用实现方式来避免丢失更新造成的影响。
  场景一,在不做余额检查的类似转账交易场景中,一般通过账号筛选出需要修改余额 的记录,然后直接在数据库中进行数学运算的 SQL 来实现对账户余额的更新,诸如此类写法 的事务即使在并发执行时遇到了丢失更新异常,也可以正确的完成转账操作,并不会被用户感 知到:
update account set realtimeremain = realtimeremain-100 where cuno='A';
update account set realtimeremain = realtimeremain+100 where cuno='B';
commit;
  同上转账场景,如果实现方式是应用获取了当前转入转出账户的余额后,在应用中计 算出转账后两账户的余额,使用常值写入余额字段,这样的实现方式在事务并发执行时将会导 致错误:
  select realtimeremain from account where cuno='A';
  --返回 1000
  select realtimeremain from account where cuno='B';
  --返回 1000
  --应用中计算出两账户转账后的余额分别为 900 和 1100
  update account set realtimeremain = 900 where cuno='A'; update account set realtimeremain = 1100 where cuno='B';
  commit;
  4. 计数器,秒杀场景的处理方法 如上一段所讲,TiDB 采用了乐观锁机制,在事务的并发处理中,TiDB 会自动重试提 交时遇到冲突而发生退避的事务;而在使用了 select for update 或关闭 tidb_disable_txn_auto_retry 变量时,这种退避机制会失效,后提交的事务会被回滚。 select for update 被使用于计数器,秒杀,公用账户、理财产品、国债的余额扣减等场 景,技术特点是并发的对同一行数据进行修改。传统的单机 DBMS 多使用悲观锁来实现 select for update,在事务开始的时候即进行锁检查,如果事务所需要的锁和数据上当前的锁不 兼容,就会发生锁等待,等当前的锁释放后本事务才能执行。TiDB 在执行 select for update 时 相当于悲观锁系统中将锁等待时间设置为 0,遇到锁冲突的事务会执行失败。 综上,TiDB 不适合用于处理高并发的对同一行数据进行修改,事务使用了 select for update 语句,可以保证数据的一致性,但并发执行的事务中,只有最先提交的事务会成功,其 余的并发请求都会被回滚。 处理计数器场景的最佳实践是将计数器功能转移到缓存(redis,codis 等)中实现,如 购买国债产品场景中,将国债余额读取到缓存中,在缓存中根据余额与购买额度对请求队列进 行控制,向合格的请求发放访问数据库的令牌,向购买额度超过余额的请求返回余额不足的错 误,拿到令牌的请求可以并发去修改数据库中的产品余额。 在应用了悲观锁的 DBMS 中,并发的 select for update 事务实际上是被排成队列以串行 的方式执行的,因此性能不高,而使用缓存来处理计数器场景也有着较大的性能优势。
  5. “嵌套事务” 遵照 ACID 理论,并发事务间应彼此相互隔离,避免互相干扰。即事务不能“嵌套”。 在 Read Committed 隔离级别下,同一事务中如果存在多次读取,每次读到的都是当 时已经提交的数据,在多个事务并发执行时,一个事务内多次读取的结果可能千差万别,这种 现象被称为“不可重复读(Non-repeatable Reads)”。应用于传统金融行业的 RDBMS 产品中,默认隔离级别为 RC 的
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇数据库系统(二)--关系型数据库.. 下一篇Hbase入门(四)——表结构设计-Row..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目