Что такое идентификатор снежинки?

Java задняя часть
Что такое идентификатор снежинки?

Статья была выбрана Github, добро пожаловать в Star:GitHub.com/Yehongqin/Лай…

Зачем использовать идентификатор снежинки

В предыдущих проектах два наиболее распространенных типа первичных ключей — это самоувеличивающийся идентификатор и UUID.Прежде чем сравнивать эти два типа идентификаторов, мы должны сначала понять вопрос, а именно, почему первичный ключ более эффективен, чем неупорядоченный запрос, потому что самоинкрементный Самая большая разница между Id и UUID заключается в порядке.

Все мы знаем, что когда мы определяем первичный ключ, база данных выберет первичный ключ таблицы в качестве кластеризованного индекса (B+Tree), а MySQL хранит данные в единицах страниц данных внизу.

То есть, если первичный ключ自增 id Если mysql заполнен страницей данных, он может напрямую обратиться к другой новой странице данных, а затем записать ее.Если страница данных заполнена, MySQL подаст заявку на новую страницу данных для хранения данных.. Если первичный ключUUID, чтобы убедиться, что индекс упорядочен, mysql необходимо помещать вставляемые данные каждый раз в соответствующую позицию.Это вызывает разбиение страниц, и этот процесс перемещения большого количества данных серьезно повлияет на эффективность вставки..

Одним словом, если порядок записи данных таблицы InnoDB может быть согласован с порядком листовых узлов индекса дерева B+, эффективность доступа в это время является самой высокой.

Но почему во многих случаях自增idкак первичный ключ?

  • Легко вызвать дублирование первичных ключей. Например, при импорте старых данных новые данные добавляются онлайн, в это время при импорте может возникнуть исключение дублирования первичного ключа. Чтобы избежать дублирования первичных ключей при импорте данных, выберите импорт старых данных после закрытия приложения и запуск приложения после завершения импорта. Очевидно, что это вызовет ненужные проблемы. И UUID как первичный ключ не нужно беспокоиться об этой ситуации.
  • Не способствует расширению базы данных. Когда используется самоувеличивающийся идентификатор, подбаза данных и подтаблица также будут иметь проблему дублирования первичных ключей. UUID не должны беспокоиться об этом.

Затем приходит проблема,自增idБеспокойство о дублировании первичных ключей,UUIDЗаказ не гарантируется. Существует ли идентификатор, который является одновременно упорядоченным и уникальным?

Конечно есть, т.雪花ID.

Что такое идентификатор снежинки

Snowflake — это открытый алгоритм генерации распределенного идентификатора Twitter, результатом которого является 64-битный идентификатор длинного типа, который уникален в глобальном масштабе и увеличивается по порядку.

  • Старший бит — это бит знака, потому что сгенерированный идентификатор всегда положителен, всегда равен 0 и недоступен.
  • 41-битный временной ряд имеет точность до миллисекунд, а 41-битная длина может использоваться в течение 69 лет. Другая важная функция временных битов заключается в том, что их можно сортировать по времени.
  • 10-битный идентификатор машины, 10-битная длина поддерживает развертывание до 1024 узлов.
  • 12-битный серийный номер счетчика, представляющий собой серию самоувеличивающихся идентификаторов, может поддерживать один и тот же узел для генерации нескольких серийных номеров идентификаторов за одну миллисекунду, а 12-битный серийный номер счетчика поддерживает каждый узел для генерации серийных номеров 4096 идентификаторов. числа в миллисекунду.

Есть и некоторые недостатки, то есть это сильно зависит от часов машины.Если часы на машине настроены назад, это может вызвать проблему дублирования первичных ключей.

Java реализует идентификатор снежинки

Ниже приведен код для реализации Snowflake ID в Java для справки.

public class SnowflakeIdWorker {
    /**
     * 开始时间:2020-01-01 00:00:00
     */
    private final long beginTs = 1577808000000L;

    private final long workerIdBits = 10;

    /**
     * 2^10 - 1 = 1023
     */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    private final long sequenceBits = 12;

    /**
     * 2^12 - 1 = 4095
     */
    private final long maxSequence = -1L ^ (-1L << sequenceBits);

    /**
     * 时间戳左移22位
     */
    private final long timestampLeftOffset = workerIdBits + sequenceBits;

    /**
     * 业务ID左移12位
     */
    private final long workerIdLeftOffset = sequenceBits;

    /**
     * 合并了机器ID和数据标示ID,统称业务ID,10位
     */
    private long workerId;

    /**
     * 毫秒内序列,12位,2^12 = 4096个数字
     */
    private long sequence = 0L;

    /**
     * 上一次生成的ID的时间戳,同一个worker中
     */
    private long lastTimestamp = -1L;

    public SnowflakeIdWorker(long workerId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("WorkerId必须大于或等于0且小于或等于%d", maxWorkerId));
        }

        this.workerId = workerId;
    }

    public synchronized long nextId() {
        long ts = System.currentTimeMillis();
        if (ts < lastTimestamp) {
            throw new RuntimeException(String.format("系统时钟回退了%d毫秒", (lastTimestamp - ts)));
        }

        // 同一时间内,则计算序列号
        if (ts == lastTimestamp) {
            // 序列号溢出
            if (++sequence > maxSequence) {
                ts = tilNextMillis(lastTimestamp);
                sequence = 0L;
            }
        } else {
            // 时间戳改变,重置序列号
            sequence = 0L;
        }

        lastTimestamp = ts;

        // 0 - 00000000 00000000 00000000 00000000 00000000 0 - 00000000 00 - 00000000 0000
        // 左移后,低位补0,进行按位或运算相当于二进制拼接
        // 本来高位还有个0<<63,0与任何数字按位或都是本身,所以写不写效果一样
        return (ts - beginTs) << timestampLeftOffset | workerId << workerIdLeftOffset | sequence;
    }

    /**
     * 阻塞到下一个毫秒
     *
     * @param lastTimestamp
     * @return
     */
    private long tilNextMillis(long lastTimestamp) {
        long ts = System.currentTimeMillis();
        while (ts <= lastTimestamp) {
            ts = System.currentTimeMillis();
        }

        return ts;
    }

    public static void main(String[] args) {
        SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(7);
        for (int i = 0; i < 10; i++) {
            long id = snowflakeIdWorker.nextId();
            System.out.println(id);
        }
    }
}

основной метод, результаты испытаний следующие:

184309536616640512
184309536616640513
184309536616640514
184309536616640515
184309536616640516
184309536616640517
184309536616640518
184309536616640519
184309536616640520
184309536616640521

Суммировать

В большинстве проектов разработки компании Snowflake ID является основной стратегией генерации идентификаторов.Помимо собственной реализации, на рынке также существует множество реализаций с открытым исходным кодом, таких как:

  • Мейтуан с открытым исходным кодомLeaf
  • Baidu с открытым исходным кодомUidGenerator

Если вам интересно, можете сами посмотреть, то эта статья написана здесь, спасибо за прочтение.

Статьи постоянно обновляются. Wechat ищет «энтузиастов технологии Java», а отправленные технические статьи получают как можно скорее после того, как обращают на них внимание. Статьи классифицируются и включаются в github:github.com/yehongzhi, вы всегда сможете найти то, что вас интересует

Ставьте лайки, если считаете это полезным, ваши лайки — самая большая мотивация для моего творчества.~

Я программист, который изо всех сил старается запомниться. Увидимся в следующий раз! ! !

Возможности ограничены, если есть какие-то ошибки или неуместности, пожалуйста, критикуйте и исправьте их, учитесь и общайтесь вместе!