数据系统与分布式(三) 分布式数据系统(事务,事务隔离级别)

数据 和 分布式

数据系统基础

第一章. 可靠 可拓展 可维护的应用系统

  • 可靠性

    • 出现意外情况, 硬软件故障,人为失误, 系统应该正常运转, 虽然性能降低, 但是功能正确
  • 可拓展性

    • 随着系统规模的增长 , 系统应该合理的匹配增长

    • 比如Twitter的例子P19

      • 描述性能我们关心中位数, 百分位数
      • 比如P50代表至少一半用户查询等待时间是在这个时间之内的
      • 同样的还有99.99%这种
      • 实际上为了提高性能, 我们常常在垂直拓展和水平拓展之间做取舍
  • 可维护性

    • 许多时间的推移, 新加入的运维人员应该比较容易适应
    • 可运维性: 运维更轻松
    • 简单性: 简化系统复杂度
    • 可演化性: 易于改变
  • 常见术语

    • 现在的应用很多都有数据系统的很多模块
    • 数据库: 用来存储数据
    • 高速缓存: 缓存那些复杂或操作代价昂贵的 结果 加快下一次访问
    • 索引: 按关键字来检索数据 并支持各种过滤
    • 流式处理: 持续发送数据到另外一个进程 , 采用异步方式
    • 批处理: 定期处理大量的累计数据

第二章 数据模型 和 查询语言

  • 计算机常常的分层结构, 在数据系统中也不例外. 在某个具体的层次上 , 我们关注的问题是 如何用下一层来表示当前层

    • 用数据结构对现实建模 -> 用json或者xml或者关系数据库的表进行存储-> 用内存, 磁盘或者网络的字节格式来表示 ->硬件用电流, 磁场来表示数据
  • 关系模型和文档模型

    • 关系模型最出名的就是SQL
    • 子主题 2

第三章 数据存储 和 检索

  • 从基本的层面: DB = 插入时存储数据, 查询时返回数据

  • 哈希索引

  • B树索引

  • 事务分析和处理

    • 事务不一定有ACID属性

    • OLTP

      • 在线事务处理
      • OnLine Transition Process
      • 使用索引的某些键查找少量积累, 根据用户的输入 插入或更新记录 并且常常是交互式的
      • 比如博客的评论 ,游戏的动作
      • 基本访问模式和处理业务交易类似
    • OLAP

      • 在线分析处理
      • OnLine Analytic Process
      • 分析查询需要扫描大量的记录 ,并汇总一个信息
      • 比如 一个月店铺总收入 促销期间比平时多麦了多少香蕉
    • 数据规模 OLTP GB~TB OLAP TB到PB

    • 命名 常常OLAP单独在数据仓库上 因为不愿意让业务分析人员在OLTP上执行代价很高的操作 数据仓库有OLTP的只读副本

    • 采用数据仓库的目的是可以针对不同的分析访问模式进行优化

  • 子主题 5

    • 子主题 1

