Как эффективно создать упорядоченную последовательность в Spanner?
Google Spanner рекомендует не использовать такие вещи, как отметки времени или последовательные числа в качестве начальной части первичного ключа или индекса, что имеет смысл на основе архитектуры. Однако для моих требований мне нужен какой-то способ обеспечения строгого "только добавления" порядка строк.
Я использую Spanner для моделирования событий (как в источнике событий). Каждое событие будет иметь категорию, идентификатор потока, идентифицирующий последовательность, в которой события должны быть строго упорядочены по отношению друг к другу, и несколько полей полезной нагрузки - с этого момента я буду игнорировать фактическую полезную нагрузку.
Наивно это было бы смоделировано как:
| Category | STRING |
| Stream Id | STRING |
| Sequence Nr | INT64 |
(С первичным ключом, состоящим из Category, Stream Id, Sequence Nr.) Это обеспечит строгий порядок событий для одного потока. Теперь, когда с некоторыми категориями связано много событий, и у лучших советов Spanner есть разница в старших битах, было бы лучше перевернуть это. Каждый "поток" будет содержать достаточно небольшое количество событий (тысячи, а не миллионов) и будет считываться вместе, чтобы облегчить лучшее распределение данных и обеспечить локальность для событий, принадлежащих одному потоку:
| Stream Id | STRING |
| Category | STRING |
| Sequence Nr | INT64 |
Однако, поскольку я хотел бы иметь возможность добавлять события без необходимости считывать текущее состояние, чтобы узнать текущий порядковый номер, я бы предпочел использовать метку времени.
| Aggregate Id | STRING | |
| Category | STRING | |
| Timestamp | TIMESTAMP | allow_commit_timestamp |
В Spanner встроена временная метка коммита, которая помечает ее во время фактической обработки транзакции. Но к вопросу наконец:
Можно ли представлять данные, как указано выше, и получать уникальные временные метки фиксации, даже если я фиксирую несколько событий в одной транзакции?
Если нет, возможно ли обеспечить строгий порядок каким-либо другим способом, добавив дополнительные столбцы для обеспечения порядка?
В документации говорится, что "значения отметок времени фиксации не гарантируются как уникальные. Транзакции, которые записывают в непересекающиеся наборы полей, могут иметь одинаковую метку времени. Транзакции, которые записывают в перекрывающиеся наборы полей, имеют уникальные метки времени". - но я не совсем понимаю, что представляет собой "наборы полей" в этом контексте.
В документации также говорится, что "отметка времени фиксации облегчает создание журнала изменений, потому что отметки времени могут обеспечить порядок записей в журнале изменений". но неясно, какие гарантии существуют вокруг временных меток фиксации, имеющих принудительный порядок, в контексте нескольких одновременных авторов или нескольких событий, записываемых одновременно.
1 ответ
Если у вас есть несколько событий в одной транзакции, то все они будут иметь одну и ту же временную метку.
Поле - это ячейка таблицы (одно значение столбца в одной строке). Таким образом, "неперекрывающиеся наборы полей" в этом контексте в основном означают отдельные строки, потому что одно из полей - это отметка времени фиксации!
Две независимые транзакции, одна обновляющая строка "R1" и одна обновляющая строка "R2" в одной и той же таблице, теоретически могут иметь одну и ту же метку времени фиксации, поскольку они не перекрываются.
Можно ли представлять данные, как указано выше, и получать уникальные временные метки фиксации, даже если я фиксирую несколько событий в одной транзакции?
В приведенном вами примере, где вы используете отметку времени коммита в своем первичном ключе, тогда нет, вы не сможете добавить несколько событий к одной и той же паре stream_id/category в одной транзакции, так как они будут иметь одну и ту же метку времени - и следовательно, тот же первичный ключ.
Если нет, возможно ли обеспечить строгий порядок каким-либо другим способом, добавив дополнительные столбцы для обеспечения порядка?
Если вы использовали комбинацию метки времени фиксации и sequence_number для каждого кортежа (stream_id, category, timestamp), то вы можете сохранить строгий порядок в одной транзакции:
Увеличьте порядковый номер, начиная с 0, для каждой пары (stream_id, category) в одной и той же транзакции. Временная метка фиксации будет обеспечивать порядок для разных транзакций, а порядковый номер будет обеспечивать порядок внутри транзакции...