0

我的进程运行多个实例(进程)和多个线程,它们都写入同一个数据库。一旦提出请求,就会为要添加到专有数据库的记录生成一个唯一的请求 ID。这是我们的限制:它不能超过 9 个字符的长度,需要将 hhmmss 作为前 6 个字符。我们决定使用 ms 作为最后 3 位数字来完成 9 个字符,我们正在使用 gettimeofday() 完成所有这些操作。但是,随着流量的增加,现在在 ms 周期内放置多个请求时会发生冲突。这与 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

4 回答 4

1

您所描述的问题类型已经通过发出 UUID 或多或少地解决了。这是一个旨在解决您提到的所有问题以及更多问题的系统。

一个 linux 库: http: //linux.die.net/man/3/uuid

更多信息可在此处获得:http ://en.wikipedia.org/wiki/Universally_unique_identifier

于 2010-10-13T23:02:21.227 回答
0

使用时间戳作为唯一 ID 将永远无法可靠地工作,除非您将自己限制为每个最低时钟滴答(在本例中为 1 毫秒)只有一个事务。

由于您无法使用 9 个字节中的前 6 个字节的时间值,因此您需要尝试将尽可能多的范围放入最后 3 个字节中。

如果您可以避免在最后 3 个字节中不使用 ASCII 字符,那么您应该避免使用它,因为这会限制它可以有很多的值。如果可能,您应该尝试将这些字节用作 24 位整数(范围为 16777216),并让每个事务递增计数器。然后,您可以在每次 gettimeofday 让您知道时间已更改时将其设置回 0。(或者您可以设置一个重复的 SIGALRM 以让您知道何时再次调用 gettimeofday 以更新您的时间和 0 24 位整数)。

如果您被迫对这些字节使用 ASCII 可打印字符,那么事情就有点困难了。扩展此范围的最简单方法是使用十六进制数而不是十进制数。这会将您的可表示范围从 1000 增加到 4096。不过,如果您使用更广泛的数字基数,您可以做得更好。如果您添加了字母表的前 22 个字符(与添加十六进制前 6 个字母的方式相同),那么您可以表示32x32x32值,即 32768。这将是每秒很多事务。如果您进一步扩展数字字母表,您可以做得更好,但它会变得更加零碎,因为您可能希望限制某些字符出现在值中。使用表示strtolstrtoul可以轻松使用可能更容易编程。

如果您的应用程序是多线程的,那么您可能需要考虑将部分数字范围用作线程 ID,并让每个线程保留自己的事务计数器。这将使确定由不同线程处理的两个事务之间的相对时间更难计算,但它将阻止所有线程想要增加相同的内存位置(这可能需要互斥锁或信号量)。

于 2010-10-13T19:51:21.337 回答
0

通常在像这样的重负载系统上使用时钟时间,分辨率低于一秒无论如何都是一个坏主意。线程将获取它们的时间戳,然后在操作中间取消调度,因此您会看到事情无序到达。

剩下三个字符来唯一地编码事物并不多。至少尝试使用一些不同的编码,例如 base64。

如果您将gcc其用作编译器,则可以将线程本地存储(TLS)作为非常有效的扩展。只需在您的static变量前面加上__thread(或左右)。如果您仅限于 phtreads,那么也有办法获取线程特定的键,pthread_get_key. 但更好的办法是将信息尽可能长地放在线程的堆栈上。

要获取为您的请求生成序列号的每个线程计数器,请使用

  • 到目前为止你的 hhmmss 时间戳
  • 识别线程所需的尽可能多的位
  • 上面每个线程序列号的最后一位 应该只在一秒多后回绕

您甚至可能在作弊,并且yield线程在同一秒内触发了太多请求。

于 2010-10-13T19:51:29.773 回答
0

我想你可以在启动时给每个进程的每个线程一个唯一的 ID,我想这只会占用 3 个可用字符中的一个,除非你有数百个线程。然后,您可以使用每个线程的本地计数器来设置最后两个字符(使用 base64 甚至更多,具体取决于允许的字符,以获得足够的幅度)。

在这种情况下,唯一可能发生冲突的情况是线程的计数器在同一秒内换行。

当然,这是一个肮脏的黑客。正确的方法是在线程/进程之间共享资源。在您的情况下,这可能是最简单的解决方案。

于 2010-10-13T19:57:29.043 回答