簡單的sql調優(批處理)


最近在寫一個java的爬蟲程序時,遇到了一個大量數據進行插入更新和大量數據循環查詢的問題,所以查了一下一般的調優的方式,下面主要介紹我采取的調優措施。

一 、調優思路  

先說說我采取方式的調優的思路,這樣便於理解我的選取的調優策略。

思路分析

首先我們都知道計算機存儲空間分為:寄存器、高速緩存、內存、交換區(外部存儲虛擬化)、硬盤以及其他的外部存儲。而且我們都知道從寄存器開始到硬盤讀寫速度是從快到慢依次遞減。我們訪問數據庫,一般是通過運行的代碼去訪問數據庫,運行起來的代碼所需要的數據一般會放在內存或者是在高速緩存中,而數據庫數據存放在哪?很多人會說應該存放在電腦硬盤中,但是這個只回答對了一半。個人開發,代碼和數據庫在同一個電腦上,但是如果是團隊開發喃?明顯存放在個人的電腦上不合適,一般會存放在團隊開發的服務器上硬盤上。團隊開發時,將服務器硬盤上的數據讀到自己開發電腦的內存中(自己開發測試時)或者上線后從一個數據庫服務器硬盤讀到上線服務器內存(數據庫和程序不在一個服務器上),加上數據表查詢和查詢交互的一些准備(包括一些初始化)所需要的時間將會很多。

最簡朴的sql插入、更新和查詢一般程序一條一條的鏈接數據庫進行操作,這樣耗費的時間非常恐怖。

由此引出我們調優的想法,減少與數據庫交互的次數,將多條查詢,多條插入,多條更新合並為交互一次,也就是批操作。這樣會減少很多時間。多次處理的操作交給java程序在內存中進行處理,內存中處理的速度要快上很多。

二、插入的優化(批插入)

將插入語句進行拼接,多條插入語句拼接成一條插入語句,與數據庫交互一次執行一次。

使用insert into tableName values(),(),(),()語句進行拼接然后再一次性插入。

如果字符串太長,則需要配置下MYSQL,在mysql 命令行中運行 :set global max_allowed_packet = 2*1024*1024*10

 我插入1000條的數據耗時為毫秒級別,效率提高很多。

1、下面是代碼可以便於理解:

$sql= "insert into twenty_million (value) values";
for($i=0;$i<2000000;$i++){
$sql.="('50'),";
};
$sql = substr($sql,0,strlen($sql)-1);
$connect_mysql->query($sql);

2、我是用java寫的代碼,用的是spring帶的JdbcDaoSupport類寫的dao層,所以粘一下代碼

 public void batchInsert(List<SpdrGoldEtfPostions> spdrGoldEtfPostionsList) {
    int size = spdrGoldEtfPostionsList.size();
    String sql = "insert into " + TABLE_NAME + "(" + COLUMN_WITHOUT_ID + ") values";
    StringBuffer sbf = new StringBuffer(sql);
    for (int i = 0; i < size - 1; i++) {
      sbf.append("('").append(spdrGoldEtfPostionsList.get(i).getSpdrEftId()).append("','")
              .append(spdrGoldEtfPostionsList.get(i).getSpdrEftDate())
              .append("',");
      sbf.append(spdrGoldEtfPostionsList.get(i).getTotalNetAssetValue()).append("),");
    }
    sbf.append("('").append(spdrGoldEtfPostionsList.get(size - 1).getSpdrEftId()).append("','")
            .append(spdrGoldEtfPostionsList.get(size - 1).getSpdrEftDate())
            .append("',");
    sbf.append(spdrGoldEtfPostionsList.get(size - 1).getTotalNetAssetValue()).append(")");

    sql = sbf.toString();
    this.getJdbcTemplate().update(sql);
  }

三、更新優化(批更新)

將更新語句進行拼接,多條更新語句拼接成一條更新語句,與數據庫交互一次執行一次。

1、下面是sql語句的批更新語句,提供便於理解

UPDATE book
        SET Author = CASE id 
            WHEN 1 THEN '黃飛鴻' 
            WHEN 2 THEN '方世玉'
            WHEN 3 THEN '洪熙官'
        END
    WHERE id IN (1,2,3)

