网站建设进度计划,公众号开发和小程序开发哪个简单,如何推广微信视频号,顺企网贵阳网站建设1.UUID
1.UUID (Universally Unique Identifier)#xff0c;通用唯一识别码。UUID是基于当前时间、计数器#xff08;counter#xff09;和硬件标识#xff08;通常为无线网卡的MAC地址#xff09;等数据计算生成的。UUID由以下几部分的组合#xff1a;
1.当前日期和时…1.UUID
1.UUID (Universally Unique Identifier)通用唯一识别码。UUID是基于当前时间、计数器counter和硬件标识通常为无线网卡的MAC地址等数据计算生成的。UUID由以下几部分的组合
1.当前日期和时间UUID的第一个部分与时间有关如果你在生成一个UUID之后过几秒又生成一个UUID则第一个部分不同其余相同。
2.时钟序列。
3.全局唯一的IEEE机器识别号如果有网卡从网卡MAC地址获得没有网卡以其他方式获得。UUID 是由一组32位数的16进制数字所构成以连字号分隔的五组来显示形式为 8-4-4-4-12总共有 36个字符即三十二个英数字母和四个连字号。例如
aefbbd3a-9cc5-4655-8363-a2a43e6e6c80
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx如果需求是只保证唯一性那么UUID也是可以使用的但是按照上面的分布式id的要求 UUID其实是不能做成分布式id的原因如下
1.首先分布式id一般都会作为主键但是安装mysql官方推荐主键要尽量越短越好UUID每一个都很长所以不是很推荐。
2.既然分布式id是主键然后主键是包含索引的然后mysql的索引是通过b树来实现的每一次新的UUID数据的插入为了查询的优化都会对索引底层的b树进行修改因为UUID数据是无序的所以每一次UUID数据的插入都会对主键生成的b树进行很大的修改这一点很不好。
3.信息不安全基于MAC地址生成UUID的算法可能会造成MAC地址泄露这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。2.自增ID
这种方式在单个数据库的场景中是可以这样做的但如果是在分库分表的环境下。直接利用单个数据库的自增肯定会出现问题。因为ID要唯一但是分表分库后只能保证一个表中的ID的唯一而不能保证整体的ID唯一。 上面的情况我们可以通过单独创建主键维护表来处理。
3.数据库多主模式
单点数据库方式存在明显的性能问题可以对数据库进行优化担心一个主节点挂掉没法使用可以选择做双主模式集群也就是两个MySQL实例都能单独生产自增的ID。
可以设置主键自增的步长从2开始 这种在并发量比较高的情况下如何保证扩展性其实会是一个问题。在高并发情况下无能为力。
4.号段模式
例如 (1,1000] 代表1000个ID具体的业务服务将本号段生成1~1000的自增ID并加载到内存。表结构如下
CREATE TABLE id_generator (
id int(10) NOT NULL,
max_id bigint(20) NOT NULL COMMENT 当前最大id,
step int(20) NOT NULL COMMENT 号段的布长,
biz_type int(20) NOT NULL COMMENT 业务类型,
version int(20) NOT NULL COMMENT 版本号,
PRIMARY KEY (id) )字段说明
biz_type 代表不同业务类型
max_id 当前最大的可用id
step 代表号段的长度
version 是一个乐观锁每次都更新version保证并发时数据的正确性等这批号段ID用完再次向数据库申请新号段对maxid字段做一次update操作update maxid max_id stepupdate成功则说明新号段获取成功新的号段范围是(maxid ,maxid step] 由于多业务端可能同时操作所以采用版本号version乐观锁方式更新这种分布式ID生成方式不强依赖于数据库不会频繁的访问数据库对数据库的压力小很多。但同样也会存在一些缺点比如服务器重启单点故障会造成ID不连续。
5.Redis
基于全局唯一ID的特性通过Redis的INCR命令来生成全局唯一ID。 同样使用Redis也有对应的缺点
1.ID 生成的持久化问题如果Redis宕机了怎么进行恢复
2.节点宕机问题针对故障问题我们可以通过Redis集群来处理比如我们有三个Redis的Master节点。可以初始化每台Redis的值分别是1,2,3然后分别把分布式ID的KEY用Hash Tags固定每一个master节点步长就是master节点的个数。各个Redis生成的ID为
节点A:1,4,7
节点B:2,5,8
节点C:3,6,9优点
1.不依赖于数据库灵活方便且性能优于数据库
2.数字ID有序对分页处理和排序都很友好
3.防止了Redis的单机故障缺点
1.如果没有Redis数据库需要安装配置增加复杂度
2.集群节点确定是3个后后面调整不是很友好public class RedisDistributedId {Autowiredprivate StringRedisTemplate redisTemplate;private static final long BEGIN_TIMESTAMP 1659312000l;/*** 生成分布式ID* 符号位 时间戳[31位] 自增序号【32位】* param item* return*/public long nextId(String item){// 1.生成时间戳LocalDateTime now LocalDateTime.now();// 格林威治时间差long nowSecond now.toEpochSecond(ZoneOffset.UTC);// 我们需要获取的 时间戳 信息long timestamp nowSecond - BEGIN_TIMESTAMP;// 2.生成序号 --》 从Redis中获取// 当前当前的日期String date now.format(DateTimeFormatter.ofPattern(yyyy:MM:dd));// 获取对应的自增的序号Long increment redisTemplate.opsForValue().increment(id: item : date);return timestamp 32 | increment;}
}
6.雪花算法
Snowflake雪花算法是有Twitter开源的分布式ID生成算法以划分命名空间的方式将64bit位分割成了多个部分每个部分都有具体的不同含义在Java中64Bit位的整数是Long类型所以在Java中Snowflake算法生成的ID就是long来存储的。具体如下 第一部分占用1bit,第一位为符号位不适用 第二部分41位的时间戳41bit位可以表示241个数每个数代表的是毫秒那么雪花算法的时间年限是(241)/(1000×60×60×24×365)69年 第三部分:10bit表示是机器数即 2^ 10 1024台机器通常不会部署这么多机器 第四部分:12bit位是自增序列可以表示2^124096个数一秒内可以生成4096个ID
public class SnowflakeIdWorker {/*** 开始时间截 (2020-11-03一旦确定不可更改否则时间被回调或者改变可能会造成id重复或冲突)*/private final long twepoch 1604374294980L;/*** 机器id所占的位数*/private final long workerIdBits 5L;/*** 数据标识id所占的位数*/private final long datacenterIdBits 5L;/*** 支持的最大机器id结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)*/private final long maxWorkerId -1L ^ (-1L workerIdBits);/*** 支持的最大数据标识id结果是31*/private final long maxDatacenterId -1L ^ (-1L datacenterIdBits);/*** 序列在id中占的位数*/private final long sequenceBits 12L;/*** 机器ID向左移12位*/private final long workerIdShift sequenceBits;/*** 数据标识id向左移17位(125)*/private final long datacenterIdShift sequenceBits workerIdBits;/*** 时间截向左移22位(5512)*/private final long timestampLeftShift sequenceBits workerIdBits datacenterIdBits;/*** 生成序列的掩码这里为4095 (0b1111111111110xfff4095)*/private final long sequenceMask -1L ^ (-1L sequenceBits);/*** 工作机器ID(0~31)*/private long workerId;/*** 数据中心ID(0~31)*/private long datacenterId;/*** 毫秒内序列(0~4095)*/private long sequence 0L;/*** 上次生成ID的时间截*/private long lastTimestamp -1L;//Constructors/*** 构造函数**/public SnowflakeIdWorker() {this.workerId 0L;this.datacenterId 0L;}/*** 构造函数** param workerId 工作ID (0~31)* param datacenterId 数据中心ID (0~31)*/public SnowflakeIdWorker(long workerId, long datacenterId) {if (workerId maxWorkerId || workerId 0) {throw new IllegalArgumentException(String.format(worker Id cant be greater than %d or less than 0, maxWorkerId));}if (datacenterId maxDatacenterId || datacenterId 0) {throw new IllegalArgumentException(String.format(datacenter Id cant be greater than %d or less than 0, maxDatacenterId));}this.workerId workerId;this.datacenterId datacenterId;}// Methods/*** 获得下一个ID (该方法是线程安全的)** return SnowflakeId*/public synchronized long nextId() {long timestamp timeGen();//如果当前时间小于上一次ID生成的时间戳说明系统时钟回退过这个时候应当抛出异常if (timestamp lastTimestamp) {throw new RuntimeException(String.format(Clock moved backwards. Refusing to generate id for %d milliseconds, lastTimestamp - timestamp));}//如果是同一时间生成的则进行毫秒内序列if (lastTimestamp timestamp) {sequence (sequence 1) sequenceMask;//毫秒内序列溢出if (sequence 0) {//阻塞到下一个毫秒,获得新的时间戳timestamp tilNextMillis(lastTimestamp);}}//时间戳改变毫秒内序列重置else {sequence 0L;}//上次生成ID的时间截lastTimestamp timestamp;//移位并通过或运算拼到一起组成64位的IDreturn ((timestamp - twepoch) timestampLeftShift) //| (datacenterId datacenterIdShift) //| (workerId workerIdShift) //| sequence;}/*** 阻塞到下一个毫秒直到获得新的时间戳** param lastTimestamp 上次生成ID的时间截* return 当前时间戳*/protected long tilNextMillis(long lastTimestamp) {long timestamp timeGen();while (timestamp lastTimestamp) {timestamp timeGen();}return timestamp;}/*** 返回以毫秒为单位的当前时间** return 当前时间(毫秒)*/protected long timeGen() {return System.currentTimeMillis();}/*** 随机id生成使用雪花算法** return*/public static String getSnowId() {SnowflakeIdWorker sf new SnowflakeIdWorker();String id String.valueOf(sf.nextId());return id;}//Test/*** 测试*/public static void main(String[] args) {SnowflakeIdWorker idWorker new SnowflakeIdWorker(0, 0);for (int i 0; i 1000; i) {long id idWorker.nextId();System.out.println(id);}}
}
实际生产环境中应该怎么来应用雪花算法来实现分布式ID。
7.百度(Uidgenerator)
1.源码地址https://github.com/baidu/uid-generator
2.中文文档地址https://github.com/baidu/uid-generator/blob/master/README.zh_cn.mdUidGenerator是百度开源的Java语言实现基于Snowflake算法的唯一ID生成器。它是分布式的并克服了雪花算法的并发限制。单个实例的QPS能超过6000000。需要的环境JDK8MySQL用于分配WorkerId。
百度的Uidgenerator对结构做了部分的调整具体如下
8.美团(Leaf)
由美团开发开源项目链接https://github.com/Meituan-Dianping/Leaf
Leaf同时支持号段模式和snowflake算法模式可以切换使用。ID号码是趋势递增的8byte的64位数字满足上述数据库存储的主键要求。
Leaf的snowflake模式依赖于ZooKeeper不同于原始snowflake算法也主要是在workId的生成上Leaf中workId是基于ZooKeeper的顺序Id来生成的每个应用在使用Leaf-snowflake时启动时都会都在Zookeeper中生成一个顺序Id相当于一台机器对应一个顺序节点也就是一个workId。
Leaf的号段模式是对直接用数据库自增ID充当分布式ID的一种优化减少对数据库的频率操作。相当于从数据库批量的获取自增ID每次从数据库取出一个号段范围例如 (1,1000] 代表1000个ID业务服务将号段在本地生成1~1000的自增ID并加载到内存.。
特性
1.全局唯一绝对不会出现重复的ID且ID整体趋势递增。
2.高可用服务完全基于分布式架构即使MySQL宕机也能容忍一段时间的数据库不可用。
3.高并发低延时在CentOS 4C8G的虚拟机上远程调用QPS可达5WTP99在1ms内。
4.接入简单直接通过公司RPC服务或者HTTP调用即可接入。Leaf采用双buffer的方式它的服务内部有两个号段缓存区segment。当前号段已消耗10%时还没能拿到下一个号段则会另启一个更新线程去更新下一个号段。
简而言之就是Leaf保证了总是会多缓存两个号段即便哪一时刻数据库挂了也会保证发号服务可以正常工作一段时间。
dependencygroupIdcom.sankuai.inf.leaf/groupIdartifactIdleaf-boot-starter/artifactIdversion1.0.1-RELEASE/version/dependencydependencygroupIdorg.apache.curator/groupIdartifactIdcurator-recipes/artifactIdversion2.6.0/versionexclusionsexclusiongroupIdlog4j/groupIdartifactIdlog4j/artifactId/exclusion/exclusions/dependency属性文件配置: leaf.namecom.boge.vipleaf.segment.enablefalseleaf.segment.urlleaf.segment.usernameleaf.segment.passwordleaf.snowflake.enabletrueleaf.snowflake.addressleaf.snowflake.portleaf.snowflake.address192.168.56.100leaf.snowflake.port2181放开注解 注意你还需要启动一个Zookeeper服务。 测试代码 SpringBootTestclass DistributedIdsVipApplicationTests {AutowiredRedisDistributedId redisDistributedId;Autowiredprivate SnowflakeService snowflakeService;Test
void contextLoads() {Result abc snowflakeService.getId(abc);System.out.println(abc.getId());System.out.println(Long.toBinaryString(abc.getId()));
}}9.滴滴(TinyID)
由滴滴开发开源项目链接https://github.com/didi/tinyid
Tinyid是在美团Leaf的leaf-segment算法基础上升级而来不仅支持了数据库多主节点模式还提供了tinyid-client客户端的接入方式使用起来更加方便。但和美团Leaf不同的是Tinyid只支持号段一种模式不支持雪花模式。Tinyid提供了两种调用方式一种基于Tinyid-server提供的http方式另一种Tinyid-client客户端方式。每个服务获取一个号段1000,2000]、2000,3000]、3000,4000]
特性
1.全局唯一的long型ID
2.趋势递增的id
3.提供 http 和 java-client 方式接入
4.支持批量获取ID
5.支持生成1,3,5,7,9...序列的ID
6.支持多个db的配置适用场景只关心ID是数字趋势递增的系统可以容忍ID不连续可以容忍ID的浪费 不适用场景像类似于订单ID的业务因生成的ID大部分是连续的容易被扫库、或者推算出订单量等信息