TiDB

简介

NewSQL

将SQL的ACID保证与NoSQL的可扩展性和高性能相结合的新型数据库 TiDB TiDB 是一个分布式 NewSQL 数据库。它支持水平弹性扩展、ACID 事务、标准 SQL、MySQL 语法和 MySQL 协议,具有数据强一致的高可用特性,是一个不仅适合 OLTP 场景还适合 OLAP 场景的混合数据库。

Why

用 TiDB 之前,元数据是存在 MySQL 里的一个 2.8TB 的盘,因为增长的特别快,所以导致磁盘不够用,只能用分库分表的方案。我们以前用的的分库分表方案是 MyCAT。但用这个方案的过程中我们有遇到了一些问题,比如丢数据。某一个数据我 commit 了之后,最后发现这个数据丢了。 再就是连接的问题,目前头条做分片是大概固定分 100 个片。如果你的业务是需要分库分表,那你这边搞 101 个分片,这样有些业务,他用了一个分片键,用分片键来做查询,那可能中间件只用一个连接就可以找到相关数据。但有些业务,确实有不带分片键的请求。会导致 select 语句过来的时候,下面会建 101 个对后端的连接,也就是说,因为有连接的限制,有一个没有带分片键的这种请求过来之后, MyCAT 可以启 101 个连接到后面的每一个 MySQL 库。那这样的话,有时候我给它 5 万个连接,他一下子就把一百万用掉了。这样会导致它在非分片键的 select 请求,它连接速度消耗非常快,经常在业务这边会抛出说,连接数不够。

架构&组件

架构图

TiDB Server

接收SQL请求,处理SQL相关逻辑,并通过PD找到存储计算所需地址的TiKV地址,从TiKV获取数据,返回结果。 TiDB不存储数据,只负责计算,可以无限水平扩展,可以通过LB组件对外提供统一接入的地址。 PD Server / Placement Driver

  1. 存储集群的元信息(某个Key存储在哪个TiKV节点)
  2. 对TiKV集群进行调度和LB(如数据的迁移、Raft Group Leader的迁移等)
  3. 分配全局唯一且递增的事务ID
  4. 是一个集群,需要部署奇数个节点,至少3个

TiKV Server

负责存储数据,是一个分布式的提供事务的KV存储引擎,存储的基本单位是Region,每个Region存储一个Key Range的数据,每个TiKV节点负责多个Region,以Region为单位管理Raft副本,保持数据一致性和容灾。TiKV的LB由PD调度,以Region为单位进行调度。

TiSpark

将Spark SQL直接运行在TiDB存储层上。

特点

水平扩展

  • 随着业务的增长,可以增加TiDB节点,提供更高的吞吐
  • 随着数据的增长,可以增加TiKV节点,PD以Region为单位,将部分数据迁移到新的节点上。

高可用

  • TiDB:单个实例失效时,影响这个实例上进行的Session,会出现单次请求失败的情况。重新连接后可以继续服务。可以重启这个实例或者部署一个新的实例。
  • PD:PD是一个集群,通过Raft协议保持数据的一致性,一个实例失效时,如果这个实例不是Raft leader,服务不受影响;如果实例是Raft leader,会重新选主,自动恢复服务。
  • TiKV:TiKV是一个集群,通过Raft协议保持数据的一致性,一个节点失效时,会影响这个节点所有Region,如果失效节点是Raft leader,会中断服务,重新选主;如果不是leader,不会影响服务。某个TiKV节点失效一段时间后,PD会将其数据迁移到其他TiKV节点上。

保存数据

Key-Value

TiKV选择的是KV模型,简单来讲:

  1. 是一个巨大的map,存储的是KV pair
  2. 这个map中的KV pair按照Key的二进制顺序有序排列

RocksDB

任何持久化的引擎,数据终归要保存在磁盘上,TiKV使用RocksDB向磁盘上写数据。

Raft

Raft是一个一致性算法,TiKV使用Raft来做数据复制,每个数据变更都会落地成为一条Raft日志,通过Raft的日志复制,将数据安全可靠地同步到多数节点中。 Raft in TiDB

通过单机的RocksDB,我们将数据快速的存储在磁盘上;通过Raft,我们将数据复制到多台机器上。

Region

KV系统:1. Hash,2. Range TiKV选择2,并且每一个Range作为一个Region(每个Region默认64mb)

  • 负载均衡组件实现了将Region尽量均匀的散布在各个节点上
  • Raft以Region为单位做数据的复制,一个Region的多个副本保存在不同的节点上,构成一个Raft Group。 Raft of Region

MVCC

版本号直接加在Key后面

事务

  • Percolate模型
  • 乐观锁:只有在提交过程中,才会做冲突检测

SQL层

关系映射(SQL <=> KV)

Key: tablePrefix_rowPrefix_tableID_rowID
Value: [col1, col2, col3, col4]

// index数据
Key: tablePrefix_idxPre
fix_tableID_indexID_indexColumnsValue
Value: rowID

eg.假设表中有3行数据

  1. “TiDB”, “SQL Layer”, 10
  2. “TiKV”, “KV Engine”, 20
  3. “PD”, “Manager”, 30
    t_r_10_1 --> ["TiDB", "SQL Layer", 10]
    t_r_10_2 --> ["TiKV", "KV Engine", 20]
    t_r_10_3 --> ["PD", "Manager", 30]
    

SQL运算

Select count(*) from user where name="TiDB";
  1. 构造Key Range:​RowID∈[0, MaxInt64)​ => 获取到 ​[StartKey, EndKey)​
  2. 扫描Key Range:读取TiKV中的数据
  3. 过滤数据:计算 ​name=”TiDB”​ 这个表达式,得到正确的结果
  4. 计算Count

问题:

  • 每一行通过KV操作从TiKV中取出,RPC开销很大
  • 不满足条件的行也读取出来了
  • 符合要求的行的值没有意义,此处只需要知道有几行

分布式SQL计算

分布式SQL计算

原则:将计算尽量靠近存储节点,以避免大量RPC调用。

详细介绍TiDB如何让SQL跑得更快

SQL层架构

SQL层架构图

Reference

TiDB官方文档

十问TiDB:关于架构设计的一些思考

TiDB源码

Table of Contents