什么是Leaf
leaf是叶子的意思
我们使用的Leaf是美团公司开源的一个分布式序列号(id)生成系统(id分发服务器)
为什么需要Leaf
背景:实际开发中常见的数据库部署是读写分离的,且专门进行数据更新(写)的有多个数据库节点
原因:它们同时新增数据可能产生相同的id,一旦生成相同的id,数据同步就会有问题,会产生id冲突,甚至引发异常
目的:我们为了在这种多数据库节点的环境下能够产生唯一id,可以使用Leaf来生成
Leaf的工作原理
Leaf底层支持通过"雪花算法"生成不同id
我们使用的是单纯的序列
要想使用,需要事先设置好leaf的起始值和缓存id数
举例,从1000开始缓存500
也就是从id1000~1499这些值,都会保存在Leaf的内存中,当有服务需要时,直接取出下一个值
取出过的值不会再次生成
leaf要想设置起始值和缓存数
需要给leaf创建一个指定格式的数据库表
运行过程中会从数据库表获取信息
快速上手
https://github.com/Meituan-Dianping/Leaf下载leaf项目
项目中的scripts是表信息:
DROP TABLE IF EXISTS `leaf_alloc`;
CREATE TABLE `leaf_alloc` (
`biz_tag` varchar(128) NOT NULL DEFAULT '',
`max_id` bigint(20) NOT NULL DEFAULT '1',
`step` int(11) NOT NULL,
`description` varchar(256) DEFAULT NULL,
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`biz_tag`)
) ENGINE=InnoDB;
biz_tag字段相当于分组,比如一个名为order的项目要从leaf中获取id,可以弄一个名为order的biz_tag字段值
max_id是指生成id的起始值,step是增加的最大值,因为是存在内存中的,不宜太大
比如max_id为5000,step为1000,则生成5000-5999的id存在内存供order项目提取;
如果取完,max_id会自动变成6000,step不变,又生成6000-6999的id;
leaf就是个提供id的服务器,每次访问的是leaf-server里的LeafController的/api/segment/get/{key}路径
其中的key就上上面说的如果是order项目取,key就是order;
leaf-server配置:
application.properties配置:
server.port=9090
spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.check-template-location=true
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=true
spring.freemarker.expose-session-attributes=true
spring.freemarker.request-context-attribute=request
leaf.properties配置:
leaf.segment.enable=true
leaf.jdbc.url=jdbc:mysql://localhost:3306/leafdb?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
leaf.jdbc.username=root
leaf.jdbc.password=root
leaf.snowflake.enable=false
调用leaf的order项目里面可以自定义个工具类
import org.springframework.web.client.RestTemplate;
public class IdGeneratorUtils {
private static final RestTemplate restTemplate = new RestTemplate();
public static Long getDistributeId(String key) {
String url = "http://127.0.0.1:9090/api/segment/get/" + key;
return restTemplate.getForObject(url, Long.class);
}
}