[PLSQL]Are you sure it will be definitely random? (DBMS_RANDOM.SEED)

如果想得到一个随机值,非常自然地会想到用DBMS_RANDOM包来做。但是有时候不注意,可能得到的并不是我们想要的结果,就遇到如下一个”陷阱“: 

假设有张表T里面有一列存储字符串,并且在这个列上有个unique constraint, 由于系统并不care这列里面存储的字符串是啥东东,只要它是唯一的就OK。那么有一个简单的function来获取每次需要往这一列插入的字符串,如下所示:

CREATE OR REPLACE FUNCTION GET_NEXT_STR RETURN VARCHAR2
IS
V_SEED
VARCHAR2(50);
BEGIN
SELECT TO_CHAR(SYSTIMESTAMP, 'ddmmyyyyHH24MMSSFF') INTO V_SEED FROM DUAL;
DBMS_RANDOM.SEED(V_SEED);

RETURN DBMS_RANDOM.STRING('A', 20);
END GET_NEXT_STR;

因为同样的SEED会产生同样的随机值,自然是想确保每次调用的时候都是用不同的seed来生成随机的字符串,时间(time)自然是很常见的SEED选择。 一般来说, GET_NEXT_STR可以工作的很好,没有一点问题。 

但是,如果在一个循环里面去调用这个function,或者多线程同时来调用这个function, 很可能就会出现让人”惊讶"的情况。可以进行如下简单地测试...

set serveroutput on
BEGIN
FOR I IN 1..10
LOOP
DBMS_OUTPUT.PUT_LINE(GET_NEXT_STR);
END LOOP;
END;

输出结果如下:

AwPUykNKErDexsElUnwC
snKBvcxsTUiMSLWuMCgo
snKBvcxsTUiMSLWuMCgo
snKBvcxsTUiMSLWuMCgo
snKBvcxsTUiMSLWuMCgo
snKBvcxsTUiMSLWuMCgo
zNuLksgAvpBiSqXGqCeA
zNuLksgAvpBiSqXGqCeA
zNuLksgAvpBiSqXGqCeA
zNuLksgAvpBiSqXGqCeA

可以看到结果出现了很多重复的值,居然不是绝对随机的!What a surprise! 不过细细想想,用时间作为seed能够保证每次调用的时候seed的值都是唯一的吗? 

SELECT TO_CHAR(SYSTIMESTAMP, 'ddmmyyyyHH24MMSSFF') INTO V_SEED FROM DUAL;

常规调用的话,每次返回的值确实不一样,但是因为是放在一个循环里面来执行,CPU运行速度快过我们能捕获到时间的变化,因此有些调用的seed值会一样的,可以简单测试如下,

set serveroutput on
BEGIN
FOR I IN 1..10
LOOP
DBMS_RANDOM.SEED(TO_CHAR(SYSTIMESTAMP,
'ddmmyyyyHH24MMSSFF'));
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SYSTIMESTAMP,
'ddmmyyyyHH24MMSSFF') || '---' || DBMS_RANDOM.STRING('A', 20));
END LOOP;
END;

输入结果如下,

27102010201031970000000---pULExiNuvqHCEwdQWnZQ
27102010201031980000000---pULExiNuvqHCEwdQWnZQ
27102010201031980000000---yIsbzOzTCSvcJxmXPEzG
27102010201031980000000---yIsbzOzTCSvcJxmXPEzG
27102010201031980000000---yIsbzOzTCSvcJxmXPEzG
27102010201031980000000---yIsbzOzTCSvcJxmXPEzG
27102010201031990000000---yIsbzOzTCSvcJxmXPEzG
27102010201031990000000---uySDJCwCmPqYrpJeKMeY
27102010201031990000000---uySDJCwCmPqYrpJeKMeY
27102010201031990000000---uySDJCwCmPqYrpJeKMeY

因此,这样常规地用timestamp来做seed不能保证特殊情况下的需求。那么该如何做呢,一种方法很简单,就是每次调用的时候,我让当前线程sleep一下下,这样可以保证每次循环得到的seed都不同,当然缺点也显而易见,响应时间会受影响。另外一种方法,我觉得可以之间不要自己来生成seed, 而是让系统自动为我们生成,这样生成重复的值的可能性会小得多!

【Sum-up】

以后再”随机“的时候,可要小心点了:)




--------------------------------------
Regards,
FangwenYu
原文地址:https://www.cnblogs.com/fangwenyu/p/1862930.html