1. 前言
在安装TiDB之前,首先要了解下TiDB,这是TiDB官网文档还是比较全面的.
2. TiDB整体架构
- TiDB(TiDB Cluster)
- 对外暴露MySQL协议.
- 负责接受客户端的连接,执行SQL解析和优化,最终生成分布式执行计划.
- 只是解析SQL,将实际的数据读取请求转发给底层的存储节点TiKV.
- 有没有感觉,有点像MyCat(Proxy).
- PD(Placement Driver)
- 整个TiDB集群的元信息管理模块.
- 存储每个TiKV节点实时的数据分布情况和集群的整体拓扑结构.
- 下发数据调度命令给具体的TiKV节点.
- 分配分布式事务分配事务ID.
- 建议部署奇数个PD节点.
- 有没有感觉像ZK(HBase利用ZK来存储Region数据).
- TiKV
- 存储数据的基本单位是Region,每个Region负责存储一个Key Range(从StartKey到EndKey的左闭右开区间)的数据,每个TiKV节点会负责多个Region(还记得HBase(LSM)的架构吗?).
- 在KV键值对层面提供对分布式事务的原生支持.
- TiKV中的数据都会自动维护多副本(默认为三副本)天然支持高可用和自动故障转移.
- TiFlash
- 主要的功能是为分析型的场景加速.
- 数据是以列式的形式进行存储.
3. TiKV(存储)架构
TiKV存储特性
- 可以理解为:一个巨大的Map,也就是存储的是:Key-Value Pairs(键值对)
- TiKV将整个Key-Value空间分成很多段,每一段是一系列连续(顺序)的Key,将每一段叫做一个Region(还记得:HBase吗?)
- 在写入数据时,会路由到某个Region,然后,会以Region为单位进行副本复制(Raft),达到高可性(可以理解为:一个TiKV会包含N个Region).
- TiKV没有选择直接向磁盘上写数据,而是把数据保存在RocksDB中,具体的数据落地由RocksDB负责.
- 通过单机的RocksDB,TiKV可以将数据快速地存储在磁盘上.
- 通过Raft协议,将数据复制到多台机器上,以防单机失效(数据的写入是通过Raft这一层的接口写入,而不是直接写RocksDB),即使,少数几台机器宕机也能通过原生的Raft协议自动把副本补全,可以做到对业务无感知.
4. TiDB(计算)架构
TiDB如何将库表中的数据映射到TiKV中的(Key, Value)键值对的呢?
以TiDB官网的一张图为例,来剖析:
# 1. 创建表:t_test
CREATE TABLE t_test (
id INT,
name VARCHAR(20),
age INT,
score DECIMAL(10,2),
PRIMARY KEY (id),
UNIQUE KEY (name),
INDEX idx_age (age)
);
# 2. 插入数据
INSERT INTO t_test(id,name,age,score) VALUES(1,"Bob",12,99);
# 3. 查看一下索引信息
mysql> show index from t_test;
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+-----------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression | Clustered |
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+-----------+
| t_test | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL | YES |
| t_test | 0 | name | 1 | name | A | 0 | NULL | NULL | YES | BTREE | | | YES | NULL | NO |
| t_test | 1 | idx_age | 1 | age | A | 0 | NULL | NULL | YES | BTREE | | | YES | NULL | NO |
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+-----------+
# 4. TiDB中是如何把表中的数据与Key映射的呢?
# 4.1 TiDB在创建表时,会为表(t_test)分配一个唯一的表ID(TableID)
# 4.2 TiDB在插入数据时,会为每一行数据生成(表没有主键的情况下)一个唯一的行ID(RowID),当,表有主键的情况下,RowID就是主键ID.
# 4.3 TiDB为了支持索引,会为表中每个索引分配了一个索引ID,用 IndexID 表示.
# 4.4 tablePrefix(t)/recordPrefixSep(r)/indexPrefixSep(i)都是字符串常量.
# 4.5 如果,按官网这种方式存储数据,对于LIKE肯定是支持不到位的(因为,要支持LIKE,意味着:要存储大量的索引数据).
# 5. 主键索引与KV的映射
# Key: tablePrefix{TableID}_recordPrefixSep{RowID}
# Value: [col1, col2, col3, col4]
在主键索引的情况下,上图的数据在TiDB存储方式为:
KEY : t{TableID}_r1
VALUE : [Bob,12,99]
# 6. 唯一索引与KV的映射
# Key: tablePrefix{TableID}_indexPrefixSep{IndexID}_indexedColumnsValue
# Value: RowID
在唯一索引情况下,上图的数据在TiDB存储方式为:
KEY: t_{TableID}_i_Bob
VALUE: 1
# 7. 非唯一索引与KV映射
# Key: tablePrefix{TableID}_indexPrefixSep{IndexID}_indexedColumnsValue_{RowID}
# Value: null
在非唯一索引情况下,上图的数据在TiDB存储方式为(直接解析KEY,就可以找到对应的数据):
KEY: t_{TableID}_i_12_1
VALUE : NULL
5. 总结
- 感觉:TiDB是运用Raft协议,协调所有的RocksDB进行数据存储.
- 由于LSM的特性(数据有序性),在逻辑上划分多个Region(对Map中的Key进行分区,不至于让所有数据都在一个Map里),以此:实现数据的负载均衡.