由于公司準(zhǔn)備用discuz建立企業(yè)級(jí)的論壇,所以需要進(jìn)行百萬級(jí)數(shù)據(jù)的測試。
搜索了很久沒有看到相關(guān)的批量插入數(shù)據(jù)的資料,后來找到一個(gè)批量插入帖子的插件,但導(dǎo)入的txt文件超過2350條記錄就插入不了。最后只好硬著頭皮分析插入帖子的相關(guān)sql語句--直接插入到cdb_posts表前臺(tái)不會(huì)顯示的。
最后簡化為只有標(biāo)題和內(nèi)容的帖子需要在兩個(gè)表插入記錄,最后更新cdb_foums對應(yīng)的一條數(shù)據(jù)即可。
由于是本人主要是開發(fā)java的對php不熟悉,所以測試代碼是用java寫的,但懂php的人根據(jù)我的思路用php也應(yīng)該很容易搞定了。
最后分享下代碼:(用jdbc連接數(shù)據(jù)庫的,需要在java web項(xiàng)目下的lib目錄下導(dǎo)入mysq的jar包)
=====================================
DBManager.java
=====================================
package com.test.utc;
import java.sql.*;
public class DBManager {
//用戶名
private String user = "";
//密碼
private String password = "";
//主機(jī)
private String host = "";
//數(shù)據(jù)庫名字
private String database = "";
/*
private String url="jdbc:mysql://"+host+"/"+"useUnicode=true&characterEncoding=GB2312";
*/
private String url ="";
private Connection con = null;
Statement stmt;
/**
* 根據(jù)主機(jī)、數(shù)據(jù)庫名稱、數(shù)據(jù)庫用戶名、數(shù)據(jù)庫用戶密碼取得連接。
* @param host String
* @param database String
* @param user String
* @param password String
*/
public DBManager(String host, String database, String user, String password) {
this.host = host;
this.database = database;
this.user = user;
this.password = password;
//顯示中文
this.url = "jdbc:mysql://" + host + "/" + database +
"?useUnicode=true&characterEncoding=GB2312";
try {
Class.forName("org.gjt.mm.mysql.Driver");
}
catch (ClassNotFoundException e) {
System.err.println("class not found:" + e.getMessage());
}
try {
con = DriverManager.getConnection(this.url, this.user, this.password);
//連接類型為ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY
stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
}
catch (SQLException a) {
System.err.println("sql exception:" + a.getMessage());
}
}
/**
* 返回取得的連接
*/
public Connection getCon() {
return con;
}
/**
* 執(zhí)行一條簡單的查詢語句
* 返回取得的結(jié)果集
*/
public ResultSet executeQuery(String sql) {
ResultSet rs = null;
try {
rs = stmt.executeQuery(sql);
}
catch (SQLException e) {
e.printStackTrace();
}
return rs;
}
/**
* 執(zhí)行一條簡單的更新語句
* 執(zhí)行成功則返回true
*/
public boolean executeUpdate(String sql) {
boolean v = false;
try {
v = stmt.executeUpdate(sql) > 0 ? true : false;
}
catch (SQLException e) {
e.printStackTrace();
}
finally {
return v;
}
}
}
======================
一次同時(shí)插入兩個(gè)表,for循環(huán)后更新數(shù)據(jù)。
為了方便同時(shí)加入了納米級(jí)別的時(shí)間差,并且把詳細(xì)信息輸出到指定文件中。
轉(zhuǎn)換16進(jìn)制的單獨(dú)測試沒有問題,但轉(zhuǎn)換后作為sql語句插入總是提示過大或者是中文的轉(zhuǎn)換后不能識(shí)別,比較郁悶。不過這個(gè)不是很重要的問題。
插入的文件InsertIntoSQL .java如下:
======================
package com.test.utc;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.test.utc.DBManager;
public class InsertIntoSQL
{
public final static String path = "D:/ComsenzEXP/wwwroot/discuz/attachments/testLog.txt";
public static void main (String[] agrs)
{
//System.out.println(toHexString("測試"));
//System.out.println(toStringHex("0x74657374"));
insertData(300000,4,100000,"test","test");
}
/*
* @deprecated 插入數(shù)據(jù)
* @param startNumber
* @param endNumber
* @param fourmid
* @param postsNumber
* @param todayposts
*/
public static void insertData(long insertNumber,int fourmid,long todayposts,String title,String content)
{
String message = "插入:" + insertNumber + "條記錄";
System.out.println(message);
appendMethod(path,message);
long startTime = System.nanoTime();
message = "insertData startTime:" + startTime;
System.out.println(message);
appendMethod(path,message);
DBManager dbm = new DBManager("localhost:6033", "discuz", "discuz", "discuzexp");
String sql = "";
String pid = getLastRowByColumnValue(dbm,"cdb_posts","pid");
long startNumber = Long.parseLong(pid) + 1;
long endNumber = startNumber + insertNumber;
long i = 1;
//content = toHexString(content);
//0x6d4b8bd551855bb9
//0x6d4b8bd5
//String insertTitle = title;
for(i = startNumber; i < endNumber; i++)
{
//insertTitle = insertTitle + i;
//insertTitle = toHexString(insertTitle + i);
sql = "INSERT INTO cdb_posts VALUES ('" + i + "','" + fourmid + "','" + i + "','1',0x61646d696e,'1',0x74657374,'1221120302',0x74657374,0x3139322e3136382e302e3335,'0','0','0','0','-1','-1','0','0','0','0','0')";
dbm.executeUpdate(sql);
sql = "INSERT INTO cdb_threads VALUES ('" + i + "','" + fourmid + "','0','0','0','0',0x61646d696e,'1',0x74657374,'1221120302','1221120302',0x61646d696e,'1','0','0','0','0','0','0','0','0','0','0','0','0','0','0')";
dbm.executeUpdate(sql);
/*
sql = "INSERT INTO cdb_posts VALUES ('" + i + "','" + fourmid + "','" + i + "','1',0x61646d696e,'1','" + insertTitle + "','1221120302','" + content + "',0x3139322e3136382e302e3335,'0','0','0','0','-1','-1','0','0','0','0','0')";
dbm.executeUpdate(sql);
sql = "INSERT INTO cdb_threads VALUES ('" + i + "','" + fourmid + "','0','0','0','0',0x61646d696e,'1','" + insertTitle + "','1221120302','1221120302',0x61646d696e,'1','0','0','0','0','0','0','0','0','0','0','0','0','0','0')";
dbm.executeUpdate(sql);
*/
}
todayposts = endNumber - 499999;
sql = "UPDATE cdb_forums SET lastpost='0x74657374', threads=" + endNumber + ", posts=" + endNumber + ", todayposts=" + todayposts + " WHERE fid=" + fourmid;
dbm.executeUpdate(sql);
message = "insertData endTime:" + System.nanoTime();
System.out.println(message);
appendMethod(path,message);
long estimatedTime = System.nanoTime() - startTime;
message = "相差(用時(shí)):" + estimatedTime + "納秒," + estimatedTime/1000000000 + "秒\r\n\r\n";//1秒=10億納秒
System.out.println(message);
appendMethod(path,message);
}
/*
* @deprecated 獲取最后一行某列的值
* @param table
* @param column
*/
public static String getLastRowByColumnValue(DBManager dbm,String table,String column)
{
String sql = "";
sql = "select " + column + " from " + table + " order by " + column + " desc limit 1";
ResultSet rs = dbm.executeQuery(sql);
String str = "";
try {
while( rs.next())
{
str = rs.getString(column);
}
} catch (SQLException e) {
e.printStackTrace();
}
return str;
}
//轉(zhuǎn)化字符串為十六進(jìn)制編碼
public static String toHexString(String s)
{
String str="";
for (int i=0;i<s.length();i++)
{
int ch = (int)s.charAt(i);
String s4 = Integer.toHexString(ch);
str = str + s4;
}
return "0x" + str;
}
/**
* B方法追加文件:使用FileWriter
* @param fileName
* @param content
*/
public static void appendMethod(String fileName, String content){
try {
//打開一個(gè)寫文件器,構(gòu)造函數(shù)中的第二個(gè)參數(shù)true表示以追加形式寫文件
FileWriter writer = new FileWriter(fileName, true);
writer.write(content + "\r\n");
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
=========================
測試結(jié)果
=========================
插入:1條記錄
insertData startTime:996017275659
insertData endTime:996570971450
相差:555546585納秒,0秒
插入:10條記錄
insertData startTime:1005210041220
insertData endTime:1005774437812
相差:566248503納秒,0秒
插入:100條記錄
insertData startTime:1014529111102
insertData endTime:1015376139146
相差:848823244納秒,0秒
插入:1000條記錄
insertData startTime:1024027419406
insertData endTime:1027460295093
相差:3434544614納秒,3秒
插入:10000條記錄
insertData startTime:1098779316644
insertData endTime:1116864493392
相差:18086540050納秒,18秒
插入:20000條記錄
insertData startTime:1172427124676
insertData endTime:1207721237196
相差:35295511580納秒,35秒
插入:30000條記錄
insertData startTime:1221623540422
insertData endTime:1273396534755
相差:51774426358納秒,51秒
插入:40000條記錄
insertData startTime:1304022174326
insertData endTime:1373074817609
相差:69054081175納秒,69秒
插入:50000條記錄
insertData startTime:1387615577957
insertData endTime:1474072398866
相差:86458228630納秒,86秒
插入:60000條記錄
insertData startTime:1498220329120
insertData endTime:1602732788105
相差:104513895481納秒,104秒
插入:70000條記錄
insertData startTime:1713667130878
insertData endTime:1834998856279
相差:121333178658納秒,121秒
插入:80000條記錄
insertData startTime:1859249070482
insertData endTime:1998321805424
相差:139074161939納秒,139秒
插入:90000條記錄
insertData startTime:2025945141631
insertData endTime:2182073256517
相差:156129545235納秒,156秒
插入:100000條記錄
insertData startTime:2220057424616
insertData endTime:2395134985871
相差:175078943553納秒,175秒
插入:120000條記錄
insertData startTime:2658822511240
insertData endTime:2866959720299
相差:208138247459納秒,208秒
插入:150000條記錄
insertData startTime:2886769641392
insertData endTime:3146130152016
相差:259361930078納秒,259秒
插入:180000條記錄
insertData startTime:3482758554788
insertData endTime:3795354906559
相差:312598627479納秒,312秒
插入:200000條記錄
insertData startTime:4023631035788
insertData endTime:4370300796888
相差(用時(shí)):346671225253納秒,346秒
插入:250000條記錄
insertData startTime:4460471629494
insertData endTime:4894180641115
相差(用時(shí)):433710430516納秒,433秒
插入:300000條記錄
insertData startTime:4971718038186
insertData endTime:5489207947378
相差(用時(shí)):517491270818納秒,517秒
========================
另外分享幾條刪除,更新的sql語句
DELETE FROM cdb_posts where pid > 1000000
DELETE FROM cdb_threads where tid > 1000000
已刪除行數(shù): 1262140 (查詢花費(fèi) 130.2156 秒)
UPDATE cdb_forums SET lastpost='0x74657374', threads=1000000, posts=1000000, todayposts=500001 WHERE fid=4;
select pid from cdb_posts order by pid desc limit 1;
============================
最后通過loadrunner模擬用戶點(diǎn)擊頁面普通頁面,和在100萬條數(shù)據(jù)的表下面查詢20條數(shù)據(jù)的頁面(通過后臺(tái)js調(diào)用設(shè)置就可以了)。
希望官方和那位高手能整出更加高效,輕松實(shí)現(xiàn)的方法來。
=================================================
后來發(fā)現(xiàn)每個(gè)版塊最多只能1000分頁--雖然上面顯示了25000頁,但點(diǎn)到1000頁時(shí)后面就沒有分頁了,直接輸入1001就直接跳轉(zhuǎn)到1000也那里了。也就是說一個(gè)版塊前臺(tái)用戶看到的最多文章主題只有20000條。無法做到真正的100萬數(shù)據(jù)量(數(shù)據(jù)庫里面確實(shí)是100萬主題數(shù))。
最后發(fā)現(xiàn)這些越來是可以在后臺(tái)設(shè)置的。后臺(tái)-->全局-->界面與顯示-->主題列表頁-->每頁顯示主題數(shù):
主題列表中每頁顯示主題數(shù)目
主題列表最大頁數(shù):
主題列表中用戶可以翻閱到的最大頁數(shù),建議設(shè)置為默認(rèn)值 1000,或在不超過 2500 范圍內(nèi)取值,0 為不限制
==================================================================================
后記:沒想到自己發(fā)的這篇文章受到這么多人的關(guān)注,順便對回復(fù)的問題作個(gè)概要回答:
儲(chǔ)存過程是我一開始考慮到的,但為了模擬真實(shí)插入數(shù)據(jù)的效率后來否定了。
Oracle SQL Loader 工具對這個(gè)應(yīng)用沒有用,因?yàn)檫@個(gè)是要用來跟其他系統(tǒng)作對比的,使用的都是mysql,批量提交的方法也測試過了,受到文件大小的限制一次最多只能插入兩千條。
非常歡迎給更多的人分享,如果轉(zhuǎn)載文章中能注明作者或網(wǎng)址就更好了。