需求是需要給數(shù)據(jù)庫mysql插入100萬個10位的隨機字符串犁跪,以后隨時從數(shù)據(jù)庫里獲取100-1000個值用于業(yè)務系統(tǒng)裂七。
10位隨機字符串可以使用的字符包括0-9和A-Z,任何語言都基本上有隨機函數(shù),生成10位隨機字符串很簡單嘀掸,但是怎么確保100萬條數(shù)據(jù)都不一樣材蹬。
效率最低但是最簡單的想法是插入一條前和現(xiàn)有的所有數(shù)據(jù)比較实幕,不一樣就插入,發(fā)現(xiàn)一樣就重新生成一個隨機值再比較堤器,時間復雜度是o(n!),很顯然對于100萬條數(shù)據(jù)來說這種算法是不可取的昆庇。
我們換一個思路,我先順序生成100萬條數(shù)據(jù)闸溃,然后隨機從100萬條數(shù)據(jù)里取100-1000條整吆,這樣生成數(shù)據(jù)的時間復雜度是o(n),效率高很多辉川,取隨機數(shù)據(jù)直接使用mysql帶的rand()函數(shù)掂为。以下是細節(jié):
1. 生成數(shù)據(jù)
順序生成100萬條數(shù)據(jù)的方法就是我們最簡單的排列組合,可選的字符是36個员串,字符串10位勇哗,則組合可能是 P(36, 10) ,如果我們用筆和紙的話寸齐,很容易寫出第一個到n個的數(shù)據(jù):
0000000000
0000000001
...
000000000A
000000000B
...
000000000Z
0000000010
0000000011
...
其本質其實是一個36進制的數(shù)一直在增值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;
}
這里有一個細節(jié)就是 P(36,4)=36*35*34*33 的值就已經大于100萬了蛹含。我們如果按順序生成,只會用到后4位塞颁,前面6位全是0浦箱,為了避免最后生成的隨機字符串前6位都一樣,我們可以把前6位變成隨機值祠锣。
//100萬前6位都是0酷窥,所以把前6位改成隨機值
for (int j = 0; j < 6; j++) {
int temp = ThreadLocalRandom.current().nextInt(36);
ss[j] = all[temp];
}
2. 插入數(shù)據(jù)
我們肯定不能生成一條數(shù)據(jù)就插一次數(shù)據(jù)庫,100萬條數(shù)據(jù)分10組伴网,每組10萬條蓬推,我們也不能10萬條數(shù)據(jù)就生成10萬個 insert 語句,我們拼成一個 insert 語句澡腾,類似:
insert into tablename (cloumn1, column2) values
(c1_v1,c2_v1),
(c1_v2,c2_v2),
(c1_v3,c2_v3)
然后通過 JDBC 執(zhí)行 SQL 語句完成插入沸伏。基本的代碼如下:
void run() throws SQLException, ClassNotFoundException {
long index = 0;
long TOTAL = 1000000;
//分組
long PART = 100000;
//分10個insert語句插入數(shù)據(jù)庫
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());
}
}
最后測試一下动分,從生成到插入數(shù)據(jù)庫100萬條大概8秒毅糟,速度是可以接收的。
3. 獲取數(shù)據(jù)
從數(shù)據(jù)庫里獲取數(shù)據(jù)不需要再單獨寫算法了澜公,利用mysql的rand函數(shù)就可以留特。
SELECT * FROM random_values order by rand() limit 1000
但是要注意的是,獲取完需要把這1000條數(shù)據(jù)從表中刪除或加一個標記表示已用玛瘸,否則下次再獲取可能會重復蜕青。
完整源碼請參考 git