在分布式系统中经常会使用到生成全局唯一不重复ID的情况。本篇主要介绍生成的一些方法。

    常见的一些方式:

    1、通过DB做全局自增操作
    优点:简单、高效
    缺点:大并发、分布式情况下性能比较低

    有些同学可能会说分库、分表的策略去降低DB的瓶颈,单要做到全局不重复需要提前按照一定的区域进行划分。例如:120000 等等。但这个灵活度比较低。

    针对一些并发比较低的情况也可以使用类似这种方式。但大并发时不建议使用,DB很容易成为瓶颈。

    2、获取当前时间纳秒或毫秒数
    这种方式需要考虑的是在分布式集群中如果保证唯一性。

    3、类似UUID的生成方式
    生成的串比较大


    综合上述情况我们需要一种在高并发、分布式系统中提供高效生成不重复唯一的一个ID,但要求生成的结果要小
    方法1:

    1. 方法1
    2. private static long INFOID_FLAG = 1260000000000L;
    3. protected static int SERVER_ID = 1;
    4. public synchronized long nextId() throws Exception {
    5. if(SERVER_ID <= 0)
    6. throw new Exception("server id is error,please check config file!");
    7. long infoid = System.currentTimeMillis() - INFOID_FLAG;
    8. infoid=(infoid<<7)| SERVER_ID;
    9. Thread.sleep(1);
    10. return infoid;
    11. }

    说明:
    SERVER_ID为不同的服务器使用的不同server ID,如果不同的机器使用相同的server ID有可能会生成重复的全局ID

    简单的应用在一定的并发情况下使用这种方式已经足够了,简单、高效。但是每秒生成的ID是有限的,因为Thread.sleep(1)会无形中带来一些时间的消耗。

    方法2:

    1. /**
    2. * 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加))
    3. * @author Polim
    4. */
    5. class IdWorker {
    6. private final static long twepoch = 1288834974657L;
    7. // 机器标识位数
    8. private final static long workerIdBits = 5L;
    9. // 数据中心标识位数
    10. private final static long datacenterIdBits = 5L;
    11. // 机器ID最大值
    12. private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
    13. // 数据中心ID最大值
    14. private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    15. // 毫秒内自增位
    16. private final static long sequenceBits = 12L;
    17. // 机器ID偏左移12位
    18. private final static long workerIdShift = sequenceBits;
    19. // 数据中心ID左移17位
    20. private final static long datacenterIdShift = sequenceBits + workerIdBits;
    21. // 时间毫秒左移22位
    22. private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    23. private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
    24. private static long lastTimestamp = -1L;
    25. private long sequence = 0L;
    26. private final long workerId;
    27. private final long datacenterId;
    28. public IdWorker(long workerId, long datacenterId) {
    29. if (workerId > maxWorkerId || workerId < 0) {
    30. throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0");
    31. }
    32. if (datacenterId > maxDatacenterId || datacenterId < 0) {
    33. throw new IllegalArgumentException("datacenter Id can't be greater than %d or less than 0");
    34. }
    35. this.workerId = workerId;
    36. this.datacenterId = datacenterId;
    37. }
    38. public synchronized long nextId() {
    39. long timestamp = timeGen();
    40. if (timestamp < lastTimestamp) {
    41. try {
    42. throw new Exception("Clock moved backwards. Refusing to generate id for "+ (lastTimestamp - timestamp) + " milliseconds");
    43. } catch (Exception e) {
    44. e.printStackTrace();
    45. }
    46. }
    47. if (lastTimestamp == timestamp) {
    48. // 当前毫秒内,则+1
    49. sequence = (sequence + 1) & sequenceMask;
    50. if (sequence == 0) {
    51. // 当前毫秒内计数满了,则等待下一秒
    52. timestamp = tilNextMillis(lastTimestamp);
    53. }
    54. } else {
    55. sequence = 0;
    56. }
    57. lastTimestamp = timestamp;
    58. // ID偏移组合生成最终的ID,并返回ID
    59. long nextId = ((timestamp - twepoch) << timestampLeftShift)
    60. | (datacenterId << datacenterIdShift)
    61. | (workerId << workerIdShift) | sequence;
    62. return nextId;
    63. }
    64. private long tilNextMillis(final long lastTimestamp) {
    65. long timestamp = this.timeGen();
    66. while (timestamp <= lastTimestamp) {
    67. timestamp = this.timeGen();
    68. }
    69. return timestamp;
    70. }
    71. private long timeGen() {
    72. return System.currentTimeMillis();
    73. }
    74. }

    这种方式是一种比较高效的方式。也是twitter使用的一种方式。
    测试类:

    1. import java.util.concurrent.BrokenBarrierException;
    2. import java.util.concurrent.CountDownLatch;
    3. import java.util.concurrent.CyclicBarrier;
    4. import java.util.concurrent.TimeUnit;
    5. public class IdWorkerTest {
    6. public static void main(String []args){
    7. IdWorkerTest test = new IdWorkerTest();
    8. test.test2();
    9. }
    10. public void test2(){
    11. final IdWorker w = new IdWorker(1,2);
    12. final CyclicBarrier cdl = new CyclicBarrier(100);
    13. for(int i = 0; i < 100; i++){
    14. new Thread(new Runnable() {
    15. @Override
    16. public void run() {
    17. try {
    18. cdl.await();
    19. } catch (InterruptedException e) {
    20. e.printStackTrace();
    21. } catch (BrokenBarrierException e) {
    22. e.printStackTrace();
    23. }
    24. System.out.println(w.nextId());}
    25. }).start();
    26. }
    27. try {
    28. TimeUnit.SECONDS.sleep(5);
    29. } catch (InterruptedException e) {
    30. e.printStackTrace();
    31. }
    32. }
    33. }