第四章 数据编码和 演化

  • 在内存里 ,数据保存在对象, 结构体, 数组, 哈希表 ,树. 把数据写入文件或者通过网络发送时, 由于指针对其他进程没有意义,所以字节序列和内存的大不一样

  • Json

  • XML

  • 二进制

    • Thrift

      • Facebook开发的 07 - 08开源

        • 有两种编码方式

          • Binary Protocol

            • 对一个3字段的59byte
          • CompactProtocol

            • 只要34字节
    • Protocol Buffers

      • Google开发的 07 - 08开源

        • 类似CompactProtocol

          • 只要33Byte
    • 特征是 编码时不存储具体的字段名, 比如name, 而是用一个Tag来标识字段, 带来了更好的模式演化, 为以后拓展字段等

      • 旧代码读新代码写入的数据

        • 可以通过简单的忽略解决
        • 实现了向前兼容性
      • 新代码读入旧代码的数据

        • 字段必须为optional或者具有默认值
        • 实现了向后兼容性
    • 而且required 和 optional是在运行时检查, 在编码时不编码进去

    • Avro

      • 与前两个比, 优点在于没有标签号
  • 数据流模式

    • 基于数据库的流模式

    • 基于服务的数据流

      • REST

      • RPC 远程过程调用 Remote Procedure Call

        • RPC存在一些问题

          • 本地函数可预测(只和参数有关), 网络请求是不可预测的, 网络问题很常见 ,请求或者响应可能因为网络丢失, 那是不是要有重试机制? 幂等性怎么解决
          • 本地要么return一个结果 要么抛出异常 要么死循环. 远程可能因为超时没有结果. 根本不知道发生了什么
          • 如果重试失败的请求, 可能请求已经完成, 只是响应丢失, 可能会导致执行多次, 除非建立( 重复数据)消除(幂等性)机制
          • 由于网络延迟, 函数执行时间很难说
          • 本地可以把引用或者指针传给本地内存的对象 RPC一定要编码成网络字节序列
          • 子主题 6
        • 子主题 2

      • 面向微服务的一个关键的设计目标是, 通过使服务可独立部署和演化, 让应用程序易于更改和维护

    • 基于消息传递的数据流

      • 也就是消息队列

        • Rabbit MQ,Active MQ, Apache Kafka
      • 优点:

        • 1.如果接受方过载, 或者不可用 .它可以作为缓冲区, 提高系统可靠性
        • 2.自动把消息重新发到崩溃的进程 , 防止消息丢失
          1. 解耦
        • 4.避免了发送方需要知道接收方的IP地址, 端口号( 在频繁变更的虚拟机中)很有用
        • 5.支持一条消息多个接收方
        • 6.逻辑上的分离, 发送方只管发送消息,不关心谁使用它们
        • 7.异步的
    • 分布式Actor框架

分布式数据系统

基础知识

  • 为什么要引入分布式

    • 拓展性

      • 负载过大,超出了单台机器的处理上限
    • 容错与高可用性

      • 单台机器出现故障, 其他机器可以正常工作
      • 组件失效, 冗余组件可以继续接管
    • 延迟考虑

      • 服务遍布全球各地, 希望就近服务
  • 系统拓展

    • 每个机器称为结点

    • 垂直拓展

      • 加强单台机器
    • 水平拓展

      • 加机器数量, 加小机器
  • 复制与分区

    • 当把数据分布在多节点的时候有两种常见的方式

    • 复制

      • 在多个节点上保存冗余的副本
    • 分区

      • 将一个大的数据库拆分成多个较小的子集

