jsp网站开发案例,建设项目环保竣工验收备案网站,南京app软件开发,哪个网站有学做内帐的视频文章目录 系统对Id号的要求UUIDsnowflakeLeafLeaf-snowflakeLeaf-segmentMySQL自增主键segment双buffer 系统对Id号的要求
1、业务
1#xff09;全局唯一性#xff1a;不能出现重复的ID号#xff0c;既然是唯一标识#xff0c;这是最基本的要求
2#xff09;趋势递增全局唯一性不能出现重复的ID号既然是唯一标识这是最基本的要求
2趋势递增在MySQL InnoDB引擎中使用的是聚集索引由于多数RDBMS使用B-tree的数据结构来存储索引数据在主键的选择上面我们应该尽量使用有序的主键保证写入性能
3单调递增保证下一个ID一定大于上一个ID例如事务版本号、IM增量消息、排序等特殊需求
数据自增id
4信息安全如果ID是连续的 竞对在两天中午12点分别下单通过订单id号相减就能大致计算出公司一天的订单量 。所以在一些应用场景下会需要ID无规则
UUID或雪花算法
2、可靠性
平均延迟和TP999延迟都要尽可能低可用性5个9高QPS
UUID
1、定义
36个字符示例550e8400-e29b-41d4-a716-446655440000
public class IdUtil {/** 返回使用ThreadLocalRandom的UUID比默认的UUID性能更优*/public static UUID fastUUID() {ThreadLocalRandom random ThreadLocalRandom.current();return new UUID(random.nextLong(), random.nextLong());}
}2、缺点
太长不易于存储无序性如果作为数据库主键可能会引起数据页位置频繁变动严重影响性能信息不安全基于MAC地址生成UUID的算法可能会造成MAC地址泄露
snowflake
1、结构
Long型64位的整数如图1所示 41-bit的时间戳 可以表示1L41/(1000L360024*365)69年的时间 10-bit 数据中心ID和机器 5bit数据中心id5bit机器id一般使用ZK分配 12个自增序列号 在同一毫秒内生成2^12个唯一的ID
理论上snowflake方案的QPS约为409.6w/s可以保证在任何一个IDC、任何一台机器、在任意毫秒内、生成的ID都是不同的
2、问题
41-bit时间戳部分强依赖机器时钟如果机器上时钟回拨会导致发号重复
Leaf
Leaf-snowflake
1、适合场景生成的ID需要无规则
2、解决机器时钟回拨问题
1要求当前时间戳必须 机器创建时间
2同时
对比其余Leaf节点的系统时间来判断自身系统时间是否准确RPC请求得到所有节点的系统时间计算sum(time)/nodeSize 阈值则认为正确
阈值 5ms
因为理论上5ms内无法完全使用完成后12个自增序列号所以不会重复
否则直接报错自动摘除本身节点并报警
Leaf-segment
1、适合场景生成的ID单调递增
2、实现基于MySQL的自增主键
MySQL自增主键
1、获取ID方式
使用下列SQL读写MySQL得到ID号
begin;
REPLACE INTO Tickets64 (stub) VALUES (a);
SELECT LAST_INSERT_ID();
commit;Tickets64表stub列
实现方式类似
useGeneratedKeys“true“ keyProperty“id“int insert(XxxDO xxxDo)时先将DO内容写入dbinsert成功后再将JDBC自增主键值AUTO_INCREMENT回写到DO的id属性字段后续可能会从DO中获取此id值进行查询数据、编辑数据
2、存在问题
因为每次都是都需要写读MySQL才能获取ID值单台MySQL的读写性能是瓶颈
3、解决-集群 在分布式系统中多部署几台机器 每台机器设置不同的初始值且步长和机器数相等 比如有两台机器。设置步长step为2TicketServer1的初始值为11357911… TicketServer2的初始值为2246810… 假设部署N台机器步长需设置为N每台的初始值依次为0,1,2…N-1 则整个Leaf架构如图2
4、存在问题
数据库压力还是很大每次获取ID都得读写一次数据库只能靠堆机器来提高性能系统水平扩展比较困难定义好了步长和机器台数之后如果要添加机器不好做
5、解决- 批量分段segment获取 segment
1、实现如图3所示
1db表设计
----------------------------------------------------------------------------------
| Field | Type | Null | Key | Default | Extra
-----------------------------------------------------------------------------------
| biz_tag | varchar(128) | NO | PRI | |
| max_id | bigint(20) | NO | | 1 |
| step | int(11) | NO | | NULL |
| desc | varchar(256) | YES | | NULL |
| update_time | timestamp | NO | | CURRENT_TIMESTAMP | on update biz_tag用来区分业务外卖、支付max_id表示该biz_tag目前所被分配的ID号段的最大值
UPDATE table SET max_idmax_idstep WHERE biz_tagxxxstep表示每次分配的号段长度
2系统架构
3 ID值趋势递增
egtest_tag业务
Leaf Server 1从DB加载号段[11000]。Leaf Server 2从DB加载号段[10012000]。Leaf Server 3从DB加载号段[20013000]。
用户通过Round-robin的方式调用Leaf Server的各个服务通过CAS获取ID所以某一个Client获取到的ID序列
可能是110012001210022002……
也可能是12100120012002200334…
当某个Leaf Server号段用完之后下一次请求就会从DB中加载新的号段这样保证了每次加载的号段是递增
2、解决-读写性能 原来获取一个ID值都需要读写一次数据库 现在只需要把step设置得足够大比如1000。那么只有当1000个号被消耗完了之后才会去重新读写一次 读写数据库的频率从1减小到了1/step test_tag业务在第一台Leaf机器上是1~1000的号段当这个号段用完时 会去加载另一个长度为step1000的号段 假设另外两台号段都没有更新这个时候第一台机器新加载的号段就应该是3001~4000 同时数据库对应的biz_tag test_tag 这条数据的max_id会从3000被更新成4000
3、 解决-扩容操作
只需要对biz_tag分库分表
4、问题 在号段消耗完的时候进行取号段时还是会夯在更新数据库的I/O上 假如取DB的时候网络发生抖动或者DB发生慢查询就会导致整个系统的响应时间变慢
5、解决-双buffer 双buffer
1、解决-双buffer
当其中一个Buffer中的号段消费到某个点90%时就启异步线程的把下一个号段加载到内存中的另一个Buffer
2、 容灾
分库分表
3、异常兜底 使用redis原子自增等手段参考生成单据号异常兜底