从小白到架构师(3): 揭开分布式数据库的面纱
作者:互联网
从小白到架构师(3): 揭开分布式数据库的面纱
「从小白到架构师」系列努力以浅显易懂、图文并茂的方式向各位读者朋友介绍 WEB 服务端从单体架构到今天的大型分布式系统、微服务架构的演进历程。「从小白到架构师」 系列的前两篇中多次提到各类分布式数据库,第三篇文章「揭开分布式系统的面纱」我们从基本的分库出发,来一一探索构建高可靠、高性能分布式数据库路上遇到的难题。
分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。从概念上说,将业务服务器和数据库部署在两台服务器上并通过 tcp 连接便组成了一个最简单的分布式系统。当然这样的分布式系统并没有研究的意义,我们研究分布式系统是为了解决实际问题:
是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。
通过多机备份避免单机故障
通过增加或减少机器数量灵活调整系统的吞吐量
在本系列中我们已经提到过很多分布式存储系统了, 比如高可靠的 Etcd、ZooKeeper 前面提到的 TiDB 数据库。无论这些数据库看上去多么 fancy 他们的思路归根到底无外乎数据分片和副本。
数据分片:
数据分片的核心是分片策略,即如何决定一条数据应该放到哪一个分片中。
首先要决定根据哪个字段进行分片,我们将这个决定分片的字段称为分片键(partition key), 在上图的示例中用户 ID 就是我们的分片键。
对于关系型数据库而言,通常在创建数据表时指定分片键,比如 Cassandra 和 TiDB 都需要在建表时指定分片键,对于 Redis 这类 KV 结构数据库来说只有一个 key 可以用来做分片键了。
CREATE TABLE articles (
id INT NOT NULL PRIMARY KEY,
uid INT,
title VARCHAR(60),
content TEXT,
create_time DATE NOT NULL DEFAULT '1970-01-01',
)
PARTITION BY HASH(uid) // TiDB 建表语句,按照 store_id 进行 hash 分区
PARTITIONS 3;
决定分片键后,就需要将分片键映射到节点上。最简单的方法是通过哈希 hash(partition_key) % db_num, 还有一种常用的方法是号段法(Range),即 partition key 1~1000 的在节点A、1000~2000的在节点 B……
哈希分片的好处在于数据均匀分布,单个节点的负载不会太大;而号段法则可能将一段时间内产生的数据都集中在一个分片上(特别是 partition key 为自增或由 snowflake 算法生成时),从另一个方面看某个时间段内的数据都集中在一个分片更便于进行范围查询或者统计分析,比如存储交易数据时就可以采用号段法。
分布式SQL执行引擎
分布式SQL引擎主要的目的就是实现与单机数据库SQL引擎的完全兼容。目前我们的SQL引擎能够做到与MySQL的SQL引擎全兼容,包括各类join和各类复杂函数等。他主要包含SQL解析、优化、执行和合并四个流程,如下图绿色部分:
虽然SQL是兼容的,但是分布式SQL执行算法与单机SQL的执行算法却完全不同,原因也很简单,网络通信的延迟比单机内通信延迟的大得多。举个例子说明一下,我们要从一张纸A上抄写全部内容到另外一张纸B上,单机系统就好比两张纸都在同一个办公室里,而分布式数据库则就像是一张纸在北京,一张纸在杭州。
自然的,如果两张纸在同一个办公室,因为传输距离近,逐行抄写的效率是可以接受的。而如果距离是北京到杭州,用逐行抄写的方式就立刻显得代价太高了,如果千辛万苦的飞去杭州,却只能写下一行数据,那这么抄写明显的效率太低了~在这种情况下,还是把纸A上的信息拍个照片,把这张照片带到杭州再去抄写明显更简单一些。这就是分布式数据库特别强调吞吐调优的原因,只要是涉及到跨机的所有查询,都必须尽可能的积攒一批后一起发送,以减少系统延迟提高带来的不良影响。
「从小白到架构师」系列努力以浅显易懂、图文并茂的方式向各位读者朋友介绍 WEB 服务端从单体架构到今天的大型分布式系统、微服务架构的演进历程。「从小白到架构师」 系列的前两篇中多次提到各类分布式数据库,第三篇文章「揭开分布式系统的面纱」我们从基本的分库出发,来一一探索构建高可靠、高性能分布式数据库路上遇到的难题。
分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。从概念上说,将业务服务器和数据库部署在两台服务器上并通过 tcp 连接便组成了一个最简单的分布式系统。当然这样的分布式系统并没有研究的意义,我们研究分布式系统是为了解决实际问题:
是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。
通过多机备份避免单机故障
通过增加或减少机器数量灵活调整系统的吞吐量
在本系列中我们已经提到过很多分布式存储系统了, 比如高可靠的 Etcd、ZooKeeper 前面提到的 TiDB 数据库。无论这些数据库看上去多么 fancy 他们的思路归根到底无外乎数据分片和副本。
数据分片:
数据分片的核心是分片策略,即如何决定一条数据应该放到哪一个分片中。
首先要决定根据哪个字段进行分片,我们将这个决定分片的字段称为分片键(partition key), 在上图的示例中用户 ID 就是我们的分片键。
对于关系型数据库而言,通常在创建数据表时指定分片键,比如 Cassandra 和 TiDB 都需要在建表时指定分片键,对于 Redis 这类 KV 结构数据库来说只有一个 key 可以用来做分片键了。
CREATE TABLE articles (
id INT NOT NULL PRIMARY KEY,
uid INT,
title VARCHAR(60),
content TEXT,
create_time DATE NOT NULL DEFAULT '1970-01-01',
)
PARTITION BY HASH(uid) // TiDB 建表语句,按照 store_id 进行 hash 分区
PARTITIONS 3;
决定分片键后,就需要将分片键映射到节点上。最简单的方法是通过哈希 hash(partition_key) % db_num, 还有一种常用的方法是号段法(Range),即 partition key 1~1000 的在节点A、1000~2000的在节点 B……
哈希分片的好处在于数据均匀分布,单个节点的负载不会太大;而号段法则可能将一段时间内产生的数据都集中在一个分片上(特别是 partition key 为自增或由 snowflake 算法生成时),从另一个方面看某个时间段内的数据都集中在一个分片更便于进行范围查询或者统计分析,比如存储交易数据时就可以采用号段法。
分布式SQL执行引擎
分布式SQL引擎主要的目的就是实现与单机数据库SQL引擎的完全兼容。目前我们的SQL引擎能够做到与MySQL的SQL引擎全兼容,包括各类join和各类复杂函数等。他主要包含SQL解析、优化、执行和合并四个流程,如下图绿色部分:
虽然SQL是兼容的,但是分布式SQL执行算法与单机SQL的执行算法却完全不同,原因也很简单,网络通信的延迟比单机内通信延迟的大得多。举个例子说明一下,我们要从一张纸A上抄写全部内容到另外一张纸B上,单机系统就好比两张纸都在同一个办公室里,而分布式数据库则就像是一张纸在北京,一张纸在杭州。
自然的,如果两张纸在同一个办公室,因为传输距离近,逐行抄写的效率是可以接受的。而如果距离是北京到杭州,用逐行抄写的方式就立刻显得代价太高了,如果千辛万苦的飞去杭州,却只能写下一行数据,那这么抄写明显的效率太低了~在这种情况下,还是把纸A上的信息拍个照片,把这张照片带到杭州再去抄写明显更简单一些。这就是分布式数据库特别强调吞吐调优的原因,只要是涉及到跨机的所有查询,都必须尽可能的积攒一批后一起发送,以减少系统延迟提高带来的不良影响。