数据复制

  • 目的

    • 1.数据在地理位置上更接近用户,降低延迟
    • 2.高可用, 系统出现故障, 仍然可以继续工作
    • 3.拓展多台机器, 提高读吞吐量
  • 主节点与从节点

    • 基本原理

      • 1.选一个作为主节点, client的所有写请求都发给主节点
      • 2.其他节点都是从节点, 主节点把更改写入本地后, 把更改通过复制的日志或者更改流发送给所有从副本, 从副本应用这些更改
      • 3.client可以向主节点发read or write, 对从节点只能read
    • 同步复制和异步复制

      • 同步复制的优点

        • 一旦向用户确认, 从节点可以保证数据的最新版本
        • 主节点发生故障时, 从节点有最新的备份, 可以迅速恢复
      • 同步复制的缺点

        • 一旦有某个响应特别慢, 就要一直阻塞到同步副本确认完成
      • 异步复制的优点

        • 性能特别好, 在处理写请求时, 不管从节点数据多么滞后, 主节点总是可以继续响应写请求, 系统的吞吐性能更好
      • 异步复制的缺点

        • 主节点发生失败,还未同步的写请求会丢失
        • 无法保证数据的持久化
      • trade-off也就是半同步

        • 一个节点是同步模式, 其他节点是异步模式.
        • 万一同步的从节点性能下降或者不可用了, 把另外一个节点提升成同步模式
        • 这样做的好处是保证至少有两个节点拥有最新的数据副本
    • 配置新的从节点

      • 如何在不停机 , 数据服务不中断的前提下完成从节点的设置

        • 1.在某个时间点对主节点的数据副本产生一个一致性快照
        • 2.将此快照拷贝到新的从节点
        • 3.从节点连接到主节点, 并请求 快照之后的数据更改日志
        • 4.获得日志后, 应用这些变更. 也就是 "追赶"
    • 处理节点失效

      • 从节点失效

        • 追赶式恢复
      • 主节点失效

        • 切换的具体表现

          • 选择某个从节点提升为主节点, 客户端也要把write发送给新的主节点, 从节点也要接受来自新的主节点的变更数据
        • 手动切换

          • 管理员手动切换
        • 自动切换

          • 1.确认主节点失效

            • 基于超时的机制, 如果30s都没有回复, 则认为失效
          • 2.选举新的主节点

            • 选举的方式, 最好数据差异最小
          • 3.重新配置系统让 新主节点生效

            • 很多小问题

              • 脑裂
              • 原主节点仍然认为自己是主节点
    • 复制日志的实现

      • 基于语句的复制

        • 把诸如insert和update这类语句发送给从节点

        • 问题就是很多非确定性的语句

          • 比如now()获取时间

          • 多个并发执行的事务

            • 等等
          • MySQL会自动切换到基于行d

      • 基于预写日志(WAL)传输

        • PostgreSQL和Oracle支持
        • 缺点是此方案过于底层, 与存储引擎紧密耦合, 和软件版本有关
      • 基于行的逻辑日志复制

        • 复制和存储引擎采用不一样的日志格式, 解耦合
        • MYSQL的binlog
      • 基于触发器的复制

        • 高度可定制, 复杂度高
  • 复制滞后问题

    • 主从复制所有写都要经过主节点, 可以通过水平拓展来增强 系统的读能力

    • "最终一致性"问题

      • 一个应用从一个异步的从节点读取数据, 该副本落后于主节点
      • 如果同时对主节点和从节点发送相同的查询, 可能会得到不一样的效果
      • 但是这种不一致只是暂时的, 一段时间后, 从节点会赶上主节点
      • 这种效应称为最终一致性
    • "写后读一致性"问题

      • 读自己的写

        • 用户在写入不久后查看数据

          • 但是还没同步,看起来就像数据丢失了一样
          • 用户会不高兴不高兴
        • 我们需要"写后读一致性"

          • 方案一

            • 如果用户读可能被修改的内容, 从主节点读
            • 否则 ,在从节点读
            • 比如社交网站, 读自己的朋友圈, 从主节点读, 读别人的朋友圈, 在从节点读
          • 方案二

            • 跟踪最近更新的时间,

              • 如果在1min之内, 从master读
            • 同时监控从节点的复制滞后程度

              • 避免从滞后超过1min的节点读取
          • 方案三

            • 通过client端发送读请求时的时间戳, 确保提供读服务时 时间上的合理
          • Tips

            • 如果副本分布在多数据中心, 必须先把请求路由到主节点在的数据中心
    • "单调读"

      • 由于两次路由的从节点不同的原因, 用户第一次看到了数据, 第二次却看不到了

      • 我们需要"单调读"一致性

        • 方案一

          • 根据用户ID哈希到固定的一台机器
      • 是比"强一致性"弱, , 比"最终一致性"强的

      • 保证绝对不会看到回滚

    • "前缀一致读"

      • 对于一系列按照某个顺序发生的写请求 ,读这些内容时也会按照当时写入的顺序

      • 方案

        • Happened-before
    • 复制滞后的解决方案

      • 如果采用最终一致性系统, 用户的体验能接受的话,

        • 那就采用"最终一致性"吧
      • 如果用户体验不好

        • 提供更强的一致性

          • 比如写后读
  • 多主节点复制

    • 单主节点的明显缺点

      • 如果主节点网络中断了,就会影响所有的 写入操作
    • 适用场景

      • 在单数据中心引入 多节点 只是徒增复杂度

      • 适用于多数据中心, 离线客户端, 协作编辑 这三个场景

      • 多数据中心场景

        • 在每个数据中心内, 采用常规的主从复制方案

        • 在数据中心之间, 有每个的主节点来负责 数据交换和更新

        • 与单主节点的对比

          • 性能方面

            • 肯定是多主节点性能更好,
            • 因为就近访问
          • 容忍数据中心失效

            • 也是多主节点更好
            • 每个数据中心可以独立于其他数据中心继续运行
          • 容忍网络问题

            • 也是多主更好
        • 缺点

          • 冲突问题
      • 协作编辑

        • 石墨文档, 腾讯文档
        • 如果解决冲突
      • 离线客户端操作

        • 比如手机上的日历工作, 每部手机都是一个主节点
        • 这种时候采用 多主节点场景比较合适
    • 处理写冲突

      • 场景

        • A修改标题为20201005是美好的一天, B修改标题为 20201006是美好的一天
      • 同步与异步冲突检测

      • 避免冲突

        • 最理想的策略就是避免冲突
        • 不同用户对应到不同的主数据中心
        • 但是还是有可能冲突,
      • 收敛于一致性状态

        • 通过分配唯一的ID

          • 比如基于时间戳
          • 最后写入者获胜
        • 拼接这些值

          • 20201005是美好的一天20201006是美好的一天
        • 靠应用层的逻辑, 事后解决, 比如告诉用户

      • 自定义冲突解决逻辑

        • 最合适的方式还是得应用层来
        • 在写入时执行
        • 在读取时执行
      • 什么是冲突

        • 刚才的那种对同一个字段写

        • 订票时把唯一的票给了两个人

        • 自动冲突解决的可能方案

          • 1.无冲突的数据类型(CRDT)

            • conflict-free Replicated DataType
            • 可以由多个用户编辑的数据结构
          • 2.可合并的持久数据结构

            • 类似git
            • 二向合并 三向合并
          • 3.操作转换

        • 没现成的答案

    • 拓扑结构

      • 指的是多个主节点之间传播路径

      • 环形拓扑

      • 星形拓扑

        • 拓展到树形结构
      • all to all拓扑

        • 容错性更好
        • 问题: 可能由于网络 原因, 出现类似"前缀一致读"的问题
        • 使用版本向量的技术来
  • 无主节点复制

    • 思路清奇, 放弃主节点

      • 亚马孙Dynamo系统 不开放, DynamoDB不是Dynamo
    • 节点失效时写入数据库

      • 读修复与反熵

        • 读修复

          • 并行读多个副本时, 可以检测到过期的返回值
        • 反熵

          • 有后台进程不断查找副本之间的数据差异, 有明显的同步滞后
      • 读写quorum

        • quorum是法定人数的意思

        • 3个副本的例子里, 成功的写操作至少2个, 那么失败的至多一个, 我们读的时候至少读两个, 就可以保证至少有一个是最新的副本

        • W+R>n

        • 常见的设计

          • n为奇数
          • w = r = (n+1)/2
        • 问题

          • 即使满足W+R>n , 我们还是不能保证读取最新值
        • 所以我们最好把w和r看成灵活可调的读取新值的概率

    • Quorum一致性的局限性

      • 可以把w和r<=n

        • 这样虽然可能读的是旧值, 但是提高了性能
      • 监控旧值

        • 我们监控复制的运行状态

        • 如果差距过大, 可能就是网络问题或者节点超负荷

        • 原理大概是

          • 由于主节点和从节点上写入遵循相同的顺序, 每个节点维护了复制日志执行的当前偏移量. 通过比较偏移量的差值, 来衡量从节点落后于主节点的程度
      • 宽松的quorum与回传

        • 配置适当的quorum是有好处的

          • 1.可以容忍一些节点慢, 因为请求不用等所有节点的响应, 只需要w或者r个
        • sloppy quorum

          • 写入和读取仍然需要w和r个成功的响应, 但包含了不在先前指定的n个节点

          • 原先指定的n个节点暂时不可用

          • 把写请求写入了一些暂时可访问的节点中, 一旦网络问题解决, 这些临时节点会把这些写入全部发送到原始主节点上,

            • 这就是数据回传,(或者说暗示移交)
      • 多数据中心操作

        • 无主节点复制 aim at 更好容忍并发写入冲突, 网络中断 和 延迟尖峰
    • 检测并发写

      • Dynamo风格的数据库在并发写入时缺乏顺序保证

      • 最后写入者获胜( 丢弃并发写入)

        • 每个写请求附加一个时间戳

        • Last Write Wins(LWW)

        • 可以实现最终收敛的目标, 但是牺牲了数据的持久性

        • 如果覆盖, 丢失数据可以接受,就用

        • 确保LWW安全无副作用的唯一办法

          • 只写入一次然后写入值视为不可变
          • 比如UUID作为主键
      • Happens-before关系和并发

        • 并发性, 时间, 相对性
        • 确定前后关系
        • 合并同时写入的值
        • 版本矢量

