1. ホーム
  2. mysql

[解決済み] MySQLとMongoDBの比較 1000回読み込み

2022-03-15 08:01:14

質問

私はMongoDbにとても期待していて、最近テストしています。MySQLにpostsというテーブルがあって、約2000万件のレコードがあり、'id'というフィールドにのみインデックスが付けられています。

MongoDBと速度を比較したいと思い、巨大なデータベースからランダムに15レコードを取得して印刷するテストを実行しました。mysqlとMongoDBでそれぞれ1,000回ずつクエリを実行しましたが、スピードに大きな差がないことに驚いています。もしかしたら、MongoDBの方が1.1倍速いかもしれません。それはとても残念なことです。私が何か間違っているのでしょうか?私のテストが完璧でないことは分かっていますが、読み込みに集中する作業に関しては、MySQLはMongoDbと同等なのでしょうか?


注意してください。

  • デュアルコア+(2スレッド)i7 cpuと4GB RAMを搭載しています。
  • MySQLに100万レコードのパーティションが20個あります。

MongoDBのテストに使用したサンプルコード

<?php
function microtime_float()
{
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
}
$time_taken = 0;
$tries = 100;
// connect
$time_start = microtime_float();

for($i=1;$i<=$tries;$i++)
{
    $m = new Mongo();
    $db = $m->swalif;
    $cursor = $db->posts->find(array('id' => array('$in' => get_15_random_numbers())));
    foreach ($cursor as $obj)
    {
        //echo $obj["thread_title"] . "<br><Br>";
    }
}

$time_end = microtime_float();
$time_taken = $time_taken + ($time_end - $time_start);
echo $time_taken;

function get_15_random_numbers()
{
    $numbers = array();
    for($i=1;$i<=15;$i++)
    {
        $numbers[] = mt_rand(1, 20000000) ;

    }
    return $numbers;
}

?>


MySQLのテスト用サンプルコード

<?php
function microtime_float()
{
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
}
$BASE_PATH = "../src/";
include_once($BASE_PATH  . "classes/forumdb.php");

$time_taken = 0;
$tries = 100;
$time_start = microtime_float();
for($i=1;$i<=$tries;$i++)
{
    $db = new AQLDatabase();
    $sql = "select * from posts_really_big where id in (".implode(',',get_15_random_numbers()).")";
    $result = $db->executeSQL($sql);
    while ($row = mysql_fetch_array($result) )
    {
        //echo $row["thread_title"] . "<br><Br>";
    }
}
$time_end = microtime_float();
$time_taken = $time_taken + ($time_end - $time_start);
echo $time_taken;

function get_15_random_numbers()
{
    $numbers = array();
    for($i=1;$i<=15;$i++)
    {
        $numbers[] = mt_rand(1, 20000000);

    }
    return $numbers;
}
?>

解決方法は?

MongoDBは魔法のように速くなるわけではありません。同じデータを保存し、基本的に同じ方法で整理し、まったく同じ方法でアクセスした場合、結果が大きく変わることは期待できません。結局のところ、MySQLとMongoDBはどちらもGPLなので、もしMongoが魔法のように優れたIOコードを持っていたら、MySQLチームはそれをコードベースに取り込めばいいだけなのです。

MongoDBが現実の世界でパフォーマンスを発揮しているのは、MongoDBがワークロードに適した別の方法でクエリーを実行できるようにしたことが大きいです。

例えば、複雑なエンティティに関する多くの情報を正規化した形で保存するデザインを考えてみましょう。この場合、MySQL(または他のリレーショナルデータベース)の何十ものテーブルを使用して、データを正規の形で保存し、テーブル間の関係整合性を確保するために多くのインデックスを使用することが容易にできます。

次に、同じ設計をドキュメントストアで考えてみましょう。関連するすべてのテーブルがメインテーブルの下位にある場合(多くの場合そうです)、エンティティ全体を1つのドキュメントに格納するようにデータをモデル化することができるかもしれません。MongoDBでは、これをひとつのドキュメントとして、ひとつのコレクションに格納できます。MongoDBはここから優れたパフォーマンスを発揮するのです。

MongoDBでは、エンティティ全体を取得するためには、以下のようなパフォーマンスが必要です。

  • コレクションのインデックス検索 (エンティティは id で取得されると仮定)
  • データベース1ページの内容を取得する(実際のバイナリjsonドキュメント)。

つまり、B-treeのルックアップと、バイナリページの読み込みです。Log(n) + 1 IOです。もしインデックスが完全にメモリに存在できるなら、1IO。

20のテーブルを持つMySQLでは、実行しなければならない。

  • ルートテーブルのインデックス検索を1回(ここでも、エンティティはidで取得されると仮定しています)
  • クラスタ化されたインデックスでは、ルート行の値がインデックスにあると仮定することができる
  • エンティティのpk値について、(できればインデックスで)20以上の範囲検索を行う。
  • これらはおそらくクラスタ化されたインデックスではないので、適切な子行が何であるかを把握した後は、同じように20以上のデータ検索を行います。

つまり、mysqlの場合、すべてのインデックスがメモリ上にあると仮定しても(20倍もあるので大変ですが)、合計で約20回の範囲ルックアップが必要なのです。

これらの範囲検索は、おそらくランダムIOで構成されています。異なるテーブルは間違いなくディスク上の異なる場所に存在し、同じテーブルの同じ範囲内の異なる行は(エンティティの更新方法などに応じて)連続していない可能性があります。

つまり、この例の場合、最終的な集計は約 20回 論理アクセスあたりの IO は、MySQL の方が MongoDB よりも多くなっています。

MongoDBはこうしてパフォーマンスを向上させる いくつかのユースケースで .