gettimeofday clock_gettime решение для генерации уникального номера - PullRequest
0 голосов
/ 13 октября 2010

Мой процесс запускает несколько экземпляров (процессов) и несколько потоков, и все они записывают в одну и ту же базу данных. Как только запрос размещен, генерируется уникальный идентификатор запроса для записи, которая будет добавлена ​​в проприетарную базу данных. Вот наши ограничения: он не может быть длиннее 9 символов, должен иметь hhmmss в качестве первых 6 символов. Мы решили использовать ms для последних 3 цифр, чтобы завершить 9 символов, и мы делаем все это с помощью gettimeofday (). Однако, с увеличением трафика, теперь есть случаи коллизий, когда несколько запросов размещаются за период мс. Это в сочетании с тем фактом, что метод gettimeofday () сам по себе не является точным, вызывает увеличение числа коллизий. Я пытался использовать clock_gettime, но при тестировании он также не настолько точен, как я наблюдал из следующей тестовой программы:

  • Мы не можем использовать статические или глобальные переменные из-за проблем с потоками
  • Невозможно использовать случайные числа, поскольку они должны быть последовательными

Ценю любую помощь.

#include <time.h>

int main( int argc, char **argv )
{
    long i;
    struct timespec start, stop;
    double gap;

    clock_gettime( CLOCK_REALTIME, &start);

    for (i =0; i< 123456789 ; i++);

    clock_gettime( CLOCK_REALTIME, &stop);

    gap = ( stop.tv_sec - start.tv_sec ) + ( stop.tv_nsec - start.tv_nsec ) / 1000000;
    printf( "%lf ms\n", gap );
    return 0;
}

Ответы [ 4 ]

1 голос
/ 14 октября 2010

Тип проблемы, которую вы описываете, уже более или менее решен путем выдачи UUID. Это система, предназначенная для решения всех проблем, о которых вы упоминаете, и некоторых других.

Библиотека Linux: http://linux.die.net/man/3/uuid

Более подробная информация доступна здесь: http://en.wikipedia.org/wiki/Universally_unique_identifier

0 голосов
/ 13 октября 2010

Как правило, использование часов в такой нагруженной системе с разрешением менее секунды - плохая идея.Потоки получат свою временную метку, а затем будут отложены в середине операции, так что вы увидите, что вещи приходят не по порядку.

Три символа, оставленные для уникального кодирования, - это немного.Попробуйте хотя бы использовать другую кодировку, такую ​​как base64.

Если вы используете gcc в качестве компилятора, у вас есть локальное хранилище потоков (TLS) в качестве расширения, которое является весьма эффективным.Просто добавьте к своей переменной static __thread (или около того).Если вы ограничены phtreads, есть способы получить специфичные для потока ключи, также pthread_get_key.Но лучше было бы иметь информацию как можно дольше в стеке потока.

Чтобы получить счетчик на поток, который делает серийный номер для вашего запроса, используйте

  • вашhhmmss отметка времени, пока
  • столько битов, сколько вам нужно, чтобы идентифицировать ваши потоки
  • последние биты для для серийного номера потока , как указано выше, которые должны переноситься только послеболее секунды

Вы можете даже обманывать и yield поток, который запускает слишком много запросов в течение одной секунды.

0 голосов
/ 13 октября 2010

Полагаю, вы могли бы дать каждому потоку каждого процесса уникальный идентификатор при запуске, я полагаю, что для этого потребуется только один из 3 доступных символов, если у вас нет сотен потоков. Затем вы можете использовать локальный счетчик для каждого потока, чтобы установить последние два символа (используя base64 или даже больше, в зависимости от того, какие символы разрешены, чтобы получить достаточную амплитуду).

В этой ситуации единственный случай, когда может произойти столкновение, - это если счетчик потока оборачивается в течение той же секунды.

Конечно, это грязный хак. Правильный путь - разделять ресурсы между потоками / процессами. Это может быть самое простое решение в вашем случае, хотя.

0 голосов
/ 13 октября 2010

Использование метки времени в качестве уникального идентификатора никогда не будет работать надежно, если вы не ограничите себя только одной транзакцией на самый низкий такт (в данном случае 1 миллисекунду).

Поскольку вы застряли, используя значение времени для первых 6 из 9 байтов, вам нужно попытаться вписать как можно больший диапазон в последние 3 байта.

Если вы можете избежать использования символов ASCII в последних 3 байтах, вам следует избегать этого, поскольку это ограничит значения, которые могут иметь большое значение. Если возможно, вы должны попытаться использовать эти байты как 24-битное целое число (диапазон 16777216) и просто сделать каждую транзакцию увеличивающей счетчик. Затем вы можете установить его обратно в 0 каждый раз, когда gettimeofday сообщит вам, что время изменилось. (или вы можете настроить повторяющийся SIGALRM, чтобы сообщить, когда снова вызывать gettimeofday для обновления вашего времени и 0 для 24-битного целого числа).

Если вы вынуждены использовать печатные символы ASCII для этих байтов, тогда все немного сложнее. Самый простой способ расширить диапазон этого - использовать шестнадцатеричные, а не десятичные числа. Это увеличивает ваш представительный диапазон с 1000 до 4096. Вы можете добиться большего успеха, если используете еще более широкую базу номеров. Если вы добавили первые 22 символа алфавита (точно так же, как для шести первых букв), вы можете представить 32x32x32 значений, что составляет 32768. Это будет много транзакций в секунду. Вы можете сделать еще лучше, если расширите числовой алфавит еще больше, но он станет более частичным, поскольку вы, вероятно, захотите ограничить отображение некоторых символов в значении. Использование представления, с которым strtol или strtoul может легко работать, вероятно, будет проще для программирования.

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

...