您好,登錄后才能下訂單哦!
這篇文章主要介紹“redis分布式ID解決方法有哪些”的相關(guān)知識(shí),小編通過實(shí)際案例向大家展示操作過程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“redis分布式ID解決方法有哪些”文章能幫助大家解決問題。
在分布式系統(tǒng)中,生成全局唯一ID是非常重要的,因?yàn)樵诜植际较到y(tǒng)中,多個(gè)節(jié)點(diǎn)同時(shí)生成ID可能會(huì)導(dǎo)致ID沖突。
下面介紹幾種常用的分布式ID解決方案。
UUID(通用唯一標(biāo)識(shí)符)是由128位數(shù)字組成的標(biāo)識(shí)符,它可以保證在全球范圍內(nèi)的唯一性,因?yàn)槠渖伤惴ɑ跁r(shí)間戳、節(jié)點(diǎn)ID等因素。UUID可以使用Java自帶的UUID類來生成,如下所示:
javaCopy code import java.util.UUID; public class UuidGenerator { public static void main(String[] args) { UUID uuid = UUID.randomUUID(); System.out.println(uuid.toString()); } }
UUID的優(yōu)點(diǎn)是簡(jiǎn)單易用,無需額外的配置和管理,可以直接使用Java自帶的UUID類生成。但是UUID長(zhǎng)度較長(zhǎng)(128位),不太適合作為數(shù)據(jù)庫表的主鍵,且不易于排序和索引。
Snowflake是Twitter開源的一種分布式ID生成算法,它可以生成64位的唯一ID,其中包含了時(shí)間戳、數(shù)據(jù)中心ID和機(jī)器ID等信息。Snowflake算法的Java代碼如下所示:
javaCopy code public class SnowflakeGenerator { private final static long START_STMP = 1480166465631L; private final static long SEQUENCE_BIT = 12; private final static long MACHINE_BIT = 5; private final static long DATACENTER_BIT = 5; private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT); private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); private final static long MACHINE_LEFT = SEQUENCE_BIT; private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT; private long datacenterId; private long machineId; private long sequence = 0L; private long lastStmp = -1L; public SnowflakeGenerator(long datacenterId, long machineId) { if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) { throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"); } if (machineId > MAX_MACHINE_NUM || machineId < 0) { throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); } this.datacenterId = datacenterId; this.machineId = machineId; } public synchronized long nextId() { long currStmp = getNewstmp(); if (currStmp < lastStmp) { throw new RuntimeException("Clock moved backwards. Refusing to generate id"); } if (currStmp == lastStmp) { sequence = (sequence + 1) & MAX_SEQUENCE; if (sequence == 0L) { currStmp = getNextMill(); } } else { sequence = 0L; } lastStmp = currStmp; return (currStmp - START_STMP) << TIMESTMP_LEFT | datacenterId << DATACENTER_LEFT | machineId << MACHINE_LEFT | sequence; } private long getNextMill() { long mill = getNewstmp(); while (mill <= lastStmp) { mill = getNewstmp(); } return mill; } private long getNewstmp() { return System.currentTimeMillis(); } }
Snowflake算法的優(yōu)點(diǎn)是生成ID的性能高,且ID長(zhǎng)度較短(64位),可以作為數(shù)據(jù)庫表的主鍵,且便于排序和索引。但是需要注意,如果集群中的節(jié)點(diǎn)數(shù)超過了機(jī)器ID所占的位數(shù),或者集群規(guī)模很大,時(shí)間戳位數(shù)不夠用,那么就需要考慮其他的分布式ID生成算法。
Leaf是美團(tuán)點(diǎn)評(píng)開源的一種分布式ID生成算法,它可以生成全局唯一的64位ID。Leaf算法的Java代碼如下所示:
javaCopy code public class LeafGenerator { private static final Logger logger = LoggerFactory.getLogger(LeafGenerator.class); private static final String WORKER_ID_KEY = "leaf.worker.id"; private static final String PORT_KEY = "leaf.port"; private static final int DEFAULT_PORT = 8080; private static final int DEFAULT_WORKER_ID = 0; private static final int WORKER_ID_BITS = 10; private static final int SEQUENCE_BITS = 12; private static final int MAX_WORKER_ID = (1 << WORKER_ID_BITS) - 1; private static final int MAX_SEQUENCE = (1 << SEQUENCE_BITS) - 1; private static final long EPOCH = 1514736000000L; private final SnowflakeIdWorker idWorker; public LeafGenerator() { int workerId = SystemPropertyUtil.getInt(WORKER_ID_KEY, DEFAULT_WORKER_ID); int port = SystemPropertyUtil.getInt(PORT_KEY, DEFAULT_PORT); this.idWorker = new SnowflakeIdWorker(workerId, port); logger.info("Initialized LeafGenerator with workerId={}, port={}", workerId, port); } public long nextId() { return idWorker.nextId(); } private static class SnowflakeIdWorker { private final long workerId; private final long port; private long sequence = 0L; private long lastTimestamp = -1L; SnowflakeIdWorker(long workerId, long port) { if (workerId < 0 || workerId > MAX_WORKER_ID) { throw new IllegalArgumentException(String.format("workerId must be between %d and %d", 0, MAX_WORKER_ID)); } this.workerId = workerId; this.port = port; } synchronized long nextId() { long timestamp = System.currentTimeMillis(); if (timestamp < lastTimestamp) { throw new RuntimeException("Clock moved backwards. Refusing to generate id"); } if (timestamp == lastTimestamp) { sequence = (sequence + 1) & MAX_SEQUENCE; if (sequence == 0L) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; return ((timestamp - EPOCH) << (WORKER_ID_BITS + SEQUENCE_BITS)) | (workerId << SEQUENCE_BITS) | sequence; } private long tilNextMillis(long lastTimestamp) { long timestamp = System.currentTimeMillis(); while (timestamp <= lastTimestamp) { timestamp = System.currentTimeMillis(); } return timestamp; } } }
Leaf算法的特點(diǎn)是生成ID的速度比Snowflake算法略慢,但是可以支持更多的Worker節(jié)點(diǎn)。Leaf算法生成的ID由三部分組成,分別是時(shí)間戳、Worker ID和序列號(hào),其中時(shí)間戳占用42位、Worker ID占用10位、序列號(hào)占用12位,總共64位。
以上是常見的分布式ID生成算法,當(dāng)然還有其他的一些方案,如:MongoDB ID、UUID、Twitter Snowflake等。不同的方案適用于不同的業(yè)務(wù)場(chǎng)景,具體實(shí)現(xiàn)細(xì)節(jié)和性能表現(xiàn)也有所不同,需要根據(jù)實(shí)際情況選擇合適的方案。
除了上述介紹的分布式ID生成算法,還有一些新的分布式ID生成方案不斷涌現(xiàn),例如Flicker的分布式ID生成算法,它使用了類似于Snowflake的思想,但是采用了不同的位數(shù)分配方式,相比Snowflake更加靈活,并且可以根據(jù)需要?jiǎng)討B(tài)調(diào)整每個(gè)部分占用的位數(shù)。此外,F(xiàn)acebook還推出了ID Generation Service (IGS)方案,該方案將ID的生成和存儲(chǔ)分離,提供了更加靈活和可擴(kuò)展的方案,但是需要進(jìn)行更加復(fù)雜的架構(gòu)設(shè)計(jì)和實(shí)現(xiàn)。
針對(duì)不同的業(yè)務(wù)需求,可以設(shè)計(jì)多套分布式ID生成方案。下面是我個(gè)人的一些建議:
基于數(shù)據(jù)庫自增ID生成:使用數(shù)據(jù)庫自增ID作為全局唯一ID,可以很好的保證ID的唯一性,并且實(shí)現(xiàn)簡(jiǎn)單,但是并發(fā)量較高時(shí)可能會(huì)導(dǎo)致性能瓶頸。因此,在高并發(fā)場(chǎng)景下不建議使用。
基于UUID生成:使用UUID作為全局唯一ID,可以很好地保證ID的唯一性,但是ID長(zhǎng)度較長(zhǎng)(128位),不便于存儲(chǔ)和傳輸,并且存在重復(fù)ID的概率非常小但不為0。因此,建議在分布式系統(tǒng)中使用時(shí)要考慮ID的長(zhǎng)度和存儲(chǔ)傳輸?shù)某杀尽?/p>
基于Redis生成:使用Redis的原子性操作,可以保證ID的唯一性,并且生成ID的速度非??欤梢赃m用于高并發(fā)場(chǎng)景。但是需要注意,如果Redis宕機(jī)或者性能不足,可能會(huì)影響ID的生成效率和可用性。
基于ZooKeeper生成:使用ZooKeeper的序列號(hào)生成器,可以保證ID的唯一性,并且實(shí)現(xiàn)較為簡(jiǎn)單,但是需要引入額外的依賴和資源,并且可能會(huì)存在性能瓶頸。
選擇適合自己業(yè)務(wù)場(chǎng)景的分布式ID生成方案,需要綜合考慮ID的唯一性、生成速度、長(zhǎng)度、存儲(chǔ)成本、可擴(kuò)展性、可用性等多個(gè)因素。同時(shí)需要注意,不同方案的實(shí)現(xiàn)細(xì)節(jié)和性能表現(xiàn)也有所不同,需要根據(jù)實(shí)際情況進(jìn)行權(quán)衡和選擇。
下面給出每種方案的詳細(xì)代碼demo:
javaCopy code public class IdGenerator { private static final String JDBC_URL = "jdbc:mysql://localhost:3306/test"; private static final String JDBC_USER = "root"; private static final String JDBC_PASSWORD = "password"; public long generateId() { Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD); pstmt = conn.prepareStatement("INSERT INTO id_generator (stub) VALUES (null)", Statement.RETURN_GENERATED_KEYS); pstmt.executeUpdate(); rs = pstmt.getGeneratedKeys(); if (rs.next()) { return rs.getLong(1); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (rs != null) { rs.close(); } if (pstmt != null) { pstmt.close(); } if (conn != null) { conn.close(); } } catch (Exception e) { e.printStackTrace(); } } return 0L; } }
javaCopy code import java.util.UUID; public class IdGenerator { public String generateId() { return UUID.randomUUID().toString().replace("-", ""); } }
javaCopy code import redis.clients.jedis.Jedis; public class IdGenerator { private static final String REDIS_HOST = "localhost"; private static final int REDIS_PORT = 6379; private static final String REDIS_PASSWORD = "password"; private static final int ID_GENERATOR_EXPIRE_SECONDS = 3600; private static final String ID_GENERATOR_KEY = "id_generator"; public long generateId() { Jedis jedis = null; try { jedis = new Jedis(REDIS_HOST, REDIS_PORT); jedis.auth(REDIS_PASSWORD); long id = jedis.incr(ID_GENERATOR_KEY); jedis.expire(ID_GENERATOR_KEY, ID_GENERATOR_EXPIRE_SECONDS); return id; } catch (Exception e) { e.printStackTrace(); } finally { if (jedis != null) { jedis.close(); } } return 0L; } }
javaCopy code import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; public class IdGenerator implements Watcher { private static final String ZK_HOST = "localhost"; private static final int ZK_PORT = 2181; private static final int SESSION_TIMEOUT = 5000; private static final String ID_GENERATOR_NODE = "/id_generator"; private static final int ID_GENERATOR_EXPIRE_SECONDS = 3600; private long workerId = 0; public IdGenerator() { try { ZooKeeper zk = new ZooKeeper(ZK_HOST + ":" + ZK_PORT, SESSION_TIMEOUT, this); CountDownLatch latch = new CountDownLatch(1); latch.await(); if (zk.exists(ID_GENERATOR_NODE, false) == null) { zk.create(ID_GENERATOR_NODE, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } workerId = zk.getChildren(ID_GENERATOR_NODE, false).size(); zk.create(ID_GENERATOR_NODE + "/worker_" + workerId, null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); } catch (Exception e) { e.printStackTrace(); } } public long generateId() { ZooKeeper zk = null; try { zk = new ZooKeeper(ZK_HOST + ":" + ZK_PORT, SESSION_TIMEOUT, null); CountDownLatch latch = new CountDownLatch(1); latch.await(); zk.create(ID_GENERATOR_NODE + "/id_", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, (rc, path, ctx, name) -> {}, null); byte[] data = zk.getData(ID_GENERATOR_NODE + "/worker_" + workerId, false, null); long id = Long.parseLong(new String(data)) * 10000 + zk.getChildren(ID_GENERATOR_NODE, false).size(); return id; } catch (Exception e) { e.printStackTrace(); } finally { if (zk != null) { try { zk.close(); } catch (Exception e) { e.printStackTrace(); } } } return 0L; } @Override public void process(WatchedEvent event) { if (event.getState() == Event.KeeperState.SyncConnected) { System.out.println("Connected to ZooKeeper"); CountDownLatch latch = new CountDownLatch(1); latch.countDown(); } } }
注意,這里使用了ZooKeeper的臨時(shí)節(jié)點(diǎn)來協(xié)調(diào)各個(gè)工作節(jié)點(diǎn),如果一個(gè)工作節(jié)點(diǎn)掛掉了,它的臨時(shí)節(jié)點(diǎn)也會(huì)被刪除,這樣可以保證每個(gè)工作節(jié)點(diǎn)獲得的ID是唯一的。
關(guān)于“redis分布式ID解決方法有哪些”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。