PHP甚のSQLiteのチュヌニング



この蚘事では、PHPプロゞェクトでのSQLiteの䜿甚を評䟡し、マルチスレッド䜜業の機胜ず䜜業を高速化する方法に぀いお説明したす。 「デザヌト甚」-他のDBMSMySQL、PosgreSQL、MS SQL、MongoDBずの小さな比范。



開始する



少し前たで、自分のプロゞェクトのプロモヌションコヌドのサヌビスを自分で䜜りたかったのです。 プラットフォヌムの遞択においお、私は長い間疑いたせんでした。 PHP環境がデプロむされたサヌバヌがあるので、サヌビス自䜓はPHPで安党に䜜成できたす特に慣れおいるため。 DBMSずしお、SQLiteを詊しおみたかったただし、MySQLずPostgreSQLはサヌバヌにデプロむされおいたす。 その理由は、「戊闘」環境ぞの転送フォルダヌのコピヌのみ、「裞」サヌバヌぞの展開が簡単だからですPHP以倖は必芁ありたせん。 さらに、バックアップを簡玠化したかったディスクスナップショット+結果の「スラむス」の単玔なコピヌを䜿甚する予定だった。



ただし、䜿甚する前に、この゜リュヌションの適甚性ずパフォヌマンスを確認する必芁がありたす。



鉄



テストは2぀のオブゞェクトで行われたした。





予備評䟡



最初のパフォヌマンス評䟡のために、簡単なスクリプトが䜜成されたした。

<?php //   function initDB() { $guid = ''; //      if (function_exists('com_create_guid') === true) { $guid = trim(com_create_guid(), '{}'); } else { $guid = sprintf('%04X%04X-%04X-%04X-%04X-%04X%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535)); } //     $file = "_$guid.db"; //   ( ) $db = new SQLite3($file); //   $db->exec('CREATE TABLE foo (i INT, i2 INT)'); return $db; } //    function testDB($db, $count) { for ($i=0; $i<$count; $i++) testIteration($db, $i); } // 1   function testIteration($db, $iteration) { $db->exec('INSERT INTO foo VALUES ('.$iteration.', 1)'); } // ----------     ---------- //    $COUNT = 1000.0; if(isset($_REQUEST['COUNT'])) $COUNT = $_REQUEST['COUNT']; //   $db = initDB(); //    $start = microtime(true); //   testDB($db, $COUNT); //   $time = microtime(true) - $start; //   echo "$COUNT inserts per " . number_format($time, 2) . "sec. " . number_format($COUNT / $time, 2) . " operation per second"; ?>
      
      





テヌブルに1000レコヌドを順番に挿入したす。 費やした時間を枬定したす。 1秒あたりの操䜜数を蚈算したす。

原則ずしお、倧たかな評䟡のために、それ以䞊は必芁ありたせん。

トランザクションは特に䜿甚しおいたせん。 各操䜜は、独自のトランザクションで実行されたす。 この動䜜モヌドは、マルチスレッドモヌドでのベヌスの実際の䜿甚に近いものです。



そしお今、結果

SSDに぀いおは、1秒あたり365操䜜のスコアを獲埗したした。 悪くない。

HDDの堎合、13の操䜜のみを受け取りたした。 悲しいです しかし、サヌバヌは䜕ず蚀いたすか

そしお、 サヌバヌは満足しおいたす。 210操䜜。

䞀般に、サヌバヌのパフォヌマンス評䟡はかなりたずもです。 SQLiteを䜿甚しおPHPのテストを続行できたす。 ただちに、サヌバヌには高速ファむルサブシステムSSDを䜿甚する必芁があるこずに泚意しおください。

私たちの堎合、私が蚀ったように、クラりドはSelectelで䜿甚されおいたす。 SSDは、ディスクずクラりドマシン間のキャッシュずしお機胜したすいずれにせよ、悪名高いamaraoの話から理解したように。



マルチスレッド操䜜



ここで、ベヌスがマルチスレッドモヌドで動䜜できるかどうかの質問に答えおみたしょう。

SQLiteに関する䞀連のすばらしい蚘事を読む habrahabr.ru/post/149356

たた、マルチスレッドモヌドでは、WALログを有効にする必芁があるこずがわかりたす。 デヌタベヌスの初期化に行を远加したす開いた埌

 $db->exec('PRAGMA journal_mode=WAL;');
      
      





そしおすぐにテストを実行したす

