1. 程式人生 > >Mysql批量寫入資料,對於這類效能問題,你是如何優化的

Mysql批量寫入資料,對於這類效能問題,你是如何優化的

測試環境
配置直接影響執行速度,先上一下測試機配置:
cpu i7 5500U(低電壓傷不起,以後再也不買低電壓的U了)
記憶體 8G ddr3 1600
php 7.1
mysql 5.5.40
開發框架 CodeIgniter 3.1.2
影響寫入效率的因素都有什麼?
資料庫引擎
開發中常用的資料庫引擎 MyISAM,InnoDB 這兩種,其他的資料庫引擎我在開發中還沒用到,所以不在這裡測試了。
先看一下庫表結構 :
test庫下有兩張表:分別為上面提的兩種引擎:
每張表結構如下(一個自增id,一個varchar型別待插入欄位):
預設狀態下對兩表插入20w資料看一下效率:
PHP程式碼如下:

 /**
 * 
 * 測試插入效率
 * 
 * @return void
 * 
 */public function insertTest(){
    set_time_limit(0);   //防止超300s 500錯誤

    $t1 = microtime(true);


    //隨機插入num條
    for ($i=1; $i<=200000; $i++){

        $result = $this->db->insert('myisam', ['value' => uniqid().$i]);
    }

    //程式執行時間
    $t2 = microtime(true);
    echo '耗時:'.round($t2-$t1,3).'秒<br>';
    echo '記憶體消耗:'.round(memory_get_usage()/1048576,2)." M<br/>";

}

20w 資料 Myisam要 接近3分鐘了。
看一下InnoDb預設狀態下執行時間:
插入1w條資料 用了 6分49秒,沒辦法等下去了, 按照這個資料量推測 6分49 * 20 = ???
後期由於資料量增多,也會影響插入效能,所以InnoDb預設狀態插入20w單欄位資料要2小時以上,無法 忍受。

業務邏輯
顯然上面的業務邏輯是有問題的,每條資料單次插入,增加了mysql的開銷,每次插入資料都要重新連 接一下mysql,肯定是相當浪費資源了。所以CI提供了 insert_batch(),批量寫入資料。Thinkphp3.2 也有addAll() 這樣的方法來支援。其他框架應該都有!
原理很簡單就是把二維陣列,拼接為sql
將單條Sql如下:
$sql = “INSERT INTO TEST (value) VALUES (‘helloworld1’)”;
$sql = “INSERT INTO TEST (value) VALUES (‘helloworld2’)”;
拼接為:
$sql = “INSERT INTO TEST (value) VALUES (‘helloworld1’), (‘helloworld2’)”;
很明顯批量插入速度要快很多。
還是20w資料,MyISAM 下批量查詢速度多快?(已有資料會影響插入效率,已清空 myisam表)

   /**
 * 
 * 測試批量插入效率
 * 
 * @return void
 * @author [email protected]
 * 
 */
public function insertTest(){
 set_time_limit(0);   //防止超300s 500錯誤

    $t1 = microtime(true);


    //隨機插入num條
    for ($i=1; $i<=200000; $i++){

        $data[$i] = ['value' => uniqid().$i];
    }
    //程式執行時間
    $t2 = microtime(true);
    echo '迴圈耗時:'.round($t2-$t1,3).'秒<br>';

    $this->db->insert_batch('myisam', $data);  //批量插入

    $t3 = microtime(true);
    echo '插入耗時:'.round($t3-$t2,3).'秒<br>';

    echo '記憶體消耗:'.round(memory_get_usage()/1048576,2)." M<br/>";

}

比起之前的167秒的單條插入速度快了 5 倍。記憶體消耗增加1.5倍左右,記憶體換時間,可取~~~
InnoDB 會是什麼速度呢?
執行結果:

這次終於執行完了,而且速度很快。和之前的兩個小時比,效率也提升了N倍。

修改配置引數提升效能:
InnoDB 引擎 真的這麼慢?這麼low?
答案顯然是:NO
InnoDB寫入之所以這麼慢的一個原因是:
innodb_flush_log_at_trx_commit

引數配置的問題
如下圖預設的值:

   當innodb_flush_log_at_trx_commit=0時, log buffer將每秒一次地寫入log file, 並且log file的flush(重新整理          到disk)操作同時進行. 此時, 事務提交是不會主動觸發寫入磁碟的操作.
   當innodb_flush_log_at_trx_commit=1時(預設), 每次事務提交時, MySQL會把log buffer的資料寫入log f          ile, 並且將log file flush(重新整理到disk)中去.
   當innodb_flush_log_at_trx_commit=2時, 每次事務提交時, MySQL會把log buffer的資料寫入log file, 但          不會主動觸發flush(重新整理到disk)操作同時進行. 然而, MySQL會每秒執行一次flush(重新整理到disk)操作.

