手机如何创建网站,wordpress 页面 文章 区别,公司网络维护服务方案,网站建设 html51.为什么要生成唯一ID#xff1f; 数据唯一性#xff1a;每个记录都需要有一个独一无二的标识符来确保数据的唯一性。这可以避免重复的数据行#xff0c;并有助于准确地查询、更新或删除特定的记录。 数据完整性#xff1a;通过使用唯一ID#xff0c;可以保证数据库中的数…1.为什么要生成唯一ID 数据唯一性每个记录都需要有一个独一无二的标识符来确保数据的唯一性。这可以避免重复的数据行并有助于准确地查询、更新或删除特定的记录。 数据完整性通过使用唯一ID可以保证数据库中的数据完整性。例如在关系数据库中外键通常引用主表的唯一ID以建立和维护表之间的关联。 高效查询唯一ID通常是索引的一部分这意味着使用它们进行查询可以非常快速和高效。这对于提高应用程序性能特别重要尤其是在处理大量数据时。 简化逻辑处理拥有一个唯一的标识符可以简化很多业务逻辑的处理。例如当需要引用或共享特定数据项时唯一ID提供了一个简单而直接的方法。 分布式系统支持在分布式系统或微服务架构中唯一ID特别是全局唯一ID是必不可少的。它们允许不同的服务能够独立生成不冲突的ID从而简化了跨服务的数据整合和通信问题。 易于扩展和维护随着系统的发展和需求的变化唯一ID使得添加新功能或者修改现有功能变得更加容易。例如重构数据库结构时唯一ID可以帮助更平滑地迁移数据。 2.生成唯一ID的基本要求 唯一性这是最基本的要求即生成的ID在整个系统中必须是独一无二的。特别是在分布式系统中需要确保不同节点生成的ID也不会发生冲突。 全局唯一性如果适用在某些场景下如分布式系统或微服务架构中可能需要ID不仅在同一数据库或系统内唯一而且在所有相关的系统和数据库中也保持唯一。 不可预测性为了安全考虑尤其是在涉及用户敏感信息或重要业务逻辑时ID应该是难以预测的。这可以防止恶意猜测或其他安全问题。 顺序性如果需要有些应用可能需要ID有一定的顺序性例如按时间顺序排列以便于排序、分页等操作。但需要注意的是严格的顺序性可能会对系统的扩展性和性能造成影响。 高效性生成ID的过程应该尽可能快速且消耗资源少以避免成为系统性能瓶颈。 长度适中ID的长度应该适中既能保证足够的空间来确保唯一性又不至于过长导致存储和传输效率降低。 兼容性生成的ID应与现有的系统和协议兼容不会因为格式或编码问题而引起错误。 可扩展性随着系统的发展和规模的增长生成ID的方法应能方便地进行扩展以适应更高的需求。 3.常见生成方法
UUID通用唯一识别码 基于时间的UUIDVersion 1结合时间戳和MAC地址生成。 基于随机数的UUIDVersion 4使用随机数生成。 基于命名空间的UUIDVersion 3 和 5基于命名空间和名称的哈希值生成。 优点 全局唯一性UUID的设计保证了在全球范围内的唯一性适用于分布式系统。 无需中央协调无需依赖数据库或其他中央服务生成ID减少了系统复杂性。 生成速度快基于随机数的UUID生成速度非常快适用于高并发场景。 缺点 长度较长UUID长度为128位通常表示为36个字符包括连字符这在存储和传输时占用较多空间。例如在数据库中存储UUID会比存储整数类型占用更多的空间。 无序性UUID通常是随机生成的缺乏时间上的顺序性。这在数据库索引中可能导致性能问题因为插入操作可能需要在B树索引中频繁分裂节点。 可读性差UUID对人类不友好难以记忆和识别不利于在用户界面或日志中直接使用。 缺乏业务相关性UUID不包含任何业务信息如时间戳或区域信息难以用于业务分析和追踪。 适用场景 分布式系统在多节点、多数据中心的环境中UUID是生成唯一标识符的理想选择。无需排序的场景如果不需要按时间或其他顺序对ID进行排序UUID是一个不错的选择。高并发环境UUID的生成速度非常快适用于需要快速生成唯一标识符的高并发场景。 数据库自增ID 数据库自增ID是一种常见的生成唯一标识符的方法通过在数据库表中设置自增字段如AUTO_INCREMENT每次插入新记录时数据库会自动为该字段生成一个唯一的、递增的整数。 优点 简单易用实现简单只需在数据库表中设置自增字段无需额外的代码或配置。 有序性自增ID是有序的有利于数据库索引的性能特别是在使用B树索引时。 节省存储空间整数类型的ID通常比UUID占用更少的存储空间。 可读性较好整数ID对人类相对友好易于识别和记忆。 缺点 单点瓶颈在分布式数据库环境中自增ID难以保证全局唯一性通常需要依赖单个数据库实例来生成ID这会成为系统的单点瓶颈影响性能和可扩展性。 难以水平扩展如果系统需要水平扩展到多个数据库实例自增ID的生成会成为问题因为每个实例都会生成自己的ID序列导致ID冲突。 依赖数据库生成ID依赖于数据库这在数据库故障或高负载时可能成为问题。 缺乏业务相关性自增ID不包含任何业务信息如时间戳或区域信息难以用于业务分析和追踪。 适用场景 单体应用在单体应用中数据库自增ID是一个简单且有效的方法。无需分布式唯一性的场景如果系统不需要在多个数据库实例或多个数据中心中保持全局唯一性自增ID是一个不错的选择。对ID有序性有要求的场景如果需要按时间或其他顺序对ID进行排序自增ID的有序性是一个优势。 4.雪花算法的实现过程如下 获取当前时间戳精确到毫秒级别。 根据给定的数据中心ID和机器ID生成一个10位的二进制数。 将时间戳左移22位将数据中心ID左移17位将机器ID左移12位然后使用位或操作符将它们组合成一个64位的二进制数。 如果在同一毫秒内生成了多个ID使用序列号来区分它们序列号从0开始递增最多可以生成4096个序列号。 优点 全局唯一性在分布式系统中雪花算法可以确保生成的ID全局唯一。 有序性生成的ID按照时间戳有序递增便于数据管理和查询。 高并发每毫秒可以生成4096个ID适合高并发场景。 缺点 依赖服务器时间如果服务器时间回拨可能会导致生成重复的ID。可以通过记录最后一个生成ID的时间戳来解决这个问题。 序列号浪费在分库分表时如果序列号一直从0开始可能会导致数据倾斜和不均匀分布。 适用场景 分布式系统如分布式数据库、分布式锁等需要全局唯一且有序的ID。 高并发场景如订单号生成、用户ID生成等 实现代码
public class SnowflakeIdGenerator {// 开始时间戳 (2023-01-01)private final long twepoch 1672502400000L;// 机器ID所占的位数private final long workerIdBits 5L;// 数据标识ID所占的位数private final long datacenterIdBits 5L;// 支持的最大机器ID结果是31 (这个值在位运算中不会溢出)private final long maxWorkerId -1L ^ (-1L workerIdBits);// 支持的最大数据标识ID结果是31private final long maxDatacenterId -1L ^ (-1L datacenterIdBits);// 序列号在ID中所占的位数private final long sequenceBits 12L;// 机器ID需要左移的位数12private final long workerIdShift sequenceBits;// 数据标识ID需要左移的位数17private final long datacenterIdShift sequenceBits workerIdBits;// 时间戳需要左移的位数22private final long timestampLeftShift sequenceBits workerIdBits datacenterIdBits;// 生成序列的掩码这里为4095 (0b1111111111114095)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;public SnowflakeIdGenerator(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;}/*** 产生下一个ID*/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;}protected long tilNextMillis(long lastTimestamp) {long timestamp timeGen();while (timestamp lastTimestamp) {timestamp timeGen();}return timestamp;}protected long timeGen() {return System.currentTimeMillis();}
}