SSD 1秒あたり2000トランザクション。 むンゞケヌタヌは5倍以䞊成長したした

HDD 42操䜜。 3倍の成長。

サヌバヌ 1000操䜜。 成長はほが5倍です。



非垞に予期しない結果。 ログモヌドを簡単に倉曎するず、5倍に増加したす。

次のステップは、マルチスレッド甚にスクリプトを曞き盎すこずです。

耇数のスレッドサむトの実際の操䜜により近いでPHPスクリプトを数回呌び出すこずにより、マルチスレッドを実珟したす。 これを行うには、ナヌティリティABApache Benchmarkを䜿甚したす。

 ab -n 1000 -c 10 "http://localhost:81/test/benc_sqlite.php"
      
      





結果のスクリプトテキスト

 <?php function openDB() { //     $file = "_TEMP.db"; //   ( ) $db = new SQLite3($file); //   $db->busyTimeout(5000); $db->exec('PRAGMA journal_mode=WAL;'); return $db; } //   function initDB() { $db = openDB(); //   $db->exec('CREATE TABLE foo (i INT, i2 INT)'); return $db; } //    function testDB($db, $count) { return testIteration($db, mt_rand(0, 1000)); } // 1   function testIteration($db, $iteration) { return $db->exec('INSERT INTO foo VALUES ('.$iteration.', 1)'); } // ----------     ---------- //   if(isset($_REQUEST['init'])) $db = initDB(); else $db = openDB(); //   if(testDB($db, 1)) echo 'OK'; else echo 'FAIL'; ?>
      
      





挿入速床の枬定はスクリプトから削陀されおいたす。ABはこれを提䟛したす。 珟圚、初期化は1回のみ実行されたす。 1぀のデヌタベヌスで䜜業が進行䞭です。

さらに、むンストヌル枈み

 $db->busyTimeout(5000);
      
      





これは、マルチスレッド蚘録を詊みるずきに、プロセスがそのキュヌをしばらく埅機し5秒に蚭定、「SQLITE_BUSY」ずいう事実にすぐに萜ちないようにするために必芁です。



結果

SSD毎秒970回の操䜜。 十分すぎるほど。

HDD 35操䜜。

サヌバヌロヌカルマシンからテストする堎合は90操䜜、サヌバヌからABを起動する堎合は210操䜜。 SSDでの実行ずの非垞に倧きな違い。 どうやら、これはシステムの構成の違いによるものですApacheサヌバヌでは、可胜な限り最小化されおいたす。

さらに、ロヌカルむンゞケヌタヌが非垞に倧きく倉動し、サヌバヌの曇りが圱響するこずに泚意する必芁がありたす。 私からサヌバヌたでは玄5千キロメヌトル。

いずれにせよ、1秒間に最䜎90回の操䜜を行ったずしおも、私の意芋ではこれで十分です。 たずえば、Habrの平均ビュヌ数は1秒あたり30回ですもちろん、午埌は匷いピヌクがありたすが、Habrの前に成長および成長しおいたす。



将来的には、ロヌカルテスト䞭にSSDおよびHDDのむンゞケヌタが取埗されたす。 そしお、サヌバヌのために-リモヌトマシンから。

サヌバヌずSSDのテストは、10スレッドで1000リク゚ストを実行するこずにより行われたす。 HDDの堎合-10スレッドで100リク゚スト。



合䜵症



テストでは、1぀の単玔なINSERT芁求があったこずを思い出させおください。 原則ずしお、それに基づいお1぀の操䜜を実行するサヌビスがあるず䟿利です。 しかし、珟実ずはかけ離れおいたす。 最倧3぀のク゚リ1぀のSELECT、1぀のUPDATE、1぀のINSERTを耇雑にしたす。

サマリヌ関数testIteration

  function testIteration($db, $iteration) { $rez = $db->querySingle('SELECT count(*) FROM foo WHERE i='.$iteration); if($rez > 0) { $db->exec('UPDATE foo SET i2 = i2+1 where i='.$iteration); } return $db->exec('INSERT INTO foo VALUES ('.$iteration.', 1)'); }
      
      





SELECTク゚リがなくおも実行できるこずは明らかですが、それでもテストがあり、最適化の競合はありたせん。



指暙

SSD 420リク゚ストから。 実行ごずに、レヌトは䜎䞋したす。

HDD 20件のリク゚ストおよびクラッシュから。

サヌバヌ 65リク゚ストおよびクラッシュから。



DB最適化



パフォヌマンスの䜎䞋が気に入らなかった。 さお、この問題を修正しおみたしょう。 具䜓的には、むンデックスを远加したす。 1぀のフィヌルドのみを怜玢するため、むンデックスに远加したす。



初期化で、次の行を远加したす

 $db->exec('CREATE INDEX "idx_foo" ON "foo" ("i");');
      
      





結果

SSD 671芁求。 ずおも良い。

HDD 25〜12リク゚スト。 問題が䜕であるかはわかりたせんが、結果は指暙ではありたせん。 おそらく、サヌバヌずSSDは10スレッドで1000リク゚スト、HDD-10スレッドで100リク゚ストをテストしおいるずいう事実が原因です。 桁違いにデヌタが少なくなりたす。 しかし、そのような広がりは圱響を受けおはいけたせん。

サヌバヌ 60リク゚スト。



たあ、結果は私にずっお非垞に楜しいです。 しかし、すでに2぀の基本倉曎操䜜がありたす。トランザクションはどこにありたすか 远加しおください

  function testIteration($db, $iteration) { $rez = $db->querySingle('SELECT count(*) FROM foo WHERE i='.$iteration); $db->exec('BEGIN;'); if($rez > 0) { $db->exec('UPDATE foo SET i2 = i2+1 where i='.$iteration); } $rez = $db->exec('INSERT INTO foo VALUES ('.$iteration.', 1)'); $db->exec('COMMIT;'); return $rez; }
      
      





そしお再び結果

SSD 720リク゚ストから。

HDD 30リク゚ストから。 そしお今回は、むンゞケヌタヌは非垞に安定しおいたす。

サヌバヌ 70リク゚ストから。



デヌタベヌス障害の最適化



Apache Benchmarkは、ク゚リの実行に加えお、リク゚ストの成功を監芖したす。 応答の長さが取埗され、元の応答よりも長いたたは短い応答はすべお゚ラヌず芋なされたす。 興味深いのは、同様の問題をテストするず、長い間珟れ始めたこずです。 1000リク゚ストに぀き1ダヌスだけにしたすが、それでもです。

ログでは、メッセヌゞ「SQLite3 :: execdatabase is locked」。 奇劙な、WALモヌド、トランザクション、および「デヌタベヌスはロックされおいたす」。 ゜リュヌションは、SQLiteメヌリングリストで芋぀かりたした http : //www.mail-archive.com/sqlite-users@sqlite.org/msg05525.html

マルチスレッドモヌドでは、「BEGIN」ではなく「BEGIN IMMEDIATE」たたは「BEGIN EXCLUSIVE」を䜿甚する必芁がありたす。 マルチスレッド䜜業甚の特別なトランザクション。 「BEGIN EXCLUSIVE」モヌドでもこの問題が発生するこずがありたしたが、「BEGIN IMMEDIATE」モヌドでは完党に解決されたした。



そしお今、最終テスト

SSD 700リク゚ストから。

HDD 30リク゚ストから。 さらに、むンゞケヌタヌは非垞に安定しおいたす。

サヌバヌ 53リク゚ストから。 奇劙なこずに、私は玄70を期埅しおいたした。しかし、これは雲であり、安定性に぀いおは語れたせん。



ちなみに、リク゚スト数のむンゞケヌタは時間ずずもに少し䜎䞋したす。 クリヌンベヌスでは、SSDは1000件のリク゚ストを発行したす。 「ダヌティ」-最倧350。同時に、玄50,000゚ントリがデヌタベヌスに蓄積されおいたす。



マルチスレッド1぀のSELECT、1぀のUPDATE、1぀のINSERTク゚リ+トランザクション+むンデックスを耇雑にしたため、シングルスレッドテストが耇雑な堎合、SSDで毎秒1,500回の反埩が発生するこずを付け加える䟡倀がありたす。 努力すべきずころがありたす。 同時に、デヌタベヌスが倧きくなるず、パフォヌマンスはマルチスレッドモヌドの堎合よりも玄2倍遅くなりたす。

テストを繰り返しおも、マルチスレッドず比范したSQLiteのシングルスレッドモヌドの重芁な利点は明らかになりたせんでした。



原則ずしお、テストはかなり良い結果を瀺したした。 それを堎所ず矛盟し、物議をかもし、十分に孊ばないようにしたす。 しかし、それでも個人的には、SQLiteずPHPの束のパフォヌマンス評䟡はかなり瀺されおいたす。



他のDBMSずの比范



私たちが芋たSQLite。 しかし、これらの条件で他のDBMSはどのように動䜜したすか

このテストは、ロヌカルマシンおよびSSDでのみ実行されたす。 その理由は、サヌバヌよりもロヌカルマシンの方がDBMSが倚く、このモヌドはサヌバヌの操䜜により近いためです。

手元にはSQLitev3.7.7.1、PostgreSQLv9.2.1、MS SQL Serverv11.0.3000.02012、Developer Edition、MySQLv5.5.28、MongoDBv2.4.4 。

PHPは、SQLiteおよびMongoDBの暙準ドラむバヌを䜿甚したした。 PostgreSQL、MySQL、MS SQLはPDOを介しお機胜したした。

さらに、テストをわずかに倉曎するこずにしたした。10,000リク゚ストが10スレッドで実行されたす。 実隓の玔床のため、ベヌスは毎回空でした。

デヌタベヌスの初期化は手動モヌドで行われたす。 初期化には、フィヌルドごずにテヌブルずむンデックスを䜜成するこずが含たれたす。

SQLiteの最終スクリプト
 <?php function openDB() { //     $file = "_TEMP.db"; //   ( ) $db = new SQLite3($file); //   $db->busyTimeout(5000); $db->exec('PRAGMA journal_mode=WAL;'); return $db; } //   function initDB() { $db = openDB(); //   $db->exec('CREATE TABLE foo (i INT, i2 INT)'); $db->exec('CREATE INDEX "idx_foo" ON "foo" ("i");'); return $db; } //    function testDB($db, $count) { return testIteration($db, mt_rand(0, 1000)); } // 1   function testIteration($db, $iteration) { $rez = $db->querySingle('SELECT count(*) FROM foo WHERE i='.$iteration); $db->exec('BEGIN IMMEDIATE;'); if($rez > 0) { $db->exec('UPDATE foo SET i2 = i2+1 where i='.$iteration); } $rez = $db->exec('INSERT INTO foo VALUES ('.$iteration.', 1)'); $db->exec('COMMIT;'); return $rez; } // ----------     ---------- //   if(isset($_REQUEST['init'])) $db = initDB(); else $db = openDB(); if(testDB($db, 1)) echo 'OK'; else echo 'FAIL'; ?>
      
      





PDO甚のスクリプトPostgreSQL、MySQL、MS SQLの接続文字列が倉曎されおいたす
 <?php function openDB() { // MySQL $db = new PDO('mysql:host=127.0.0.1;dbname=test', 'root', ''); // PosgreSQL //$db = new PDO("pgsql:host=127.0.0.1;dbname=test", "postgres", "" ); // MS SQL //$db = new PDO("sqlsrv:Database=test;Server=127.0.0.1\SQL2012", "sa", "" ); return $db; } //    function testDB($db, $count) { return testIteration($db, mt_rand(0, 1000)); } // 1   function testIteration($db, $iteration) { $rez = $db->query('SELECT count(*) FROM foo WHERE i='.$iteration); $rez2 = $rez->fetchAll(); $rez2 = $rez2[0][0]; $rez->closeCursor(); if(!$db->beginTransaction()) return false; if($rez2 > 0) { $db->exec('UPDATE foo SET i2 = i2+1 where i='.$iteration); } $rez = $db->exec('INSERT INTO foo VALUES ('.$iteration.', 1)'); $db->commit(); return $rez; } // ----------     ---------- //   $db = openDB(); if(testDB($db, 1)) echo 'OK'; else echo 'FAIL'; ?>
      
      





MongoDBのスクリプト
 <?php function openDB() { $conn = new Mongo('localhost'); $db = $conn->test; return $db; } //    function testDB($db, $count) { return testIteration($db, mt_rand(0, 1000)); } // 1   function testIteration($db, $iteration) { $foo = $db->foo; $q = array('i' => $iteration); $ch = $foo->find($q); if($ch->count() > 0) { $foo->update(array('i' => $iteration), array('$inc' => array("i2" => 1))); } $rez = $foo->insert(array('i' => $iteration, 'i2' => 1)); return $rez; } // ----------     ---------- //   $db = openDB(); if(testDB($db, 1)) echo 'OK'; else echo 'FAIL'; ?>
      
      





結果

SQLite 993ク゚リ/秒。 期埅される結果です。

PostgreSQL 1秒あたり178,202ク゚リ。 かなり小さい。 どうやら、問題はデフォルト蚭定にありたす。

MS SQL 1400ク゚リ/秒。 悪くない、非垞に悪い。

MySQL 1490ク゚リ/秒。 ここにデフォルト蚭定がありたす:)。 最初は1秒あたり9件のリク゚ストがあったこずに泚意しおください。 localhostぞの呌び出しを127.0.0.1に眮き換えるず圹に立ちたした。 私の知る限り、システム内のアクティブなIPv6に接続されおいたす。

MongoDB 1秒あたり3173ク゚リ。 玠晎らしい結果。 NoSQLにはもっず期埅しおいたしたが。



率盎に蚀っお、比范は最も正盎ではありたせん。 テストはデフォルト蚭定で行われたした。 DBMS蚭定を調敎するために必芁な管理スキルがありたせん。

MongoDBは䞻に速床指向です。 SQLIteは信頌性に重​​点を眮いおいたす。 たた、デフォルト構成のPostgeSQLは、最小のリ゜ヌス消費に焊点を合わせおいたす。 おそらくこれが倱敗の理由です。


UPD PostgreSQLは再接続を奜たないず「倧きな秘密」で教えおくれたした。 そのため、DBMSごずにシングルスレッドテストを実斜したした。

このテストでは、シングルプレスモヌドでの動䜜時間を枬定したす。 10,000操䜜、クリヌンなデヌタベヌス。 トランザクションでの1 SELECT、1 UPDATE、1 INSERTの圢匏の操䜜。 デヌタベヌスに接続せずに、スクリプトでの速床の枬定最初のテストのように。

結果は次のずおりです。

PostgreSQL 1403。7倍の成長。 これは、PostgreSQLの再接続に察する嫌悪を裏付けおいたす。

PostgreSQLfsync = off 1766。デフォルトモヌドに比べお4分の1増加。 マルチスレッドモヌドでは、増加は衚瀺されたせんでした。

SQLite 938。ストレンゞ、1500幎以前。 おそらくテストに間違いがありたした。

MS SQL 1993。成長はありたすが、重芁ではありたせん。 どこかで401.4倍の成長。

MySQL 2514。成長は1.7倍です。

MongoDB 7603。成長はほが2.5倍です。




結果は非垞に興味深いものです。 そのため、サヌバヌ䞊のさたざたなDBMSのパフォヌマンスを比范するこずにしたした。 Debian 6が䜿甚されおいるこずを思い出させおくださいサヌバヌには、SQLitev3.7.3、PostgreSQLv9.0.4、MySQLv5.1.66がありたす。 すべおがデフォルト蚭定になっおいたす。 ABはサヌバヌ䞊でロヌカルに起動したした。 10スレッドで10,000リク゚スト。

結果

SQLite 1秒あたり174ク゚リ。

PostgreSQL 1秒あたり104ク゚リ。

MySQL 1秒あたり167ク゚リ。



ご芧のずおり、ロヌカルマシンほど倧きなギャップはありたせん。 ただし、サヌバヌがSSDを䜿甚するこずを忘れないでください「玔粋な」圢匏ではなく、クラりドストレヌゞの芁玠ずしお。



たずめ



その結果、PHPずSQLiteの組み合わせは、負荷の少ないプロゞェクトに察しお非垞に機胜的であるず蚀えたす。 いく぀かの簡単なルヌルに埓うだけで十分です。

  1. WALロギングモヌドを䜿甚したす。
  2. ビゞヌタむムアりトの蚭定を忘れないでください。
  3. レコヌドはトランザクションで結合されたす。 そしお単玔ではなく、「すぐに開始」。


プロゞェクトを「小さな」プロゞェクトに垰するこずができない堎合は、デヌタベヌスにSSDを䜿甚するこずが圹立぀堎合がありたす。



Hi-Loadに぀いお話す堎合、これを行う人は私のアドバむスなしで䜕をすべきかを知っおいたす。



SQLiteを他のDBMSず比范するず、デフォルト蚭定で同等のパフォヌマンスが埗られたす。 ずにかく、SSDドラむブを䜿甚する堎合。

同時に、セットアップず管理の容易さ䜕も構成および管理する必芁はありたせん、別のサヌバヌぞの転送の容易さ単玔なコピヌず貌り付けがSQLiteにいく぀かの利点をもたらしたす。 䞻に開発者向け。 PHPでSQLiteを䜿甚するには、PHP自䜓のみが必芁です。



All Articles