把值設定為2,再試一下:

速度又提升了 3倍,和 MyISAM幾乎相同。
所以以後說InnoDB寫入速度慢,可能是配置問題

還可以優化?
還有什麼可以優化?
由於我們使用了 框架的insert_batch,看一下CI原始碼:

   /**
 * The "set_insert_batch" function.  Allows key/value pairs to be set for batch inserts
 *
 * @param   mixed
 * @param   string
 * @param   bool
 * @return  CI_DB_query_builder
 */
public function set_insert_batch($key, $value = '', $escape = NULL)
{
    $key = $this->_object_to_array_batch($key);


    if ( ! is_array($key))
    {
        $key = array($key => $value);
    } is_bool($escape) OR $escape = $this->_protect_identifiers;

    $keys = array_keys($this->_object_to_array(current($key)));
    sort($keys);

    foreach ($key as $row)
    {
        $row = $this->_object_to_array($row);
        if (count(array_diff($keys, array_keys($row))) > 0 OR count(array_diff(array_keys($row), $keys)) > 0)
        {
            // batch function above returns an error on an empty array
            $this->qb_set[] = array();
            return;
        }

        ksort($row); // puts $row in the same order as our keys

        if ($escape !== FALSE)
        {
            $clean = array();
            foreach ($row as $value)
            {
                $clean[] = $this->escape($value);
            }

            $row = $clean;
        }

        $this->qb_set[] = '('.implode(',', $row).')';
    }

    foreach ($keys as $k)
    {
        $this->qb_keys[] = $this->protect_identifiers($k, FALSE, $escape);
    }

    return $this;
}

我們傳入的資料,方法會再迴圈,判斷。所以建議語句自己拼接
程式碼修改如下:

/**
 * 
 * 測試插入效率
 * 
 * @return void
 * @author [email protected]
 * 
 */
public function insertTest(){
set_time_limit(0);   //防止超300s 500錯誤

    $t1 = microtime(true);


    $sql = "insert into innodb (value) VALUES";
    //隨機插入num條
    for ($i=1; $i<=200000; $i++){

        $val = uniqid().$i;

        $sql .= "('{$val}'),";

    }

    $sql = substr($sql,0,-1);

    //程式執行時間
    $t2 = microtime(true);
    echo '迴圈耗時:'.round($t2-$t1,3).'秒<br>';

    $this->db->query($sql);  //批量插入

    $t3 = microtime(true);
    echo '插入耗時:'.round($t3-$t2,3).'秒<br>';

    echo '記憶體消耗:'.round(memory_get_usage()/1048576,2)." M<br/>";

}

執行結果:

20W條資料 InnoDB 迴圈1.6秒,插入1.2秒。速度是不是很爽了。。
拼接語句可能會報錯
設定一下
max_allowed_packet = 500M
允許mysql 接受資料包大小。

相關推薦

Mysql批量寫入資料對於效能問題是如何優化

測試環境 配置直接影響執行速度,先上一下測試機配置: cpu i7 5500U(低電壓傷不起,以後再也不買低電壓的U了) 記憶體 8G ddr3 1600 php 7.1 mysql 5.5.40 開發框架 CodeIgniter 3.1.2 影響寫入效率的因素

mysql批量寫入數據時註意事項

mysql 批量寫入 SQLSTATE[HY000]: General error: 1390 Prepared statement contains too many placeholders.mysql批量寫入數據時,註意事項

mysql 批量更新數據庫主鍵為intbigint 字段為自增

