PostgreSQL , 全局ID分配服务 , 序列 , UDF , plpgsql , 字典化
设计一个全局ID分配服务,要求:
1. 输入字符串 返回 ID
i. 已经存在系统中的字符串返回原ID 【严格要求】
ii. 新字符串分配新ID 递增分配
2. 输入ID 返回字符串
3. 每个Topic一个ID序列
4. Topic可以动态增删
5. 支持hsf调用
1. 支持 100万QPS以上的读访问
2. 支持批量 双向查询读操作,一次批量100的查询延时在1ms
3. 支持1万QPS左右的写操作
4. 支持批量写操作 ,一次批量100的写延时在10ms
1. 稳定可靠
2. 数据完全一致
3. 数据永远不丢
4. ID从0开始递增,尽量少空洞 【空洞占比少于 1%】
5. 灾难自恢复
实际上在之前,我有写过另一个任务分配系统的设计。
<a href="https://github.com/digoal/blog/blob/master/201712/20171216_01.md">《PostgreSQL 高并发任务分配系统 实践》</a>
另一方面,如果业务要求完全无缝的自增ID,我也有对应的文档提及。
<a href="https://github.com/digoal/blog/blob/master/201610/20161020_02.md">《PostgreSQL 无缝自增ID的实现 - by advisory lock》</a>
那么接下来按本文开头提到的几个要求进行设计。
1、设计一个UDF,自动生成与组ID一对一的序列,并返回序列的值。
2、创建测试表
3、创建一个UDF,当输入组ID和文本时,如果文本存在,返回已有的序列,如果文本不存在则分配一个唯一ID,并返回这个ID。
1、创建一个序列即可
3、创建一个UDF,当输入文本时,如果文本已存在,返回文本对应的序列,如果文本不存在,则分配一个唯一序列值,同时返回该值。
我们假设字典空间为40亿,则使用INT4。
如果字典空间超过40亿,则需要使用INT8。
1、创建序列,设置起始值为INT4的最小值
例子与性能,分配100条文本的ID约2毫秒
1、包括组ID
2、不包括组ID
3、包括组ID,且全局唯一
空洞来源,序列不可逆转的使用。即使事务失败,耗费掉的序列值也不可能被返回。
实测符合要求。
只要写满了,就只是返回SN,所以只要略微修改一下压测脚本
或者你可以换成SELECT
数据库本身没有做任何优化,同时使用了ECS虚拟机环境,还有一定的性能提升空间。或者可以按GID拆成多个库,实现100万QPS不是问题。
使用PostgreSQL的UDF,序列等功能,可以实现本文开头要求的“全局ID分配服务”的设计。
同时本例用到的PG实例为ECS虚拟机实例,读性能相比物理机要差一倍左右,单机实现100万的读,在物理机下面是没有问题的。
如果考虑将来的扩展性,可以将GID分配到不同的实例上,实现横向扩展,做到单个PG实例100万,多个实例100万*N的读TPS。
<a href="https://github.com/digoal/blog/blob/master/201703/20170315_01.md">《PostgreSQL 单机3.9万亿/天(计数器、序列、自增)》</a>
<a href="https://github.com/digoal/blog/blob/master/201301/20130122_01.md">《PostgreSQL sharding有序UUID最佳实践 - serial global uuid stored in 64bit int8》</a>
<a href="https://github.com/digoal/blog/blob/master/201210/20121024_01.md">《PostgreSQL 优化CASE - 无序UUID性能问题诊断》</a>