2、下面java寫的spring帶的JdbcDaoSupport類寫的dao層的批更新語句

  public void batchUpdateBySpdrEftDate(List<SpdrGoldEtfPostions> spdrGoldEtfList) {
    int size = spdrGoldEtfList.size();
    String sql = "UPDATE " + TABLE_NAME + " set total_net_asset_value = CASE spdr_eft_date\n";
    StringBuffer sbf = new StringBuffer(sql);
    for (int i = 0; i < size; i++) {
      sbf.append("WHEN ").append(spdrGoldEtfList.get(i).getSpdrEftDate()).append(" THEN ")
              .append(spdrGoldEtfList.get(i).getTotalNetAssetValue()).append("\n");
    }
    sbf.append("END\n").append("WHERE spdr_eft_date IN(");
    for (int i = 0; i < size - 1; i++) {
      sbf.append(spdrGoldEtfList.get(i).getSpdrEftDate()).append(",");
    }
    sbf.append(spdrGoldEtfList.get(size - 1).getSpdrEftDate()).append(")");
    sql = sbf.toString();
    this.getJdbcTemplate().update(sql);
  }

四、查詢優化(批量查詢)

將所有的查詢都合並為一條查詢語句,然后返回一個集合,然后處理集合(最好返回的集合是有序的,這樣處理起來比較的方便,在sql語句中可以用order by 或者group by進行排序分類,順便多說一句,使用order by 和group by 的字段最好建立索引,這樣速度更快)

1、首先寫一下sql語句,便於大家理解

select * from tableName where id in (1,2,3,4) order by id

2、下面java寫的spring帶的JdbcDaoSupport類寫的dao層的批查詢語句

 public List<SpdrGoldEtfPostions> batchSelectBySpdrEtfDate(String[] spdrEtfDateArray) {
    String sql = "select * from " + TABLE_NAME;
    StringBuffer sbf = new StringBuffer(sql);
    sbf.append(" where spdr_eft_date IN(");
    for (int i = 0; i < spdrEtfDateArray.length - 1; i++) {
      sbf.append(spdrEtfDateArray[i]).append(",");
    }
    sbf.append(spdrEtfDateArray[spdrEtfDateArray.length - 1]).append(")").append(" ORDER BY spdr_eft_date");
    sql = sbf.toString();
    List<SpdrGoldEtfPostions> items = this.getJdbcTemplate().query(sql, rowMapper());
    return items;
  }

當然批量查詢你可以改變where后面的限定語句,也可以實現批量查詢,如where id <100 and id>10(這里id<100寫在前面也是優化的思路,這天語句在執行時,會先將范圍控制在100以內,然后在從99給數據中進行查詢限定,這也是優化,所以說,很多小細節都能體現優化),類似這類的也可以實現批量查詢,根據需要改變限定條件實現批量查詢。

五、刪除的優化(批量刪除)

其實看完了批量查詢的話,就可以得到一些關於sql批量刪除的想法了,無非是限定條件上動點手腳。

1、先給一下sql語句便於理解

delete from tableName where id in(1,2,3,4,5,6)

2、下面java寫的spring帶的JdbcDaoSupport類寫的dao層的批刪除語句

 public void batchDeleteBySpdrEtfDate(String[] spdrEtfDateArray) {
    String sql = "delete from " + TABLE_NAME;
    StringBuffer sbf = new StringBuffer(sql);
    sbf.append(" where spdr_eft_date IN(");
    for (int i = 0; i < spdrEtfDateArray.length - 1; i++) {
      sbf.append(spdrEtfDateArray[i]).append(",");
    }
    sbf.append(spdrEtfDateArray[spdrEtfDateArray.length - 1]).append(")");
    sql = sbf.toString();
    this.getJdbcTemplate().update(sql);

  }

和查詢同樣道理的,可以通過設定where后面的限定,來實現其他的類批刪除。

六、總結

1、首先,數據量較大的sql優化,采取的是批處理操作,減少與數據庫的交互次數

2、批處理的sql語句交給java程序去拼接,如果數據量較大時,可以考慮使用StringBuilder代替String,如果考慮線程安全可以考慮StringBuffer(或者其他安全的字符串處理類)拼接。

3、批查詢的時候獲取的集合數據建議排序,獲取有序數據,這樣便於后續java程序的處理。

4、一般的ORM框架都是用的sql語句,而一些sql語句的小的細節都能優化,使用時需要日積月累,平時應該時刻有優化意識。

5、使用過hibernate應該都知道,hibernate有緩存功能,一級二級緩存,這個思路符合我這篇博客優化思路,可以提一下,然后提供繼續優化的思路,對於一些經常操作的數據可以設置高速緩存

6、在使用sql語句的時候對於經常需要進行order by和group by的字段(列)建立索引,sql查詢避免進行全表掃描,這些在寫sql語句時需要注意。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM