Skip to content

Instantly share code, notes, and snippets.

@ideawu
Last active May 21, 2021 04:12
Show Gist options
  • Select an option

  • Save ideawu/b3810a2fe34cdba9128a775a31d122e5 to your computer and use it in GitHub Desktop.

Select an option

Save ideawu/b3810a2fe34cdba9128a775a31d122e5 to your computer and use it in GitHub Desktop.
数据库最常用的特性

根据我的从业经验, 用户对一个(分布式)数据库系统, 最看重这些特性:

  1. 多副本, 数据安全性
    • 能提供强一致性更好, 但基于性能考虑, 最终一致性(异步复制)有广泛的用户需求
  2. 节点容灾, 部分服务器故障, 不影响整体系统的运行
    • 方案选择, 倾向于增加无状态 proxy 层
  3. "伪装"成一个单机数据库实例, 底层细节对用户隐藏
    • 平滑扩容, 加机器不影响业务, 不需要业务在代码和架构上做改变
  4. 数据全球同步, 最终一致
    • 一个大区的日志序列是顺序 apply, 但多个大区的日志序列是乱序 apply
  5. TTL, 能设置数据的过期时间, 定期清理数据释放空间, 主要是运维需求, 节省用户的运维工作量
    • 不提供一致性, 及时性, 只有运维性
    • TTL是一种外部运维特性, 所以在存储系统之外向存储系统发送删除指令, 是运维操作, 不是内在特性
  6. 分页操作, 例如 MySQL 的 limit, Redis 的 zrange, 虽然技术上性能局限性比较突出, 但好用, 用户喜欢用
  7. 批量操作(性能), 例如 mget, mset, mdel, 主要是方便用户使用, 性能也有优势
    • Pipeline 也算批量操作
    • 批量操作如果不承诺原子性, 可以在客户端实现
  8. 可选持久化策略
    • auto: 只需要 write(), 不要求 fsync()
    • strong: 共识必须在磁盘 fsync() 之后
  9. 可选写一致性级别
    • 默认是单大区内(raft组)强一致性
    • 默认大区间是最终一致性(跨地域同步)
    • 提供 sync_write() 让用户等待同步进度, 在写数据之后调用
  10. 可选读一致性级别
    • 默认是单大区内(raft组)强一致性
    • 默认大区间是最终一致性(跨地域同步)
    • 用户可选 stale read: 读单机
    • 提供 sync_read() 记用户等待同步进度, 在读之前调用
  11. 提供事务能力(锁+快照), 多个写操作之间有关联和依赖
    • 悲观事务(读写, Transaction), 交互式 2PC, 有 commit point
      • begin() -> commit()
    • 原子写入/乐观事务(只写, AtomicWrite), 非交互式 2PC, 写操作之间无因果依赖关系
      • atomic() -> commit()
      • 若有参与者 prepare 失败(包括 CAS 冲突)则全部回滚
      • 相比悲观事务, 可以并发 prepare
    • 批量写入(只写, MultiWrite), 1PC, 忽略 CAS 冲突, 最终全部会被执行, LWW
      • multi() -> exec()
      • 真正执行时, 如果对象当操作的时间戳更新, 则直接丢弃操作(认为操作已经完成, 然后被覆盖)
      • hdel 操作的 CAS 比对的是元素的 mtime
      • hclear 操作的 CAS 比对的是容器的 ctime, 异步地遍历每一个元素检查 mtime 决定是否删除
    • 提供 lock 指令
    • 以事务启动时的时间戳作为所有参与者的 mtime
    • 读操作忽略未决资源(最终原子性, read committed, 不是 monotonic atomic view)
  12. 提供 binlog 和 redo log 订阅接口
  13. CAS 操作, CAS+原子性, 可以取代锁, 提供事务一致性保证
  14. MVCC 底层数据保留多个版本, 但带来的问题非常多, 例如容器计数也要保留多个版本

相关文章:

@ideawu
Copy link
Author

ideawu commented Apr 21, 2021

节点结构图

a

外部请求产生 binlog, binlog 经过共识后, apply 到 storage, apply 的同时, 产生 redolog

progress 记录 last_index, last_seq, progress 是独立的模块

storage 记录 last_seq

Recover 流程

节点重启时, 执行 recover 流程:

  1. 读取 binlog.last_index, progress.last_index + progress.last_seq, storage.last_seq
  2. 取 progress.last_seq 和 storage.last_seq 的最小者作为 last_seq, 从 progress 中查找对应的 last_index
  3. 根据 last_seq 回滚 progress 或者 storage
  4. 从 last_index 位置, 继续 apply binlog

关于 Raft Apply Exactly Once 的讨论

为了保证 binlog apply 是 exactly once 的, 有两种解决方法:

  1. 提供操作原子性保证, 在 apply 的同时, 保存 apply 进度
  2. 单独保存 apply 进度, 重启时回滚

如果 apply 操作和保证进度两个操作有先后顺序, 那么先执行者需要提供回滚接口, 如果是并发的(不区分先后), 那么两者都要提供回滚接口.

代码上的先后, 不代表持久化的先后, 因为模块内部可能有缓冲, 会异步进行持久化.

@ideawu
Copy link
Author

ideawu commented Apr 28, 2021

Overview
    Cluster
    Machine
    Zone
    Node
    Proxy
Cluster
    Shard
    Machine
Machine
    CPU
    Memory
    Disk
    Cluster
    Node
    Proxy
Zone
    Machine
    Cluster

@ideawu
Copy link
Author

ideawu commented May 17, 2021

全球分布式数据库通过同步原语可以在各个区域提供强一致性读, 但是, 强一致性写操作(也即锁)则必须回源.

@ideawu
Copy link
Author

ideawu commented May 20, 2021

快照, 回滚.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment