zoukankan      html  css  js  c++  java
  • [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
  • 相关阅读:
    oracle 开发 第16章 SQL优化
    oracle 开发 第13章 数据库对象
    oracle 开发 第14章 集合
    oracle 开发 第15章 大对象
    IDEA创建springboot项目失败问题解决
    redis事务 学习笔记
    redis通信协议 学习笔记
    运行报caused by: redis.clients.jedis.exceptions.JedisConnectionException: Failed connecting to host错
    redis主从同步(复制+哨兵) 学习笔记
    redis限流redis-cell模块安装 笔记
  • 原文地址:https://www.cnblogs.com/fangwenyu/p/1862930.html
Copyright © 2011-2022 走看看