1. 程式人生 > 資料庫 >分析Mysql大量資料匯入遇到的問題以及解決方案

分析Mysql大量資料匯入遇到的問題以及解決方案

在專案中,經常會碰到往資料庫中匯入大量資料,以便利用sql進行資料分析。在匯入資料的過程中會碰到一些需要解決的問題,這裡結合匯入一個大約4G的txt資料的實踐,把碰到的問題以及解決方法展現出來,一方面自己做個總結記錄,另一方面希望對那些碰到相同問題的朋友有個參考。

我匯入的資料是百科的txt檔案,檔案大小有4G多,資料有6500萬餘條,每條資料通過換行符分隔。每條資料包含三個欄位,欄位之間通過Tab分隔。將資料取出來的方法我採用的是用一個TripleData類來存放這三個欄位,欄位都用String,然後將多條資料存到List<TripleData>中,再將List<TripleData>存入mysql資料庫,分批將所有資料存到mysql資料庫中。

以上是一個大概的思路,下面是具體匯入過程中碰到的問題。

1 資料庫連線的亂碼及相容問題。

資料中如果有中文的話,一定要把連結資料庫的url設定編碼的引數,url設定為如下的形式。

URL="jdbc:mysql://"+IP+":"+PORT+"/"+DB_NAME+"?useSSL=false&useUnicode=true&characterEncoding=utf-8";

把編碼設定為UTF-8是解決亂碼問題,設定useSSL是解決JDBC與mysql的相容問題。如果不設定useSSL,會報錯。類似於

Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+,5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false,or set useSSL=true and provide truststore for server certificate verification.

這樣的錯誤資訊。主要是mysql版本比較高,JDBC版本比較低,需要相容。

2 utf8mb4編碼問題

在匯入資料的過程中,還會碰到類似於

SQLException :Incorrect string value: '\xF0\xA1\x8B\xBE\xE5\xA2...' for column 'name'

這樣的錯誤資訊,這是由於mysql中設定的utf-8是預設3個位元組的,對於一般的資料是沒有問題的,如果是大的資料量,裡面難免會包含一些微信表情,或者特殊字元,它們佔了4個位元組,utf-8不能處理,所以報錯。解決的辦法就是mysql在5.5.3以後的版本引入了4個位元組的utf-8編碼,也就是utf8mb4,需要對mysql的編碼重新設定。

可以按照以下步驟進行操作,一是對要修改的資料庫進行備份,雖然utf8mb4是向下相容utf8的,但為了以防操作不當,還是需要防患於未然,做好備份工作。二是要修改資料庫的字符集編碼為utf8mb4—UTF-8 Unicode,排序規則utf8mb4_general_ci。以上修改我是使用navicat進行修改的,如何用命令列修改,大家可以自行查詢。三是要修改配置檔案my.ini,在mysql安裝的根目錄下。加入以下設定。

[client]
default-character-set = utf8mb4
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
[mysql]
default-character-set = utf8mb4

修改完成後,需要重新啟動mysql,使修改生效。

然後再進行資料的匯入工作,應該就可以正常匯入了。

3 大批量匯入的時間效率問題

由於我們的資料量比較大,我們把資料進行了分割,我把6500萬條資料分為500個檔案,每個檔案大約11萬條資料,將這11萬條資料放到ArrayList<TripleObject>中,然後批量匯入。大概的思路是採用“insert into tb (...) values(...),(...)...;”的方法,用insert一次性插入,這樣時間會節約很多時間。示例方法如下。

public static void insertSQL(String sql,List<TripleObject> tripleObjectList) throws SQLException{
    Connection conn=null;
    PreparedStatement psts=null;
    try {
      conn=DriverManager.getConnection(Common.URL,Common.DB_USERNAME,Common.DB_PASSWORD);
      conn.setAutoCommit(false); // 設定手動提交 
      // 儲存sql字尾
      StringBuffer suffix = new StringBuffer();
      int count = 0; 
      psts=conn.prepareStatement("");
      String s="";
      String p="";
      String o="";
      while (count<tripleObjectList.size()) {
        s=tripleObjectList.get(count).getSubject().replaceAll(",",".").replaceAll("\\(","").replaceAll("\\)","").replaceAll("\'","").replaceAll("\\\\","");
        p=tripleObjectList.get(count).getPredicate().replaceAll(","");
        o=tripleObjectList.get(count).getObject().replaceAll(","");
        suffix.append("('" +s +"','"+p+"','"+ o+"'),");
        count++;
      }
      // 構建完整SQL
      String allsql = sql + suffix.substring(0,suffix.length() - 1);
      // 新增執行SQL
      psts.addBatch(allsql);
      psts.executeBatch(); // 執行批量處理 
      conn.commit(); // 提交 
    } catch (Exception e) {
      e.printStackTrace();
    }finally{
      if(psts!=null){
        psts.close();
      }
      if(conn!=null){
        conn.close();
      }
    }
  }

這種方法的優點是匯入資料花費的時間會很少,6500萬條資料,用了正好1個小時。缺點是如果資料中有一大段的句子,需要對裡面的逗號,括號,反斜線等進行處理,這裡需要進行衡量,是否使用該方法。

如果正常插入,也就是使用“insert into tb (...) values(...);insert into tb (...) values(...);……”的形式,則不用處理特殊的符號,但花費的時間會很長,我測試了一下,11萬條資料大約需要12分鐘左右,匯入6500萬條資料大概要100個小時。

我們採用的是第一種方法,資料大概可以檢視就可以,對資料要求沒有那麼嚴格,節約了時間。

以上是我在往mysql中匯入大批量資料時碰到的問題,以及所想到的解決方法,如果大家有更好的解決方法,或者碰到其他的問題,希望一起討論。