数据分区

  • 面对海量数据集或者非常高的查询压力, 复制技术还不够, 需要引入分区

    • 分区的定义

      • 每条数据, 或者说每条记录,只属于某个特定分区
      • 每个分区可以看成一个完整的小型数据库
  • 数据分区与数据复制

    • 分区通常和复制结合使用, 每个分区在多个节点上保存有副本.

      • 意思是某条记录属于特定的分区, 而这个分区里有许多节点保存着这记录的拷贝
      • 每个分区有自己的主副本
  • 键-值数据的分区

    • 基于关键字区间分区

      • 分区边界要适配数据本身的分布特征
    • 基于关键字哈希值分区

      • MD5, Fowler-Noll-Vo
      • 缺点, 丧失了区间查询特性
    • 组合索引

      • user_id, update_timestamp

        • 可以有效检索某用户在一段时间内的所有更新
    • 负载倾斜与热点

      • 所有的读/写都是针对同一个关键字

        • 比如明星的微博
        • 方法,如果某key是热点, 在这个key的前或后加一个随机数,比如两位的十进制,, 这样可以负载到100台机上
  • 分区与二级索引

    • 二级索引带来的主要挑战是它们不能规整的映射到分区中

    • 基于文档分区的二级索引

      • 每个分区完全独立, 如果我们想查询"红色的汽车"
      • 需要把查询发送到所有的分区, 然后合并所有返回的结果
      • 别名:"分散/聚集"
    • 基于词条的二级索引分区

      • 我们要对所有的数据构建全局索引, 但是不能把所有的全局索引存在一个节点上, 所以全局索引页必须分区

      • 全局索引的缺点:

        • 写入速度较慢而且比较复杂
        • 所以往往是异步的 刚写入之后读可能 不太行
      • 优点:

        • 读取更高效
  • 分区再平衡

    • 随着时间的变化, 数据库可能会变化, 我们需要再平衡

    • 再平衡需要满足

      • 1.平衡之后, 负载, 数据存储, 读写请求等应该均匀分布
      • 2.再平衡过程中, 数据库应该可以正常提供读写服务
      • 3.避免不必要的负载迁移
    • 动态再平衡的策略

      • 为什么不用取模

        • hash (A) = 123456

          • 10台机器时

            • 123456%10 = 6
          • 11台机器时

            • 123456%10 = 3
          • 12台机器时

            • 123456%10 = 0
        • 因为如果节点数N发送了变化, 会导致许多关键字需要迁移

      • 固定数量的分区

        • 比较好

          • 创建远超实际节点数的分区数, 然后为每个节点分配多个分区
        • 比如10个机器, 一开始就创建了1000个分区

          • 每个机子负责100个分区
        • 只需要做好映射关系

      • 动态分区

        • 分区数量大到一定程度, 比如10GB, 就分裂
        • 分区小到一定程度, 就合并
        • 类似B树
        • 一个节点上可能有多个分区
        • 适用于关键字区间分区, 也适用于基于哈希的分区
      • 按节点比例分区

  • 请求路由

    • 我们已经把数据集分布到多个节点上, 但是客户端有一个 悬而未决的问题 应该连接到哪个IP地址 和哪个端口号

      • 也就是服务发现的问题
    • 主要有三种策略

      • 1.客户端随机选, 然后节点看看是不是自己, 不是就转发
        1. 引入一个路由层
      • 3.客户端就知道
    • ZooKeeper是一个独立的协调服务, 它跟踪集群范围内的元数据

      • 每个节点向ZooKeeper注册自己, 路由层或者客户端订阅ZooKeeper
    • 当然还可以通过gossip协议来同步集群状态变化, 虽然增加了节点的复杂性, 但是避免了对ZooKeeper的依赖

