生成100萬(wàn)個(gè)10位隨機(jī)字符串

需求是需要給數(shù)據(jù)庫(kù)mysql插入100萬(wàn)個(gè)10位的隨機(jī)字符串,以后隨時(shí)從數(shù)據(jù)庫(kù)里獲取100-1000個(gè)值用于業(yè)務(wù)系統(tǒng)。
10位隨機(jī)字符串可以使用的字符包括0-9和A-Z,任何語(yǔ)言都基本上有隨機(jī)函數(shù),生成10位隨機(jī)字符串很簡(jiǎn)單,但是怎么確保100萬(wàn)條數(shù)據(jù)都不一樣。

效率最低但是最簡(jiǎn)單的想法是插入一條前和現(xiàn)有的所有數(shù)據(jù)比較,不一樣就插入,發(fā)現(xiàn)一樣就重新生成一個(gè)隨機(jī)值再比較,時(shí)間復(fù)雜度是o(n!),很顯然對(duì)于100萬(wàn)條數(shù)據(jù)來(lái)說(shuō)這種算法是不可取的。

我們換一個(gè)思路,我先順序生成100萬(wàn)條數(shù)據(jù),然后隨機(jī)從100萬(wàn)條數(shù)據(jù)里取100-1000條,這樣生成數(shù)據(jù)的時(shí)間復(fù)雜度是o(n),效率高很多,取隨機(jī)數(shù)據(jù)直接使用mysql帶的rand()函數(shù)。以下是細(xì)節(jié):

1. 生成數(shù)據(jù)

順序生成100萬(wàn)條數(shù)據(jù)的方法就是我們最簡(jiǎn)單的排列組合,可選的字符是36個(gè),字符串10位,則組合可能是 P(36, 10) ,如果我們用筆和紙的話,很容易寫(xiě)出第一個(gè)到n個(gè)的數(shù)據(jù):

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

其本質(zhì)其實(shí)是一個(gè)36進(jìn)制的數(shù)一直在增值1,就是從最后一位開(kāi)始,一直加,到9加1就到 A ,到 Z 之后再加1就開(kāi)始進(jìn)一位。對(duì)應(yīng)的算法代碼如下:

//36進(jìn)制加一進(jìn)位
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;
}

這里有一個(gè)細(xì)節(jié)就是 P(36,4)=36*35*34*33 的值就已經(jīng)大于100萬(wàn)了。我們?nèi)绻错樞蛏桑粫?huì)用到后4位,前面6位全是0,為了避免最后生成的隨機(jī)字符串前6位都一樣,我們可以把前6位變成隨機(jī)值。


image.png
//100萬(wàn)前6位都是0,所以把前6位改成隨機(jī)值
for (int j = 0; j < 6; j++) {
    int temp = ThreadLocalRandom.current().nextInt(36);
    ss[j] = all[temp];
}

2. 插入數(shù)據(jù)

我們肯定不能生成一條數(shù)據(jù)就插一次數(shù)據(jù)庫(kù),100萬(wàn)條數(shù)據(jù)分10組,每組10萬(wàn)條,我們也不能10萬(wàn)條數(shù)據(jù)就生成10萬(wàn)個(gè) insert 語(yǔ)句,我們拼成一個(gè) insert 語(yǔ)句,類似:

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

然后通過(guò) JDBC 執(zhí)行 SQL 語(yǔ)句完成插入?;镜拇a如下:

void run() throws SQLException, ClassNotFoundException {
    long index = 0;
    long TOTAL = 1000000;
    //分組
    long PART = 100000;
    //分10個(gè)insert語(yǔ)句插入數(shù)據(jù)庫(kù)
    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());
    }
}

最后測(cè)試一下,從生成到插入數(shù)據(jù)庫(kù)100萬(wàn)條大概8秒,速度是可以接收的。

3. 獲取數(shù)據(jù)

從數(shù)據(jù)庫(kù)里獲取數(shù)據(jù)不需要再單獨(dú)寫(xiě)算法了,利用mysql的rand函數(shù)就可以。

SELECT * FROM random_values order by rand() limit 1000

但是要注意的是,獲取完需要把這1000條數(shù)據(jù)從表中刪除或加一個(gè)標(biāo)記表示已用,否則下次再獲取可能會(huì)重復(fù)。


image.png

完整源碼請(qǐng)參考 git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容