PHP使用Sqlite3批量插入調優
阿新 • • 發佈:2018-12-28
工作中用到SQLite,簡單好用,但是存在多個程序寫同一個db檔案時的鎖問題,報database is locked錯誤導致插入失敗。
業務中存在批量插入的場景,這裡按之前MySQL的優化思路優化了SQLite的插入,下面是一些測試用例。
下面的測試用列是在我虛擬機器上執行的,絕對資料沒什麼意義,可以對比看各個方案的執行效率。
<?php class TestSqlite { private $db = null; const TABLE_NAME = "t_test"; const NUM = 500; public function __construct($file_data) { $this->db = new \SQLite3($file_data); if (!$this->db) { throw new \Exception("SQLite3 construct failed,err:" . $this->db->lastErrorMsg()); } $this->init(); } private function init() { $table_name = self::TABLE_NAME; $sql =<<<EOF CREATE TABLE IF NOT EXISTS $table_name (id INTEGER PRIMARY KEY AUTOINCREMENT, type int NOT NULL DEFAULT 0, action int NOT NULL DEFAULT 0, data TEXT NOT NULL, create_time datetime NOT NULL ); EOF; $ret = $this->db->exec($sql); if(!$ret) { echo("insert failed,err:". $this->db->lastErrorMsg().PHP_EOL); } } public function test($callable) { $this->db->exec("delete from ".self::TABLE_NAME); $tickStart = microtime(true); call_user_func_array($callable,[]); echo "cost ".(microtime(true)-$tickStart)." ms".PHP_EOL; } public function forInsert() { for($i=0;$i<self::NUM;$i++) { $ret = $this->db->exec("INSERT INTO ".self::TABLE_NAME."(id,type,action,data,create_time) VALUES(NULL,".$i.",".$i.",".$i.",datetime('now','localtime'))"); if(!$ret) { echo("insert failed,err:" . $this->db->lastErrorMsg().PHP_EOL); } } } public function forTrans() { $this->db->exec("begin;"); for($i=0;$i<self::NUM;$i++) { $ret = $this->db->exec("INSERT INTO ".self::TABLE_NAME."(id,type,action,data,create_time) VALUES(NULL,".$i.",".$i.",".$i.",datetime('now','localtime'))"); if(!$ret) { echo("insert failed,err:" . $this->db->lastErrorMsg().PHP_EOL); } } $this->db->exec("commit;"); } public function forSync() { $this->db->exec("PRAGMA synchronous = OFF;"); for($i=0;$i<self::NUM;$i++) { $ret = $this->db->exec("INSERT INTO ".self::TABLE_NAME."(id,type,action,data,create_time) VALUES(NULL,".$i.",".$i.",".$i.",datetime('now','localtime'))"); if(!$ret) { echo("insert failed,err:" . $this->db->lastErrorMsg().PHP_EOL); } } $this->db->exec("PRAGMA synchronous = ON;"); } public function forBind() { $bind_sql = "INSERT INTO ".self::TABLE_NAME."(id,type,action,data,create_time) VALUES(?,?,?,?,?)"; $rs = $this->db->prepare($bind_sql); for($i=0;$i<self::NUM;$i++) { $rs->reset(); $rs->bindValue(1,$i,SQLITE3_INTEGER); $rs->bindValue(2,$i,SQLITE3_INTEGER); $rs->bindValue(3,$i,SQLITE3_INTEGER); $rs->bindValue(4,$i,SQLITE3_TEXT); $rs->bindValue(5,$i); } $rs->execute(); } } $s = new TestSqlite("/tmp/test.db"); $s->test(array($s,"forInsert")); $s->test(array($s,"forTrans")); $s->test(array($s,"forSync")); $s->test(array($s,"forBind"));
cost 1.5027620792389 ms
cost 0.014527082443237 ms
cost 0.036828994750977 ms
cost 0.0043308734893799 ms
從這裡可以看出第四種方案是效能最好的。