生成100萬個10位隨機字串
需求是需要給資料庫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