程序员

生成100万个10位随机字符串

2019-03-24  本文已影响0人  voxer

需求是需要给数据库mysql插入100万个10位的随机字符串,以后随时从数据库里获取100-1000个值用于业务系统。
10位随机字符串可以使用的字符包括0-9和A-Z,任何语言都基本上有随机函数,生成10位随机字符串很简单,但是怎么确保100万条数据都不一样。

效率最低但是最简单的想法是插入一条前和现有的所有数据比较,不一样就插入,发现一样就重新生成一个随机值再比较,时间复杂度是o(n!),很显然对于100万条数据来说这种算法是不可取的。

我们换一个思路,我先顺序生成100万条数据,然后随机从100万条数据里取100-1000条,这样生成数据的时间复杂度是o(n),效率高很多,取随机数据直接使用mysql带的rand()函数。以下是细节:

1. 生成数据

顺序生成100万条数据的方法就是我们最简单的排列组合,可选的字符是36个,字符串10位,则组合可能是 P(36, 10) ,如果我们用笔和纸的话,很容易写出第一个到n个的数据:

0000000000
0000000001
...
000000000A
000000000B
...
000000000Z
0000000010
0000000011
...

其本质其实是一个36进制的数一直在增值1,就是从最后一位开始,一直加,到9加1就到 A ,到 Z 之后再加1就开始进一位。对应的算法代码如下:

//36进制加一进位
private char[] systemAdd(char[] ss) {
    int LENGTH = 10;
    for (int i = LENGTH - 1; i >= 0; i--) {
        int temp = ss[i];
        int number9 = '9';
        int numberZ = 'Z';
        if (temp < number9 - 1) {
            ss[i] = (char) ((int) ss[i] + 1);
            break;
        } else if (temp == number9) {
            ss[i] = 'A';
            break;
        } else if (temp < numberZ) {
            ss[i] = (char) ((int) ss[i] + 1);
            break;
        } else {
            ss[i] = '0';
        }
    }
    return ss;
}

这里有一个细节就是 P(36,4)=36*35*34*33 的值就已经大于100万了。我们如果按顺序生成,只会用到后4位,前面6位全是0,为了避免最后生成的随机字符串前6位都一样,我们可以把前6位变成随机值。


image.png
//100万前6位都是0,所以把前6位改成随机值
for (int j = 0; j < 6; j++) {
    int temp = ThreadLocalRandom.current().nextInt(36);
    ss[j] = all[temp];
}

2. 插入数据

我们肯定不能生成一条数据就插一次数据库,100万条数据分10组,每组10万条,我们也不能10万条数据就生成10万个 insert 语句,我们拼成一个 insert 语句,类似:

insert into tablename (cloumn1, column2) values 
                                  (c1_v1,c2_v1),
                                  (c1_v2,c2_v2),
                                  (c1_v3,c2_v3)

然后通过 JDBC 执行 SQL 语句完成插入。基本的代码如下:

void run() throws SQLException, ClassNotFoundException {
    long index = 0;
    long TOTAL = 1000000;
    //分组
    long PART = 100000;
    //分10个insert语句插入数据库
    for (long i = 0; i < TOTAL / PART; i++) {
        StringBuffer sb = new StringBuffer();
        dbService.initInsertSQL(sb);
        for (long j = i * PART; j < (i + 1) * PART; j++) {
            index++;
            if (index > TOTAL) break;
            dbService.appendInsertSQL(index, String.valueOf(current), sb);
            current = systemAdd(current);
        }
        sb.deleteCharAt(sb.length() - 1);
        dbService.excute(sb.toString());
    }
}

最后测试一下,从生成到插入数据库100万条大概8秒,速度是可以接收的。

3. 获取数据

从数据库里获取数据不需要再单独写算法了,利用mysql的rand函数就可以。

SELECT * FROM random_values order by rand() limit 1000

但是要注意的是,获取完需要把这1000条数据从表中删除或加一个标记表示已用,否则下次再获取可能会重复。


image.png

完整源码请参考 git

上一篇下一篇

猜你喜欢

热点阅读