需求是需要給數(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ī)值。

//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ù)。

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