我的进程运行多个实例(进程)和多个线程,并且所有线程都写入同一个数据库。一旦发出请求,就会为要添加到专有数据库的记录生成唯一的req 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;
}
答案 0 :(得分:1)
您所描述的问题类型已经或多或少地通过发布UUID来解决。这个系统旨在解决您提到的所有问题以及更多问题。
一个linux库:http://linux.die.net/man/3/uuid
此处提供了更多信息:http://en.wikipedia.org/wiki/Universally_unique_identifier
答案 1 :(得分:0)
使用时间戳作为唯一ID永远不会可靠地工作,除非您将自己限制为每个最低时钟滴答只有一个事务(在这种情况下为1毫秒)。
由于您在9个字节的前6个中使用时间值,因此需要尝试尽可能多地填充最后3个字节的范围。
如果你可以在最后3个字节中不使用ASCII字符,那么你应该避免它,因为这将限制它可以有很多的价值。如果可能,您应该尝试将这些字节用作24位整数(范围为16777216),并让每个事务递增计数器。然后,每当gettimeofday让您知道时间已经改变时,您可以将其设置回0。 (或者你可以设置一个重复的SIGALRM让你知道何时再次调用gettimeofday来更新你的时间和0 24位整数)。
如果您被迫对这些字节使用ASCII可打印字符,那么事情会有点困难。扩展此范围的最简单方法是使用十六进制而不是十进制数。这会使您的可表示范围从1000增加到4096.但是,如果使用更广泛的数字基数,则可以做得更好。如果你在字母表的前22个字符上添加(与在前六个字母上添加十六进制字符的方式相同),那么你可以表示32x32x32
值,即32768.这将是很多事务。第二。如果你进一步扩展你的数字字母表,你可以做得更好,但它会变得更加零碎,因为你可能想要限制某些字符出现在值中。使用strtol
或strtoul
可以轻松使用的表示可能更容易编程。
如果您的应用程序是多线程的,那么您可能需要考虑将部分数值范围作为线程ID,并让每个线程保留自己的事务计数器。这将确定由不同线程处理的两个事务之间的相对时间更难以计算,但它将使所有线程都不想增加相同的内存位置(可能需要互斥锁或信号量)。
答案 2 :(得分:0)
通常在像这样的重负载系统上使用时钟时间,分辨率低于一秒是一个坏主意无论如何。线程将采用其时间戳,然后在操作过程中进行计划,因此您将看到无法到达的事物。
剩下的三个字符对事物进行唯一编码并不多。至少尝试使用一些不同的编码,例如base64。
如果使用gcc
作为编译器,则将线程本地存储(TLS)作为非常有效的扩展。只需在static
变量前加上__thread
(或左右)。如果您仅限于phtreads,那么也可以获得特定于线程的密钥pthread_get_key
。但最好是在线程的堆栈上尽可能长地获取信息。
要获得为您的请求生成序列号的每个线程计数器,请使用
你甚至可以作弊和yield
一个在同一秒内发出太多请求的帖子。
答案 3 :(得分:0)
我想你可以在启动时给每个进程的每个线程一个唯一的ID,我想这只会占用3个可用字符中的一个,除非你有数百个线程。然后,您可以使用每个线程的本地计数器来设置最后两个字符(使用base64或更多,取决于允许的字符,以获得足够的幅度)。
在这种情况下,唯一可能发生碰撞的情况是线程的计数器在同一秒内换行。
当然,这是一个肮脏的黑客。正确的方法是在线程/进程之间共享一个资源。它可能是您案例中最简单的解决方案。