PHP使用Sqlite3批量插入调优

版权声明:转载请注明来源 https://blog.csdn.net/u013702678/article/details/84931475

工作中用到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

从这里可以看出第四种方案是性能最好的。

猜你喜欢

转载自blog.csdn.net/u013702678/article/details/84931475