span pre and style cat pri odi rem script select table_name, concat(‘alter table `‘,table_name,‘` MODIFY ‘, column_name, ‘ ‘, da

Mybatis 實現Mysql批量插入資料判斷資料是否存在

常見插入資料的SQL insert into 插入資料庫時會檢查主鍵是否存在,存在會報錯 replace into 替換資料庫記錄,需要表中有主鍵或者unique索引,如果資料庫已存在的資料,會先刪除該資料然後新增。不存在的資料效果和insert into

Redis的持久化機制包括RBD和AOF兩種對於兩種持久化方式各有優勢

plain 同步數據 pen toc 默認 ocl 好的 dfs 操作系統 RDB機制的策略 RDB持久化是指在指定的時間間隔內將內存中的數據和操作通過快照的方式保存到redis bin目錄下的一個默認名為 dump.rdb的文件,可以通過配置設置自動的快照持久化的

mysql 批量資料並且整理表碎片

DROP PROCEDURE IF EXISTS prc_del_loop;CREATE PROCEDURE prc_del_loop()BEGINDECLARE v_count INT ;DECLARE v_data_free INT;my_del_loop:LOOP delete from t_data

MYSQL 批量插入資料 insert into ON DUPLICATE KEY UPDATE

#批量插入並根據重複資料進行處理 class DF_MYSQL(DBBase):     #定義操作更新時間相關的方法     #獲取上次更新的時間     def __del__(self):       &

資料好不好學幾大步驟就懂了

很多初學者在萌生向大資料方向發展的想法之後,不免產生一些疑問, 應該怎樣入門? 應該學習哪些技術? 學習路線又是什麼?   所有萌生入行的想法與想要學習Java的同學的初衷是一樣的。崗位非常火,就業薪資比較高,,前景非常可觀。基本都是這個原因而嚮往大資料,但是對大資料卻

5.03-mysql批量更新資料的異常

來回測了幾次,複製sql到資料庫執行也沒錯。 導致的原因是:配置資料庫連線時url沒有配置allowMultiQueries=true屬性。 allowMultiQueries:允許多查詢. jdbc:mysql://localhost:3306/database?characte

JAVA使用HBase根據Rowkey批量查詢資料(一次查多條返回多個記錄)

最近有需求說是根據多個RowKey返回結果集: public static Configuration conf = null; public static Connection connection = null; public static Admin admin = nul

mybatis學習之路----mysql批量新增資料

mybatis學習之路----批量更新資料 接下來兩節要探討的是批量插入和批量更新,因為這兩種操作在企業中也經常用到。     mysql新增語句   insert into 表名(欄位,欄位。。。

關於繼承不同繼承方式對於派生成員以及基的可見性

通過繼承機制,可以利用已有的資料型別來定義新的資料型別。所定義的新的資料型別不僅擁有新定義的成員,而且還同時擁有舊的成員。我們稱已存在的用來派生新類的類為基類,又稱為父類。由已存在的類派生出的新類稱為派生類,又稱為子類。  在C++語言中,一個派生類可以從一個基類派生,也可以從多個基類派生。從一個基類派生的繼

MySQL批量刪除資料指令碼

#!/usr/bin/python# -*- coding: UTF-8 -*-import osimport MySQLdbimport timedb=MySQLdb.connect(host="172.16.32.11",user="a",passwd="root1123

Mysql批量刪除資料

update renyuan_gongzuojinglibiao shanChuBiaoZhi = 0, when id=#{item.id} then #{item.shanChuShiJian} where id in #{item.id}

php mysql 批量更新資料

使用原生 sql 批量更新使用者積分資料訂單列表中查出 $list,包含  buy_id , total_money 欄位                   $list = [ 0 => ['buyer_id' => 1,'

mysql批量遷移資料至redis

例子是使用hset,為什麼呢,因為考慮到便於刪除。 mysql_to_reds的檔案內容是這樣的: SELECT CONCAT("*4\r\n",  '$', LENGTH(redis_cmd), '\r\n',  redis_cmd, '\r\n',  '$', L

python向mySQL批量插入資料的方法

通過呼叫mySQLdb python庫中的 cursor.executemany()函式完成批量處理。 今天用這個函式完成了批量插入 例程: def test_insertDB():     conn = database.Connection(host=options.mysql_host, databas

mysql 批量插入資料(INNODB)優化

innodb的主要優化 init_connect='SET autocommit=0'    //關閉自動提交,這個對於innodb來說,很重要    innodb-file-per-table=1            //使用獨立表空間    innodb-open-f

Sqlite3批量寫入資料到資料庫中

1、建立或者開啟資料庫 int nResult = sqlite3_open(strDBPath.c_str(),&pDb); if (SQLITE_OK != nResult) { sqlite3_close(pDb); pDb = NULL;

關於JDBC+MySQL批量寫入: 用insert values方式批量寫入

恩,萌新剛來,聽學長說寫部落格可以總結梳理自己的知識,所以來試試,自娛自樂,不喜莫噴。目前還是大二狗,學Java半年多,錯誤很多,望大神指正。 應為經常要寫入大量資料所以做了一個批量寫入測試,這篇文章是為另一篇一個6分鐘爬去8萬條資料的多執行緒Java爬蟲中的批量寫入做說明