Java重要面试名词整理(七):分库分表
文章目录
- 搭建MySQL主从集群
- GTID同步集群
- MySQL高可用方案
- 1、MMM
- 2、MHA
- 3、MGR
- ShardingSphere
- 理解ShardingSphere的核心概念
- 垂直分片与水平分片
- ShardingSphere实现分库分表的核心概念
- 常见的分片策略
- 简单INLINE分片算法
- STANDARD标准分片算法
- COMPLEX_INLINE复杂分片算法
- CLASS_BASED自定义分片算法
- HINT_INLINE强制分片算法
- 其他功能
- 分片审计
- 数据加密
- 读写分离
- 广播表
- 绑定表
- 工作原理
- 配置原理
- SQL Parser: SQL解析引擎
- SQL Router- SQL 路由引擎
- SQL Rewriter : SQL 优化引擎
- SQL Executor : SQL执行引擎
- Result Merger: 结果归并
- ShardingProxy
- ShardinSphere中的分布式事务机制
- 什么是XA事务?
- CosId与ShardingSphere
- 主要的主键生成策略
- 1、数据库策略
- 2、应用单独生成
- 3、第三方服务统一生成
- 4、与第三方结合的segment策略
- 根据雪花算法扩展基因分片法
- 理解CosId
搭建MySQL主从集群
GTID同步集群
GTID的本质也是基于Binlog来实现主从同步,只是他会基于一个全局的事务ID来标识同步进度。GTID即全局事务ID,全局唯一并且趋势递增,他可以保证为每一个在主节点上提交的事务在复制集群中可以生成一个唯一的ID 。
在基于GTID的复制中,首先从服务器会告诉主服务器已经在从服务器执行完了哪些事务的GTID值,然后主库会又把所有没有在从库上执行的事务,发送到从库上进行执行,并且使用GTID的复制可以保证同一个事务只在指定的从库上执行一次,这样可以避免由于偏移量的问题造成数据不一致。
MySQL高可用方案
常见的MySQL集群方案有三种: MMM、MHA、MGR。这三种高可用框架都有一些共同点:
- 对主从复制集群中的Master节点进行监控
- 自动的对Master进行迁移,通过VIP。
- 重新配置集群中的其它slave对新的Master进行同步
1、MMM
MMM(Master-Master replication managerfor Mysql,Mysql主主复制管理器)是一套由Perl语言实现的脚本程序,可以对mysql集群进行监控和故障迁移。他需要两个Master,同一时间只有一个Master对外提供服务,可以说是主备模式。
他是通过一个VIP(虚拟IP)的机制来保证集群的高可用。整个集群中,在主节点上会通过一个VIP地址来提供数据读写服务,而当出现故障时,VIP就会从原来的主节点漂移到其他节点,由其他节点提供服务。
2、MHA
Master High Availability Manager and Tools for MySQL。是由日本人开发的一个基于Perl脚本写的工具。这个工具专门用于监控主库的状态,当发现master节点故障时,会提升其中拥有新数据的slave节点成为新的master节点,在此期间,MHA会通过其他从节点获取额外的信息来避免数据一致性方面的问题。MHA还提供了mater节点的在线切换功能,即按需切换master-slave节点。MHA能够在30秒内实现故障切换,并能在故障切换过程中,最大程度的保证数据一致性。在淘宝内部,也有一个相似的TMHA产品。
MHA是需要单独部署的,分为Manager节点和Node节点,两种节点。其中Manager节点一般是单独部署的一台机器。而Node节点一般是部署在每台MySQL机器上的。 Node节点得通过解析各个MySQL的日志来进行一些操作。
Manager节点会通过探测集群里的Node节点去判断各个Node所在机器上的MySQL运行是否正常,如果发现某个Master故障了,就直接把他的一个Slave提升为Master,然后让其他Slave都挂到新的Master上去,完全透明。
3、MGR
MGR:MySQL Group Replication。 是MySQL官方在5.7.17版本正式推出的一种组复制机制。主要是解决传统异步复制和半同步复制的数据一致性问题。
由若干个节点共同组成一个复制组,一个事务提交后,必须经过超过半数节点的决议并通过后,才可以提交。引入组复制,主要是为了解决传统异步复制和半同步复制可能产生数据不一致的问题。MGR依靠分布式一致性协议(Paxos协议的一个变体),实现了分布式下数据的最终一致性,提供了真正的数据高可用方案(方案落地后是否可靠还有待商榷)。
支持多主模式,但官方推荐单主模式:
- 多主模式下,客户端可以随机向MySQL节点写入数据
- 单主模式下,MGR集群会选出primary节点负责写请求,primary节点与其它节点都可以进行读请求处理.
ShardingSphere
ShardingSphere最为核心的产品有两个:一个是ShardingJDBC,这是一个进行客户端分库分表的框架。另一个是ShardingProxy,这是一个进行服务端分库分表的产品。他们代表了两种不同的分库分表的实现思路。
ShardingSphere-JDBC 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。
- 适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC;
- 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, HikariCP 等;
- 支持任意实现 JDBC 规范的数据库,目前支持 MySQL,PostgreSQL,Oracle,SQLServer 以及任何可使用 JDBC 访问的数据库。
ShardingSphere-Proxy 定位为透明化的数据库代理端,通过实现数据库二进制协议,对异构语言提供支持。 目前提供 MySQL 和 PostgreSQL 协议,透明化数据库操作,对 DBA 更加友好。
- 向应用程序完全透明,可直接当做 MySQL/PostgreSQL 使用;
- 兼容 MariaDB 等基于 MySQL 协议的数据库,以及 openGauss 等基于 PostgreSQL 协议的数据库;
- 适用于任何兼容 MySQL/PostgreSQL 协议的的客户端,如:MySQL Command Client, MySQL Workbench, Navicat 等。
这两个产品都各有优势。ShardingJDBC跟客户端在一起,使用更加灵活。而ShardingProxy是一个独立部署的服务,所以他的功能相对就比较固定。
Governance Center也是其中重要的部分。他的作用有点类似于微服务架构中的配置中心,可以使用第三方服务统一管理分库分表的配置信息,当前建议使用的第三方服务是Zookeeper,同时也支持Nacos,Etcd等其他第三方产品。
由于ShardingJDBC和ShardingProxy都支持通过Governance Center,将配置信息交个第三方服务管理,因此,也就自然支持了通过Governance Center进行整合的混合部署架构。
理解ShardingSphere的核心概念
垂直分片与水平分片
这是设计分库分表方案时经常会提到的概念。 其中垂直分片表示按照业务的纬度,将不同的表拆分到不同的库当中。这样可以减少每个数据库的数据量以及客户端的连接数,提高查询效率。而水平分表表示按照数据的纬度,将原本存在同一张表中的数据,拆分到多张子表当中。每个子表只存储一份的数据。这样可以将数据量分散到多张表当中,减少每一张表的数据量,提升查询效率。
ShardingSphere实现分库分表的核心概念
接下来我们依次解析一下刚才示例中配置的一些重要的概念,可以对照一下之前的配置信息进行验证。
- 虚拟库: ShardingSphere的核心就是提供一个具备分库分表功能的虚拟库,他是一个ShardingSphereDatasource实例。应用程序只需要像操作单数据源一样访问这个ShardingSphereDatasource即可。
- 真实库: 实际保存数据的数据库。这些数据库都被包含在ShardingSphereDatasource实例当中,由ShardingSphere决定未来需要使用哪个真实库。
- 逻辑表: 应用程序直接操作的逻辑表。
- 真实表: 实际保存数据的表。这些真实表与逻辑表表名不需要一致,但是需要有相同的表结构,可以分布在不同的真实库中。应用可以维护一个逻辑表与真实表的对应关系,所有的真实表默认也会映射成为ShardingSphere的虚拟表。
- 分布式主键生成算法: 给逻辑表生成唯一主键。由于逻辑表的数据是分布在多个真实表当中的,所以,单表的索引就无法保证逻辑表的ID唯一性。ShardingSphere集成了几种常见的基于单机生成的分布式主键生成器。比如SNOWFLAKE,COSID_SNOWFLAKE雪花算法可以生成单调递增的long类型的数字主键,还有UUID,NANOID可以生成字符串类型的主键。当然,ShardingSphere也支持应用自行扩展主键生成算法。比如基于Redis,Zookeeper等第三方服务,自行生成主键。
- 分片策略: 表示逻辑表要如何分配到真实库和真实表当中,分为分库策略和分表策略两个部分。分片策略由分片键和分片算法组成。分片键是进行数据水平拆分的关键字段。如果没有分片键,ShardingSphere将只能进行全路由,SQL执行的性能会非常差。分片算法则表示根据分片键如何寻找对应的真实库和真实表。简单的分片策略可以使用Groovy表达式直接配置,当然,ShardingSphere也支持自行扩展更为复杂的分片算法。
常见的分片策略
简单INLINE分片算法
行表达式分片策略(inline
)适用于具有单一分片键的简单分片场景,支持SQL语句中=
和in
操作符。
像= 和 in 这样的操作,可以拿到cid的精确值,所以都可以直接通过表达式计算出可能的真实库以及真实表,ShardingSphere就会将逻辑SQL转去查询对应的真实库和真实表。这些查询的策略,只要配置了sql-show参数,都会打印在日志当中。
STANDARD标准分片算法
标准分片策略(standard
)适用于具有单一分片键的标准分片场景。该策略支持精确分片,即在SQL中包含=
、in
操作符,以及范围分片,包括BETWEEN AND
、>
、<
、>=
、<=
等范围操作符。
COMPLEX_INLINE复杂分片算法
复合分片策略(complex
)适用于多个分片键的复杂分片场景,属性shardingColumns
中多个分片健以逗号分隔。支持 SQL 语句中有>
、>=
、<=
、<
、=
、IN
和 BETWEEN AND
等操作符。
CLASS_BASED自定义分片算法
我们测试数据中的user_id都是固定的1001L,那么接下来我就可以希望在对user_id进行范围查询时,能够提前判断一些不合理的查询条件。而具体的判断规则,比如在对user_id进行between范围查询时,要求查询的下限不能超过查询上限,并且查询的范围必须包括1001L这个值。如果不满足这样的规则,那么就希望这个SQL语句就不要去数据库执行了。因为明显是不可能有数据的,还非要去数据库查一次,明显是浪费性能。
虽然对于COMPLEX_INLINE策略,也支持添加allow-range-query-with-inline-sharding参数让他能够支持分片键的范围查询,但是这时这种复杂的分片策略就明显不能再用一个简单的表达式来忽悠了。
这就需要一个Java类来实现这样的规则。这个算法类也不用自己瞎设计,只要实现ShardingSphere提供的ComplexKeysShardingAlgorithm接口就行了。
HINT_INLINE强制分片算法
Hint强制分片策略相比于其他几种分片策略稍有不同,该策略无需配置分片健,由外部指定分库和分表的信息,可以让SQL在指定的分库、分表中执行。
其他功能
分片审计
分片审计功能是针对数据库分片场景下对执行的 SQL 语句进行审计操作。分片审计既可以进行拦截操作,拦截系统配置的非法 SQL 语句,也可以是对 SQL 语句进行统计操作。
目前ShardingSphere内置的分片审计算法只有一个,DML_SHARDING_CONDITIONS。他的功能是要求对逻辑表查询时,必须带上分片键。
数据加密
ShardingSphere内置了多种加密算法,可以用来快速对关键数据进行加密。
读写分离
在ShardingSphere中,实现读写分离也非常简单。只需要创建一个类型为readwrite-splitting的分片规则即可。这个配置看起来挺麻烦,但是思路和前面实现读写分离是一样的,就是将多个真实库组合成一个虚拟库
广播表
广播表表指所有的分片数据源中都存在的表,表结构及其数据在每个数据库中均完全一致。适用于数据量不大且需要与海量数据的表进行关联查询的场景
绑定表
绑定表指分片规则一致的一组分片表。使用绑定表进行多表关联查询时,必须使用分片键进行关联,否则会出现笛卡尔积关联或跨库关联,从而影响查询效率。
工作原理
配置原理
在进入ShardingSphere的内核之前,ShardingSphere做了大量的配置信息管控。不光是将应用的配置信息进行解析,同时ShardingSphere还支持将这些配置信息放到第三方的注册中心,从而可以实现应用层的水平扩展。
对于使用ShardingJDBC开发来说,或许这不是一个多起眼的功能。因为应用完全可以自己管理配置,或者自行接入Nacos这样的配置中心。但是如果运维人员使用ShardingProxy的话,这样的配置管控功能就非常有作用了。后面分享ShardingProxy时,我们再深入理解。
SQL Parser: SQL解析引擎
解析过程分为词法解析和语法解析。 词法解析器用于将SQL拆解为不可再分的原子符号,称为Token。并根据不同数据库方言所提供的字典,将其归类为关键字,表达式,字面量和操作符。 再使用语法解析器将SQL转换为抽象语法树(简称AST, Abstract Syntax Tree)。
SQL Router- SQL 路由引擎
分片策略通常可以采用由数据库内置或由用户方配置。数据库内置的方案较为简单,内置的分片策略大致可分为尾数取模、哈希、范围、标签、时间等。由用户方配置的分片策略则更加灵活,可以根据使用方需求定制复合分片策略。
实际使用时,应尽量使用分片路由,明确路由策略。因为广播路由影响过大,不利于集群管理及扩展。
- 全库表路由:对于不带分片键的DQL、DML以及DDL语句,会遍历所有的库表,逐一执行。例如 select * from course 或者 select * from course where ustatus=‘1’(不带分片键)
- 全库路由:对数据库的操作都会遍历所有真实库。 例如 set autocommit=0
- 全实例路由:对于DCL语句,每个数据库实例只执行一次,例如 CREATE USER customer@127.0.0.1 identified BY ‘123’;
- 单播路由:仅需要从任意库中获取数据即可。 例如 DESCRIBE course
- 阻断路由:屏蔽SQL对数据库的操作。例如 USE coursedb。就不会在真实库中执行,因为针对虚拟表操作,不需要切换数据库。
SQL Rewriter : SQL 优化引擎
在数据方言方面。Apache ShardingSphere 提供了 SQL 方言翻译的能力,能否实现数据库方言之间的自动转换。例如,用户可以使用 MySQL 客户端连接 ShardingSphere 并发送基于 MySQL 方言的 SQL,ShardingSphere 能自动识别用户协议与存储节点类型自动完成 SQL 方言转换,访问 PostgreSQL 等异构存储节点。
用户只需要面向逻辑库和逻辑表来写SQL,最终由ShardigSphere的改写引擎将SQL改写为在真实数据库中可以正确执行的语句。SQL改写分为正确性改写和优化改写。
正确性改写
在包含分表的场景中,需要将分表配置中的逻辑表名称改写为路由之后所获取的真实表名称。仅分库则不需要表名称的改写。除此之外,还包括补列和分页信息修正等内容。
优化改写
优化改写的目的是在不影响查询正确性的情况下,对性能进行提升的有效手段。它分为单节点优化和流式归并优化。比如我们之前提到,在当前版本下,对一个库的多次查询,会通过UNION 合并成一个大的SQL,这也是一种优化改写。
SQL Executor : SQL执行引擎
ShardingSphere 采用一套自动化的执行引擎,负责将路由和改写完成之后的真实 SQL 安全且高效发送到底层数据源执行。它不是简单地将 SQL 通过 JDBC 直接发送至数据源执行;也并非直接将执行请求放入线程池去并发执行。它更关注平衡数据源连接创建以及内存占用所产生的消耗,以及最大限度地合理利用并发等问题。执行引擎的目标是自动化的平衡资源控制与执行效率。
Result Merger: 结果归并
将从各个数据节点获取的多数据结果集,组合成为一个结果集并正确的返回至请求客户端,称为结果归并
其中重点是理解流式归并与内存归并:
- 流式归并是指每一次从结果集中获取到的数据,都能够通过逐条获取的方式返回正确的单条数据,它与数据库原生的返回结果集的方式最为契合。遍历、排序以及流式分组都属于流式归并的一种。通常内存限制模式就可以使用流式归并,比较适合OLTP场景。
- 内存归并则是需要将结果集的所有数据都遍历并存储在内存中,再通过统一的分组、排序以及聚合等计算之后,再将其封装成为逐条访问的数据结果集返回。通常连接限制模式就可以使用内存归并,比较适合OLAP场景。
ShardingProxy
ShardingSphere-Proxy,早前版本就叫做ShardingProxy。定位为一个透明化的数据库代理,目前提供MySQL和PostgreSQL协议,透明化数据库操作。简单理解就是,他会部署成一个MySQL或者PostgreSQL的数据库服务,应用程序只需要像操作单个数据库一样去访问ShardingSphere-proxy,由ShardingProxy去完成分库分表功能。
ShardinSphere中的分布式事务机制
如果你比较仔细,会发现,在之前的配置中,server.yaml中的rules部分,还有一个不太眼熟的配置 TRANSACTION 分布式事务管理器。
由于ShardingSphere是需要操作分布式的数据库集群,所以数据库内部的本地事务机制是无法保证ShardingProxy中的事务安全的,这就需要引入分布式事务管理机制,保证ShardingProxy中的SQL语句执行的原子性。也就是说,在ShardingProxy中打开分布式事务机制后,你就不需要考虑SQL语句执行时的分布式事务问题了。
什么是XA事务?
这其中XA是由X/Open Group组织定义的,处理分布式事务的标准。主流的关系型数据库产品都实现了XA协议。例如,MySQL从5.0.3版本开始,就已经可以直接支持XA事务了。但是要注意,只有InnoDB引擎才提供支持:
//1、 XA START|BEGIN 开启事务,这个test就相当于是事务ID,将事务置于ACTIVE状态
XA START 'test';
//2、对一个ACTIVE状态的XA事务,执行构成事务的SQL语句。
insert...//business sql
//3、发布一个XA END指令,将事务置于IDLE状态
XA END 'test'; //事务结束
//4、对于IDLE状态的XACT事务,执行XA PREPARED指令 将事务置于PREPARED状态。
//也可以执行 XA COMMIT 'test' ON PHASE 将预备和提交一起操作。
XA PREPARE 'test'; //准备事务
//PREPARED状态的事务可以用XA RECOVER指令列出。列出的事务ID会包含gtrid,bqual,formatID和data四个字段。
XA RECOVER;
//5、对于PREPARED状态的XA事务,可以进行提交或者回滚。
XA COMMIT 'test'; //提交事务
XA ROLLBACK 'test'; //回滚事务。
在这个标准下有多种具体的实现框架。ShardingSphere集成了Atomikos、Bitronix和Narayana三个框架。其中在ShardingProxy中默认只集成了Atomikos实现。
这其中,XA标准规范了事务XID的格式。有三个部分: gtrid [, bqual [, formatID ]] 其中
- gtrid 是一个全局事务标识符 global transaction identifier
- bqual 是一个分支限定符 branch qualifier 。如果没有提供,会使用默认值就是一个空字符串。
- formatID 是一个数字,用于标记gtrid和bqual值的格式,这是一个正整数,最小为0,默认值就是1。
但是使用XA事务时需要注意以下几点:
- XA事务无法自动提交
- XA事务效率非常低下,全局事务的状态都需要持久化。性能非常低下,通常耗时能达到本地事务的10倍。
- XA事务在提交前出现故障的话,很难将问题隔离开。
CosId与ShardingSphere
主要的主键生成策略
1、数据库策略
在单数据库场景下,主键可以很简单。可以把主键扔给数据库,让他自己生成主键。比如MySQL的自增主键。
优点很明显。应用层使用简单,都不用考虑主键问题了,因此不会有主键稳定性的问题。以现代数据库的设计,自增主键的性能通常也比较高。另外,也不存在并发问题。应用不管部署多少个服务,主键都不会冲突。
但是坏处也同样明显。数据库自增主键不利于扩展。而且按照之前的分析,这类主键的规律太过明显,安全性也不是很高。在内部系统中使用问题不大,但是暴露在互联网环境就非常危险了。另外,在分库分表场景下,依靠数据库自增生成主键也非常不灵活。例如两台数据库服务,虽然可以定制出 让第一台数据库生成奇数序列,第二台数据库生成偶数序列 的方式让主键不冲突,但是由于每个数据库并不知道整个数据库集群的工作情况,所以如果数据库集群要扩缩容,所有的主键就都需要重新调整。
2、应用单独生成
既然数据库不靠谱,那就由应用自己生成。这一类算法有很多,比如UUID、NANOID、SnowFlake雪花算法等。
与数据库自增方案相比,应用自己生成主键的优点就比较明显。简单实用,比如UUID,用JDK自带的工具生成就行,而SNOWFLAKE,按他的规则自行组合就行了。另外主键很容易进行扩展。应用可以根据自己的需求随意组合生成主键。
但是缺点也非常明显。首先,算法不能太复杂。太复杂的算法会消耗应用程序的计算资源和内存空间,在高并发场景下会给应用带来很大的负担。然后,并发问题很难处理。既要考虑单进程下的多线程并发安全问题,又要防止分布式场景下多进程之间的主键冲突问题,对主键生成算法的要求其实是比较高的。所以,这一类算法虽然看起来挺自由,但是可供选择的算法其实并不多。要自己设计一个即高效,又靠谱的出来,那就更难了。
并且,如果与某一些具体的数据库产品结合使用,那么可能还会有一些定制化的需求。比如,如果使用我们最熟悉的MySQL数据库,通常还会要求主键能够趋势递增。因为MySQL的InnoDB引擎底层使用B+树进行数据存储,趋势递增的主键可以最大限度减少B+树的页裂变。所以,像UUID、NANOID这一类无序的字符串型主键,相比就没有SNOWFLAKE雪花算法这类趋势递增的数字型主键性能高。
3、第三方服务统一生成
还一种典型的思路是借助第三方服务来生成主键。 比较典型的工具有Redis,Zookeeper,还有MongoDB。
- Redis
使用incr指令,就可以生成严格递增的数字序列。配合lua脚本,也比较容易防并发。
- Zookeeper
比较原生的方法是使用Zookeeper的序列化节点。Zookeeper在创建序列化节点时,会在节点名称后面增加一个严格递增的数字序列。
另一种方法,在apache提供的Zookeeper客户端Curator中,提供了DistributedAtomicInteger,DistributedAtomicLong等工具,可以用来生成分布式递增的ID。
- MongoDB
比较原生的方法是使用MongoDB的ObjectID。MongoDB中每插入一条记录,就会给这条记录分配一个objectid。
这些方案成本比较低,使用时也比较灵活。应用拿到这些ID后,还是可以自由发挥进行扩展的,因此也都是不错的主键生成工具。
但是他们的缺点也很明显。这些原生的方式大都不是为了分布式主键场景而设计的,所以,如果要保证高效以及稳定,在使用这些工具时,还是需要非常谨慎。
4、与第三方结合的segment策略
segment策略的基本思想就是应用依然从第三方服务中获取ID,但是不是每次获取一个ID,而是每次获取一段ID。然后在本地进行ID分发。等这一段ID分发完了,再去第三方服务中获取一段。
这个策略中有一个最大的问题,就是申请ID是需要消耗网络资源的,在申请资源期间,应用就无法保持高可用了。所以有一种解决方案就是双Buffer写入。
应用既然可以接收一段ID,那就可以再准备一个Buffer,接收另一段ID。当Buffer1的ID使用了10%后,就发起线程去请求ID,放到Buffer2中。等Buffer1中的ID用完了,应用就直接从Buffer2中分配ID。然后等Buffer2用到10%,再同样缓过来。通过双Buffer的交替使用,保证在应用申请ID期间,本地的JVM缓存中一直都是有ID可以分配的。
没错,这就是美团Leaf的完整方案。
他的好处比较明显。ID单调递增,在一定范围内,还可以保持严格递增。通过JVM本地进行号段缓存,性能也很高。
但是这种方案也有几个明显的不足之处。
1、强依赖于DB。其实你可以想象,DB中最为核心的就是max_id和step两个字段而已。这两个字段其实可以往其他存储迁移。想用那个就用哪个不是更方便?这个想法现在不需要自己动手了,CosID已经实现了。数据库、Redis、Zookeeper、MongoDB,想用哪个就用哪个。程序员又找到了一个偷懒的理由。
2、10%的阈值不太灵活。如果应用中的业务非常频繁,分配ID非常快,10%有可能不够。而如果业务非常慢,10%又有点浪费,因为申请过来的ID,如果应用一停机,就浪费掉了。所以,其实可以添加一个动态控制功能,根据分配ID的频率,灵活调整这个阈值,保持本地缓存内的ID数量基本稳定。并且,这也可以用来定制限流方案。
3、延长本地缓存。不管你用哪种服务来充当号段分配器,还是会有一个问题。如果号段分配器挂了,本地应用就只能通过本地缓存撑一段时间。这时,是不是可以考虑多缓存几个号段,延长一下支撑的时间呢?
CosId也想到了,直接将双Buffer升级成了SegmentChain。用一个链表的方式可以灵活缓存更多的号段。默认保留10个Segment,并且在后面分配ID的过程中,也尽量保证SegmentChain中的Segment个数不少于10个。这不就是为了保证本地缓存能够比较充足吗?
4、ID的安全性其实是不太高的。分配的ID在同一个号段内是连续的,之前分析过,这种规律过于明显的ID其实是不太安全的。在面向互联网使用的时候,还是需要自行进行一些打散的操作。比如下面会提到一种方法,将生成的主键作为雪花算法的工作机器位,再次计算生成主键。
以上这几类可以认为是比较基础的分布式主键生成工具。以这些方案为基础,就诞生了很多其他的玩法。
根据雪花算法扩展基因分片法
他的基础思想有点类似于雪花算法的序列部分。基础思想是在给用户分配userId时,就将用户名当中的某种序列信息插入到userId当中。从而保证userId和用户名可以按照某一种对应的规则分到同一个分片上。这样,在用户登录时,就可以根据用户名确定对应的用户信息只有可能分布在某一个数据分片当中。这样就只要去对应的分片上进行一次查询,就能查询到用户对应的信息。
整个基因法分片的方案这样设计:
1、用户注册时,先从主键生成器获取一个唯一的用户ID。这是原始ID。
2、按照上面示例的方式,从用户注册时输入的用户名中抽取分片基因。并将分片基因插入到原始ID中,生成一个新的用户ID。
3、根据新的用户ID,将用户数据按照 2或4或8 的数量进行分片存储,保存到数据库中。这样保证了根据用户名和新的用户ID都是可以拿到相同的分片结果的,也就是数据实际存储的分片。
4、用户登录时,根据用户输入的用户名,获取分片基因,并对数据分片数取模,这样就能获得这个用户名可能存在的用户分片。如果用户信息存在,就只可能保存在这一个分片里,不可能在其他分片。这样就只要到这一个用户分片上进行查询,就能获得用户的信息了。如果查不到,那就证明用户输入的用户名不存在,也不用去其他分片上确认了。
基因分片法总结:
从实现中也能看到,基因分片法还是有很多限制的
1、分片数量和基因位数强绑定。 比如基因片段长度如果设置为3,那么数据的分片数就只能是2或4或8。当然,这其实也有一个好处,那就是如果数据集群从4扩展到8,那么用户数据的迁移量是最少的。只要迁移一半数据。如果基因片段设置更长,也意味着更大的扩展空间。
2、基因分片法只能在主键中插入一个基因片段。如果还想要按照其他字段查询,就无法做到了。比如在登录场景,也有可能要根据用户的手机号码查询,这时,基因法就没有用了。这时,通常的解决方案就只有在插入用户信息的时候,单独维护一个从手机号码到所在分片的倒排索引。这个思想比较简单,但是具体实现时的麻烦会比较多。
基因分片法,其实一直以来会觉得有点过于偏理论,并没有一个框架来帮你实现一个现成的算法,所以很多人对他其实不太感冒。但是,结合雪花算法来看,是不是就是对后面的工作序列位做做手脚?雪花算法都弄好了,源码都有现成的可以抄了,基因分片法你就不想自己实现一下?
理解CosId
CosID虽然集成了很多种实现机制,但是主键生成模式也就三种。1、SnowFlake雪花算法。2、SegmentID号段模式。3、SegmentChainID号段链模式。其中后两种应该算是同一类模式,都是用segment模式,依靠第三方服务生成分布式主键。只不过SegmentID号段模式属于单Segment,而SegmentChainID号段链模式是扩展成为多Segment。