TiDB深入业务实践

246 阅读5分钟

TiDB是开源分布式关系型数据库,是NewSQL的一种。业界类似产品有Google Spanner、阿里OceanBase。

架构图

  • 计算节点 -- TiDB Server

    • 对外提供兼容MySQL协议接入节点,处理客户端链接。
    • 本身无状态,不存储数据。
    • 协议SQL解析,生成执行计划,并负责部分执行。
  • 存储 -- TiKV

    • 负责存储数据,是一个分布式的提供事务的 Key-Value 存储引擎
    • TiKV 中的数据都会自动维护多副本(默认为三副本),支持高可用和自动故障转移。
  • 管理 -- PD(Placement Driver)

    • 整个 TiDB 集群的元信息管理模块,负责数据分布的调度
    • 为分布式事务分配事务 ID

存储

单机存储

单机存储使用RocksDB。RocksDB由facebook维护,是由Google的基于LSM-Tree的LevelDB扩展开发而来,提供提供如下能力:

  • 优异的随机修改(插入)和读取性能
  • key顺序排列,提供遍历key能力
  • 快照访问

分布式共识

单机存储可能因单机故障而丢失数据,可通过多副本机制避免单机故障。但如何保证多副本之间保持一致,就是分布式共识的问题。TiDB中,通过Raft协议实现。

Raft简述

Raft是分布式共识协议,以唯一主节点提供读写简化共识了算法,提供如下功能:

  1. Leader 选举
  1. 成员变更
  1. 日志复制

日志复制

  1. 主节点新增日志

  1. 发送给从节点

  1. 成功发送给多数从节点,主节点应用日志

  1. 从节点应用日志

选主

选主可以视为特殊的日志复制。

成员变更

成员变更也可以视为特殊的日志复制,不过需在新旧两个集群中分别进行一次,共两次。

TiDB中的Raft

数据分片分布与共识(Region)

数据分布方式:

  1. Hash:对key hash后选择对应的存储节,redis cluster的一致性hash。
  1. Range:某一段连续的 key 都保存在一个存储节点上。

TiDB选择Range,用 StartKey 到 EndKey 这样一个左闭右开区间作为一个Region,同时控制大小不超过96MB。

  • 以 Region 为单位,将数据分散在集群中所有的节点上,并且尽量保证每个节点上服务的 Region 数量差不多
  • 以 Region 为单位做 Raft 的复制和成员管理,规避单Raft性能瓶颈。

\

MVCC

TiKV 的 MVCC 实现是通过在 Key 后面添加 Version 来实现:

Key1-Version3 -> Value
Key1-Version2 -> Value
Key1-Version1 -> Value
……
Key2-Version4 -> Value
Key2-Version3 -> Value
Key2-Version2 -> Value
Key2-Version1 -> Value
……
KeyN-Version2 -> Value
KeyN-Version1 -> Value
……

计算

表的存储

TiDB 对每个表分配一个 TableID,每一个索引都会分配一个 IndexID,每一行分配一个 RowID(如果表有整数型的主键,那么会用主键的值当做 RowID)。其中 TableID 在整个集群内唯一,IndexID/RowID 在表内唯一,这些 ID 都是 int64 类型。

行数据的KV存储:

Key: tablePrefix{tableID}_recordPrefixSep{rowID}
Value: [col1, col2, col3, col4]

唯一索引的存储:

Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue
Value: rowID

非唯一索引的数据:

Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue_rowID
Value: null

Prefix:

tablePrefix     = []byte{'t'}
recordPrefixSep = []byte("_r")
indexPrefixSep  = []byte("_i")

元信息的存储

数据库/表的元信息,以单独的m_为前缀存储在KV中

SQL的执行

调度

调度的目标

  • 一个 Region 的 Replica 数量正确
  • 一个 Raft Group 中的多个 Replica 不在同一个位置
  • 副本在 Store (TiKV节点) 之间的分布均匀分配
  • Leader 数量在 Store 之间均匀分配
  • 访问热点数量在 Store 之间均匀分配
  • 各个 Store 的存储空间占用大致相等
  • 控制调度速度,避免影响在线服务
  • 支持手动下线节点

调度的操作

  • 增加/删除一个副本
  • 将 Leader 角色在一个 Region Raft Group 的不同副本之间迁移

调度的实现

  • 调度由PD生成策略,Region Raft Group自主执行
  • TiKV定期向PD发有心跳包,汇报TiKV机器负载情况
  • Region的Leader定期向PD发心跳包,汇报Region的情况
  • PD通过心跳包收集信息,获得整个集群的详细数据,并且根据这些信息以及调度策略生成调度操作序列。
  • PD通过给Region的Leader心跳包回包中下发策略,通知Leader执行调度,并有后续的心跳包监控执行结果。

与MySQL的兼容性

  • 高度兼容MySQL 5.7 协议,支持分布式事务。
  • 字符集近支持asciilatin1binaryutf8utf8mb4gbk
  • 不支持外键约束、存储过程与函数、触发器、事件、自定义函数、全文语法与索引、空间类型函数等
  • AUTO_INCREMENT仅保证唯一,不保证集群全局连续与自增(单个TiDB内可保持)。仅为唯一的情况,建议使用AUTO_RANDOM
  • 多行结果查询SQL行顺序不稳定,因结果可能由多个TiKV汇总。
  • 执行计划分析explain与MySQL有差异
  • 业务自定义生成的自增字段可能导致热点数据问题
  • 删除数据为标记删除,由后台异步GC实现物理删除

试用

# 安装tiup
curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh

# 启动tidb(会自动下载相应组件)
tiup playground v6.1.0 --db 1 --pd 1 --kv 1

引入TiDB实践

背景

业务存在如下问题:

  1. 原数据库为MongoDB,单节点部署,存在单机故障风险
  1. 接口性能遇到瓶颈,磁盘IOPS到达瓶颈

性能测试

3台8C16G机器,混合部署TiDB Server、TiKV、PD,Sysbench select性能测试:

平均CPU使用率并发数select qps平均耗时ms99耗时ms
39%162.38w0.670.88
60%324.1w0.780.98
81%645.93w1.083.24
89%1287.54w1.697.52
92%2568.86w2.8914.0
94%51210w5.1017.9
96%102410.7w9.5831.4
97%204811.4w18.1760.6

引入方案

  1. 改动代码,引入TiDB组件,原有MongoDB平行,对TiDB不进行读写,T日发布生产,观察1周

image.png 2. T+1w,进行冷同步

image.png 3. T+8d,停写、增量同步

image.png

  1. T+8d,完成增量同步后,开双写 ,观察业务约一周

image.png

  1. T+2w,切读TiDB,并注意观察业务情况一周

image.png

  1. T+3w,切写TiDB,关闭双写,并注意观察业务情况一周

image.png

  1. 观察期注意点

    1. 业务流程是否正常
    2. 业务接口吞吐量、耗时情况
    3. TiDB机器资源消耗情况,CPU、内存、磁盘等
    4. TiDB数据库指标情况,SQL耗时、吞吐量等