当前位置: 首页 > 图灵资讯 > 技术篇> 微服务架构下如何做数据分区呢?

微服务架构下如何做数据分区呢?

来源:图灵教育
时间:2023-04-28 09:16:37

 

  对于大型分布式集群或数据密集型应用,为了提高吞吐量、性能和可用性,一般采用数据复制和数据分区。数据复制将单个库的请求压力分配给更多的数据库例子。数据分区将每个例子中的巨大数据文件按一定的规则划分为更小的数据文件,并可以存储到不同的磁盘(或数据节点) Node)为了提高要求的并发性能,同时增加可扩展性。

  本文将介绍分布式存储集群高可用性的另一个解决方案——数据分区,以及 MySQL 以一些数据分区的具体实现为例。了解数据分区

  复制和分区有什么区别?请看下图:

微服务架构下如何做数据分区呢?_数据

分区和分表有什么区别?

  对于一些大规模数据集群的应用,经常可以听到分库分表的解决方案。根据服务维度划分微服务系统是不可避免的(一个微服务对应于一个数据库),但当一个微服务刚刚开始,领域模型刚刚建立时,不要在一开始就根据感觉估计需要制作分表。若在早期阶段为了扩展数据存储,选择分区将更好地实施,许多存储引擎都内置了建立分区的策略。架构是进化的,可以在业务数据增加到一定数量后进行分表。

  下面具体看数据库分表和分区的区别:

  (1)存储结构和查询逻辑

  分表:一个表将分为多个表。分表后的每个表都有自己的索引文件、数据文件和表结构文件。具体的分表将通过分表字段的查询句找到,而不是所有的分表。

  分区:一个表分区或一个表,但数据文件和索引文件分为较小的数据文件和索引文件。查询仍然是一个表,但数据需要从多个分区进行搜索和合并。

  (2)实现方式

  分表:分表的方法有很多,有的是数据库存储引擎内置的方案,对应用开发透明。如果发动机不支持,需要找一些分表中间件来实现,比如 MySQL 可使用开源 Cobar,以及基于国内开源爱好者的国内开源爱好者 Cobar 开发,解决了一些问题 Cobar 问题的 mycat,类似,阿里的 Tddl 也是支持分表的中间件。

  分区:实现简单,大多数存储服务都有自己的分区实现。对于用户来说,它与通常的写入和阅读方法没有什么不同。应用程序开发是透明的,但在建立表格时需要确定分区方案。

  (3)适用场景

  分表:适用于基于业务索引的分表。分表列不一定是主键或包含主键。需要手动创建多个表。阅读和写作时,应根据分表规则将路由应用到特定的子表中。

  分区:适用于基于主键的分区。创建表不需要任何手动处理,应用程序仍然按照原始访问表的规则要求,通常由存储引擎提供到分区的路由。创建分区规则需要更加谨慎,以减少分区之间数据的依赖。

  虽然两者之间存在一些差异,但单表中有大量的数据。大多数情况下,它们被分区和分区一起使用,两者都可以提高阅读和写作的性能。如果你想快速扩大容量,你可以先分区,然后分区表。分区对开发、运行和维护更加透明。数据分区的好处

  了解分区与分表的区别,重点关注回分区。对于大规模数据集群的应用,数据分区的优点是: 性能:通过将大数据切割到不同的数据节点,提高访问性能。 可用性:分区允许部分分区节点崩溃,分散风险,提高可用性。 易于使用:与复制一样,应用程序完全透明,分区 + 复制可以说是倚天屠龙的大规模数据存储应用。 实现数据分区

  当你决定对一堆大数据进行数据分区时,你需要决定在哪个维度上进行分区,即当数据被写入时,数据应该放在哪个分区中。如果没有明确的目的和足够的理解如何设计分区策略,分区后的性能很容易下降。存储服务如 HBase、Cassandra、MySQL 会有内置的分区策略,不同存储引擎的分区策略使用的加密算法和支持类型会有所不同,但想法是相似的。下面以 MySQL 以分区方案为例,以及适合不同分区方案的场景。MySQL 的分区策略

  MySQL 截止到 5.7 版本主要提供以下分区类型,并简要列出一些场景。Hash 分区

  Hash 常规分区有两种方式。 Hash 和线性 Hash。常规 Hash 是使用 MySQL 内置的 Hash 并且可以添加用户定制的函数来计算指定列的值。但是这个函数和所选列值也有限制:计算指定函数后的值必须是整数值类型(int,bigint)这种方法简单易懂,分区均匀。但问题是,当需要重新分区时,比如原来是 10 个分区,现在要分 15 一个分区,需要数据迁移,Hash 分区不易重新分区和扩展。

  线性 Hash 以线性为基础 2 算法规则:分区值 = POWER(2, CEILING(LOG(2, 等待计算的key))。线性 Hash 扩大分区或缩小分区时,更容易扩大,适用于一开始分区数量不太确定的情况。

  两者都适用于连续性,如连续性 key 例如,如果你想按照一列均匀地划分场景,你可以选择 Hash,Hash 分区比线性分区更均匀。考虑到未来迁移可能扩大,使用线性分区。

  使用 Hash 分区,只需在建表语句后添加分区策略即可。分区公式:HASH( your expression(column_name) ),示例使用 Year 函数的 Hash 分区: PARTITION BY HASH(YEAR (xx_id))PARTITIONS 10; Range 分区

  Range 分区可以理解为分区的列值(xx_id),按一定范围划分,这个范围需要从一开始就定义,比如可以 1-10000,10001-20000。一般比较适合按月规则划分时间分区。MySQL 内置支持用 Less Than 语法划分范围。

  也是在 create table 之后,添加分区信息,写法如下: PARTITION BY RANGE (xx_id) ( PARTITION p0 VALUES LESS THAN (10000), PARTITION p1 VALUES LESS THAN (20000), PARTITION p2 VALUES LESS THAN (50000), PARTITION p3 VALUES LESS THAN MAXVALUE);

  如果按时间戳,可以使用内置函数UNIX_TIMESTAMP(按照 TIMESTAMP 时间戳类型字段),YEAR(按照 DATE 类型时间字段的年份)等,如下: PARTITION BY RANGE ( UNIX_TIMESTAMP(work_time) ) ( PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2016-01-01 00:00:00') ), PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2017-01-01 00:00:00') ), PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2018-01-01 00:00:00') ), PARTITION p9 VALUES LESS THAN (MAXVALUE)); Key 分区

  Key 分区跟 Hash 分区很像。不过 Key 使用分区不提供用户自定义函数 MySQL 提供的 Hash 设置分区的函数和列必须至少包含部分或全部主键或唯一索引。如果表中没有主键,则需要使用 INT 类型,非空(Not Null)、唯一索引(Unique Key),否则会报错。同时,Key 分区还支持类似的线性 Hash 算法的 线性 Key,如下示例: CREATE TABLE test ( t_key INT NOT NULL PRIMARY KEY, t_name CHAR(5), t_date DATE)PARTITION BY LINEAR KEY (t_key)PARTITIONS 10; List 分区

  List 分区 跟 Range 分区相似,但是 Range 是连续值在一定范围内落在一个分区, List 通过指定的离散值提供分区方法。写入的列值落在 List 集合中在这个分区,如果写入的值没有定义好 List 在里面,会报错:Table has no partition for value XX。如下示意: CREATE TABLE test_list ( birth_month INT, name VARCHAR(10), ) PARTITION BY LIST(birth_month) ( PARTITION p0 VALUES IN (1, 2, 3), PARTITION p1 VALUES IN (4, 5, 6), PARTITION p2 VALUES IN (7, 8, 9), PARTITION p3 VALUES IN (10, 11, 12) ); Columns 分区

  以上四个分区都需要分区 key 是 Int 类型数据列,Columns 分区可以接受一些非 Int 类型的值。Columns 根据范围值,分区提供了两种分区策略: Range Columns 以及离散值 List Columns 分区。

  如下使用 List Columns 示例: CREATE TABLE test_list_columns ( birth_month VARCHAR(25), name VARCHAR(10), city VARCHAR(15), ) PARTITION BY LIST COLUMNS(city) ( PARTITION p_north VALUES IN('Haerbin', 'Beijing', 'Jiamusi'), PARTITION p_east VALUES IN('Shanghai', 'Hangzhou', 'Nanjing'), PARTITION p_west VALUES IN('Xian', 'Lasa', 'Chengdu'), PARTITION p_sourth VALUES IN('Guangzhou', 'Nanning') ); 实现分区路由

  分区数据后,分区文件将分散到不同的数据节点(Node)里面(如文章前面第一张图的示意)。对于一个请求,你需要知道哪个请求。 Node 哪个分区,也就是要求的路由。路由也是“服务发现”的一部分,不仅用于存储 DB,也可用于应用层的服务路由。

  不同的存储引擎,有些有内置的路由方案,有些需要通过一些中间部件进行路由配置,但基本策略相似,更常见的路由方式有: 客户端(应用程序)连接到任何一个 Node 例如,如果是这样 Node 这个数据分区可以处理,否则请求可以转发到其他可以处理这个数据的分区 Node。 客户端首先将请求发送到请求转发中心(路由中心),然后由代理中心决定将数据请求转发到哪里 Node 上面。这个代理不处理任何数据请求,只平衡一个分区的负载。 客户端自己做服务发现,也就是客户端知道发送请求时要发送哪个分区,应该连接哪个分区 Node。

  Cassandra 使用 Gossip 协议管理集群服务实例的状态(节点在线和离线)。总体思路是第一个方案,请求发送给任何节点,然后节点决定如何处理或转发请求,以避免类似于其他服务 ZK 依赖,更灵活。

  许多分布式数据系统依赖于独立的特殊处理服务发现的合作服务,如 ZooKeeper。HBase、Kafka 就是通过 Zookeeper 服务发现,整体路由模型类似于第二种方式。MongoDB 也比较相似,只是用它自己带的。 Config Server 实现路由服务。

  Redis 分区方案以第三种路由方案为基础,客户端需要自行分区路由。目前广泛使用的开源路由代理有 Twitter 的 twemproxy。Twemproxy 相当于路由代理,客户端发送请求 Twemproxy,再由 Twemproxy 通过对 Redis 分区路由配置的分析将其请求转发给包含数据的分区 Redis 节点,并将结果返回客户端。小结

  除数据存储服务外,本文还介绍了数据存储服务的分区 MySQL,许多存储引擎支持分区,分区可以拆分现有的大数据,并对开发人员透明。但是分区也有一些问题,很多存储引擎的分区数一般都有上限,比如 MySQL 5.6.7 版本之前支持的最大分区数是 1024,这个版本后来支持了 8192 在制定了个分区数和分区策略后,一般需要进行一定的数据迁移才能重新分区,因此最初的分区策略选择尤为重要。

  在分布式存储集群中,无论是否使用微服务,都需要优化存储层,或者随着领域模型数据的增加,从上到下优化,如 DB 阅读要求负载高,可考虑使用 Cache、搜索。如果有一些数据热区,可以垂直拆分一些大表,从领域模型中抽取一些字段,建立相关表,因为并非所有请求都必须返回所有列的数据。如仍不能分享,可考虑用上一篇文章介绍的数据库复制,先水平扩容。如果表中的数据量很大,检索效率仍然很低,可以考虑使用本文中提到的数据分区,但请注意,查询应尽可能通过少数分区,以防止扫描过多的分区文件。如果仍然不能满足性能和扩展要求,可以考虑使用一些中间部件进行水平拆分-分表,以便请求尽可能落在一个分表中。假如分表很难满足场景,对于写少读多的场景,那么就可以做其它冗余查询维度的分表。

  集群扩张的实际选择因团队和业务而异。在了解了基本的分布式存储知识后,您可以扩展到应用程序服务。