事务

  • ACID的含义

    • 隔离性各不相同

    • BASE

      • Basically Available
      • Soft state
      • Eventual consistency
    • ACID原子性

      • 在出错时中止事务, 并将部分完成的写入全部丢弃
      • 或者说, 可终止性
    • ACID一致性

      • 不同场景含义不同

        • 在副本一致性和异步复制模型时, 这里指的是最终一致性问题
        • 一致性哈希指的是系统动态分区再平衡的办法
      • ACID一致性指的是 任何数据更改必须满足这些状态约束

      • 状态机 从一致状态 经过一些操作 跳转到另外一个状态

    • ACID隔离性

      • 并发执行的多个事务 相互隔离, 不能互相交叉

      • 经典定义是 可串行化

        • means 可以假装它是 数据库运行的唯一 事务
    • ACID持久性

      • 对于单点数据库

        • 持久性意味着 数据已经被写入 非易失性 存储设备
      • 对于支持远程复制的数据库,

        • 持久性意味着 数据已经成功复制到多个节点
  • 单对象与多对象事务操作

    • 单对象写入

      • compare-and-set

      • 单对象操作

        • 称为轻量级事务
    • 多对象事务的必要性

      • = =
    • 处理错误与中止

  • 弱隔离级别

    • 从理论上讲 ,隔离是假装没有并发

      • 可串行化的隔离会严重影响性能

        • 所有常常用弱级别的隔离
    • 读-提交

      • 提供两个保证

        • 1.读数据, 只能看见已经成功提交的数据(防止"脏读")

          • 如果事务A已经完成部分数据写入, 但是事务没有提交(或者中止了), 那事务B能不能看到 未提交的数据呢?

          • 如果能, 就是脏读

          • 适用场景

            • 1.如果事务更新多个对象, 比如更新电子邮件 和 更新 未读邮件计数器
        • 2.写数据库,只会覆盖已经成功提交的数据(防止"脏写")

          • 推迟第二个写请求, 直到前面的事务完成提交
      • 读-提交的实现

        • 数据库通常用行级锁来防止脏写

        • 但是防止脏读的话, 也加锁就太慢了

          • 一般来说会同时维护旧值和新值

            • 事务提交之前,都读旧值, 提交之后, 读新值
    • 快照级别隔离和可重复读

      • 每个事务都从数据库的一致性快照中读取, 事务一开始看到的是最近提交的数据 ,即使数据

      • 快照级别隔离的实现

        • 考虑到多个正在执行的事务可能会在不同的时间点 查看数据库状态, 所以数据库保留了对象多个 不同的提交版本(MVCC) Multi-Version Concurrency Control
        • 当事务开始时, 赋予一个唯一的, 单调递增的 事务ID(txid)
        • 会引入一个快照 一个created_by + 事务ID字段 一个deleted_by + 事务ID 字段
      • 一致性快照的实现

        • 当且仅当这两个条件成立, 数据对象对事务可见

          • 1.事务开始的时刻, 创建该对象的事务已经完成了提交
          • 2.对象没有标记为删除; 即使标记了, 删除的事务还没提交
      • 索引与快照级别隔离

      • 可重复读 与 命名混淆

        • 快照级别隔离 在Oracle里叫可串行化
        • 在PostgreSQL和MySQL里叫可重复读
    • 防止更新丢失

      • 写事务并发 最著名的问题就是 更新丢失问题

      • 解决方案

        • 1.原子写操作

          • 如果原子操作可行, 那么就是最佳方案

          • 实现方法

            • 加独占锁
            • 强制所有原子操作都在单线程上执行
        • 2.显式加锁

        • 3.自动检测更新丢失

          • 先让他们并发执行, 如果事务检测器 检测到了更新丢失风险, 那么中止当前事务, 回退到安全的模式
          • MySQL的可重复读 不支持 检测更新丢失
        • 4.原子比较和设置

        • 5.冲突解决与复制

          • 多副本的数据库
    • 写倾斜和幻读

      • 医院至少一个医生值班, 如果两个人同时 请假

      • 幻读

        • 在一个事务的写入中 改变了 另一个事务查询结果的现象 叫幻读
      • 为何产生写倾斜

      • 实体化冲突

        • 大多数情况下, 可串行化隔离更好
  • 串行化

    • 前面一大堆问题

      • 我们可以用可串行化隔离来解决
    • 被认为是最强的隔离级别

    • 实际串行执行

      • 背景

        • 1.内存偏移, 可以把数据集加载到内存里
        • 2.OLTP事务通常很快 而OLAP通常是只读的, 可以在一致性快照上运行, 不用在串行主循环里
      • Redis采用串行方式执行事务

        • 单线程执行优势可能会比支持并发的系统效率更高, 尤其是避免锁开销
      • 吞吐量上限是单核的吞吐量

        • 事务需要作出调整
      • 采用存储过程封装事务

        • 应用程序必须提交整个事务代码作为存储过程 打包发送 给数据库, 不需要等待网络或磁盘I/O

        • 而不是交互式

        • 优点

          • Redis 采用lua
          • VoltDB使用JAVA或者Groovy
        • 缺点

      • 分区

        • 如果可以让事务在一个分区里

          • 那么每个分区都可以有自己的事务处理线程且独立运行
          • 每个CPU核 分配一个分区
        • key-value可以试试

        • 带有许多二级索引的 需要跨区协调, 不合适

      • 场景

        • 事务简短高效
        • 适合数据可以完全加载到内存
        • 跨分区少
    • 两阶段锁定

      • 2PL(two-phrase locking)

        • 2PC是两阶段加锁, 二者区别很大
      • 多个事务可以同时读取同一对象, 但只要出现两个事务同时尝试修改同一个对象时, 以加锁的方式来确保第二个写入等待前面事务完成

        • 1.如果事务A已经读取了某个对象, 事务B想写入, 事务B必须要等A提交或终止
        • 2.如果事务A已经修改了 对象, 事务B想读 , 那么必须等到A提交或终止
      • 反高速缓存

        • 如果访问不在内存的数据, 先中止事务,然后异步地加载到内存, 此期间处理其他事务, 后面再重启事务
      • 实现两阶段加锁

        • 已经实现的数据库

          • Mysql(innoDB), SQL Server"可串行化隔离"
        • 用法

          • 1.如果要读 ,则要获取共享锁

          • 2.如果要写, 要获取排他锁

          • 3.如果要读, 然后写, 等价于获取排他锁

          • 4.事务获得锁后, 一致持有锁到事务结束.

            • 在第一阶段(事务执行之前)获得锁
            • 在第二阶段(事务结束)释放锁
      • 两阶段加锁的性能

        • 主要缺点就是 相比于弱隔离性

          • 性能下降非常多

          • 原因

            • 1,获取和释放锁本身的开销

            • 2,降低了事务的并发性

              • 按2PL的设计, 两个并发事务如果试图做任何困难导致竞争条件的事情, 其中一个必须等对方完成
            • 如果竞争严重,会非常慢

              • 而且还有死锁的问题
      • 谓词锁

        • 为了防止"幻读"问题

          • 可以防止 写倾斜
        • 如果事务A想读取匹配某些条件的对象,比如select查询, 那它必须以共享模式 获得查询条件的谓词锁, 如果事务B持有任何一个匹配对象的互斥锁, 则A必须等

        • 如果事务A想要 更改任何对象, 必须检查所有旧值和新值是否与现有的任何谓词锁匹配, 如果B持有, A要等B

      • 索引区间锁

        • next key locking

          • 因为谓词锁性能不佳
        • 简化谓词锁的方式是 将其保护的对象 扩大化

          • 索引区间锁就是扩大了
        • 但是开销低的多, 是一种很好的折中方案

        • 如果无合适的索引 可以施加区间锁, 可以锁整个表

    • 可串行化的快照隔离

      • 概览

        • 是一种乐观并发控制技术

        • 基于数据库的一致性快照, 在之前的基础上, 新增了 算法 来检测写入之间的串行化冲突 从而决定 中止 哪些事务

        • 是2008年提出的算法

          • SSI
          • Serializable Snapshot Isolation
      • 悲观和乐观的并发控制

        • 两阶段加锁是一种典型的悲观并发控制机制

          • 如果某些操作可能出错, 那么就等到足够安全为止
        • 可串行化的快照隔离 是一种 乐观并发控制

          • 如果可能潜在冲突, 会继续执行而不是中止, 寄希望于平安无事, 冲突了 会重试
      • 基于过期的条件做决定

        • 检测是否读取了过期的MVCC对象
        • 检测写是否影响了之前的读
      • 可串行化快照隔离的性能

        • 关于跟踪事务读, 写的粒度会影响性能
        • 与"两阶段加锁"比, 事务不需要等待其他事务所持有的锁. 读写不会互相阻塞. 在一致性快照上执行只读查询不需要任何 锁
        • 与"串行执行"相比, 可以突破单CPU核的限制,
        • 可串行化隔离希望事务要简短

派生数据

XMind: ZEN - Trial Version

posted @ 2020-10-08 14:48  Yan_Hao  阅读(362)  评论(0编辑  收藏  举报