ファイル vs SQLite vs MariaDB パフォーマンス比較

背景

掲示板・SNS・チャットサービスのような新しいサービスを作ろうと思っています。
しかし、このシステムのデータの保管方法に悩んでいます。
このシステム設計にミスがあるとコストの肥大化が止められなくなるためです。

類似サービスのシステム構成などを調査したりもしましたが、パフォーマンスやロックの問題が発生する可能性があり難しい判断を迫られています。
そこで、類似サービスから考慮するのではなく、ゼロから候補としているデータ保管方法のパフォーマンスを調査することにしました。

候補は次の通りです。他のデータベースはシェア・情報量などの観点で選ばないこととします。
・テキストファイル
・SQLite (ファイルベースDB)
・MariaDB (リレーショナルDB)

環境

Client:Conoha VPS 1Core/512MB
 リクエスト送信用

Server:Conoha VPS 1Core/512MB
 1台のフロントにnginx、バックにnode.js/expressのサーバー

検証

ファイルベース

まずはファイルベースのシステムを検証していきます。
データをテキストファイルで管理するレガシーな手法になります。

プログラムコード

node.js / express のコードは次のようなものです。
書き込み時は「test.txt」に追記、読み込み時は「test.txt」を送信するシンプルなプログラムです。

const fs = require('fs');
router.post('/write_file', function (req, res, next) {
  const text = req.body.text;
  fs.appendFileSync(__dirname +'/test.txt', text);
  res.send("書き込み完了")
});
router.get('/read_file', function(req, res, next) {
  res.sendFile(__dirname +'/test.txt')
});

Write Only

apache bench でファイルへの書き込みを試します。
同時コネクション数100、総リクエスト数10000です。(今後も同様)

※ IPが出ていますが、試験後にサーバーは削除しているので問題ありません。

[root@118-27-34-65 ~]# ab -c 100 -n 10000 -p /root/postfile -T "application/x-www-form-urlencoded" http://118.27.4.116:3001/write_file
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 118.27.4.116 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        
Server Hostname:        118.27.4.116
Server Port:            3001

Document Path:          /write_file
Document Length:        58 bytes

Concurrency Level:      100
Time taken for tests:   11.369 seconds
Complete requests:      10000
Failed requests:        0
Non-2xx responses:      10000
Total transferred:      2770000 bytes
Total body sent:        1920000
HTML transferred:       580000 bytes
Requests per second:    879.62 [#/sec] (mean)
Time per request:       113.686 [ms] (mean)
Time per request:       1.137 [ms] (mean, across all concurrent requests)
Transfer rate:          237.94 [Kbytes/sec] received
                        164.93 kb/s sent
                        402.87 kb/s total

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1    4   3.0      3      36
Processing:    12  110  24.6    105     244
Waiting:        6   83  23.6     82     174
Total:         13  113  25.3    108     255

Percentage of the requests served within a certain time (ms)
  50%    108
  66%    117
  75%    126
  80%    131
  90%    143
  95%    157
  98%    184
  99%    198
 100%    255 (longest request)

結果は、秒間 879.62リクエストとなりました。

Read Only

次にWriteで書き込んだデータを読み込みます。
条件は同じです。

[root@118-27-34-65 ~]# ab -c 100 -n 10000 http://118.27.4.116:3001/read_file
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 118.27.4.116 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        
Server Hostname:        118.27.4.116
Server Port:            3001

Document Path:          /read_file
Document Length:        80008 bytes

Concurrency Level:      100
Time taken for tests:   69.071 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      803010000 bytes
HTML transferred:       800080000 bytes
Requests per second:    144.78 [#/sec] (mean)
Time per request:       690.715 [ms] (mean)
Time per request:       6.907 [ms] (mean, across all concurrent requests)
Transfer rate:          11353.30 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   1.7      1      35
Processing:     4  635 1055.6    233   16217
Waiting:        1   61 285.9      7   13111
Total:          5  636 1055.5    234   16218

Percentage of the requests served within a certain time (ms)
  50%    234
  66%    453
  75%    840
  80%    870
  90%   1672
  95%   2301
  98%   3538
  99%   4581
 100%  16218 (longest request)

結果は、秒間 144.78 リクエストとなりました。
読み込みは、書き込みよりもかなり遅い結果です。

読み込みの方が時間がかかっている原因は、おそらくデータサイズの違いと考えられます。
読み込みのデータサイズは約80KBですが、書き込みは58byteのためです。

ファイルベースの場合、仕様上はこれで良いのですが考慮事項ではあります。

Read & Write

次に、Siegeを使ってRead/Writeを同時に実施してみます。
同時コネクション数100、30秒間実施します。
ファイルのデータは初期化して実施します。

http://118.27.4.116:3001/write_file POST category=test&text=hogehoge

http://118.27.4.116:3001/read_file

[root@118-27-34-65 ~]# siege -f urls.txt -c 100 -t 30s
...
...
Lifting the server siege...
Transactions:                  18556 hits
Availability:                 100.00 %
Elapsed time:                  29.48 secs
Data transferred:             327.33 MB
Response time:                  0.15 secs
Transaction rate:             629.44 trans/sec
Throughput:                    11.10 MB/sec
Concurrency:                   95.33
Successful transactions:       18556
Failed transactions:               0
Longest transaction:            9.57
Shortest transaction:           0.00

Failは無し
ファイルのロックも発生しませんでした。
しかし、最大で9.57秒かかるリクエストもあり、スケーリングが難しいシステムとなりかねないファイルベースでは最大値が抑制されるのは懸念点と言えるかもしれません。

ファイルベース 末尾

最新情報のみを読み込みたい場合もあります。
その場合は、ファイルの末尾を読み込むことになります。
この試験も行っておきます。

プログラムコード

末尾を読み込むシステムとして read-last-lines を使用しました。
https://www.npmjs.com/package/read-last-lines

const readLastLines = require('read-last-lines');
router.get('/read_file_limit', async function (req, res, next) {
  const lastLine = await readLastLines.read(__dirname + '/test.txt', 100)
  res.send(lastLine)
});

StackOverFlowには他にもいくつかのアイディアがあります。
https://stackoverflow.com/questions/40107433/read-last-line-of-a-large-file-with-nodejs

Read

[root@118-27-34-65 ~]# ab -c 100 -n 10000 http://118.27.4.116:3001/read_file_limit
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 118.27.4.116 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        
Server Hostname:        118.27.4.116
Server Port:            3001

Document Path:          /read_file_limit
Document Length:        900 bytes

Concurrency Level:      100
Time taken for tests:   94.700 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      11020000 bytes
HTML transferred:       9000000 bytes
Requests per second:    105.60 [#/sec] (mean)
Time per request:       947.000 [ms] (mean)
Time per request:       9.470 [ms] (mean, across all concurrent requests)
Transfer rate:          113.64 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1    2   1.7      2      27
Processing:    44  944  78.9    936    1153
Waiting:       38  927  79.2    923    1150
Total:         45  946  78.8    938    1153

Percentage of the requests served within a certain time (ms)
  50%    938
  66%    975
  75%   1003
  80%   1016
  90%   1058
  95%   1100
  98%   1110
  99%   1117
 100%   1153 (longest request)

秒間リクエスト数は、105.60 となりました。
全体を読み込む場合は 144.78 でしたので3割減といったところでしょうか。
やはりというか、ファイルの末尾を読み込むシステムの場合は読み込み速度が大幅に落ちます。

ただ、read-last-lines のアルゴリズムが良いのか思ったよりは落ちないなという印象です。
調べてみると、どうやら本当にファイルの末尾から読み込みながら改行の数を数えて返しているようです。先頭からReadLineしていない分で速いのでしょう。

Read & Write

[root@118-27-34-65 ~]# cat urls_file2.txt

http://118.27.4.116:3001/write_file
POST category=test&text=hogehoge
http://118.27.4.116:3001/read_file
[root@118-27-34-65 ~]# siege -f urls.txt -c 100 -t 30s ... ... Lifting the server siege... Transactions: 5300 hits Availability: 100.00 % Elapsed time: 29.14 secs Data transferred: 2.28 MB Response time: 0.53 secs Transaction rate: 181.88 trans/sec Throughput: 0.08 MB/sec Concurrency: 96.87 Successful transactions: 5300 Failed transactions: 0 Longest transaction: 1.22 Shortest transaction: 0.00

順当な結果ですね。やはり遅いです。

SQLiteベース

プログラムコード

node.js / express のプログラムコードは次のように組んでみました。
SQLiteのデータベース「test.sqlite」にはtestテーブルがあります。
書き込み時はtestテーブルに追記、読み込み時はtestテーブルからレコードを全件取得し送信するプログラムです。

-- SQLite
CREATE TABLE test (
    id integer NOT NULL PRIMARY KEY AUTOINCREMENT, 
    category text NOT NULL,
    text text NOT NULL
);

router.post('/write_sqlite', async function (req, res, next) {
  const text = req.body.text;
  const result = await insert_sqlite_db(text)
  res.send("書き込み完了")
});
router.get('/read_sqlite', async function (req, res, next) {
  const result = await get_sqlite_db_all()
  res.send(result)
});

const sqlitedb = new sqlite3.Database('./db/test.sqlite');
async function get_sqlite_db_all() {
  try {
    return new Promise((resolve, reject) => {
      sqlitedb.all('SELECT text FROM test',
        function (err, rows) {
          if (err) {
            console.log("ERROR: " + err)
            return reject(err)
          } else {
            return resolve(rows);
          }
        }
      )
    })
  } catch (e) {
    console.log("ERROR: " + e)
  }
}

async function insert_sqlite_db(query) {
  try {
    sqlitedb.run('INSERT INTO test (category, text) VALUES ("test", ?)', query,
      function (err) {
        if (err) {
          console.log("ERROR: " + err)
          return;
        }
      }
    )
  } catch (e) {
    console.log("ERROR: " + e)
  }
}

Write

まず書き込みを行います。

[root@118-27-34-65 ~]# ab -c 100 -n 10000 -p /root/postfile -T "application/x-www-form-urlencoded" http://118.27.4.116:3001/write_sqlite
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 118.27.4.116 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        
Server Hostname:        118.27.4.116
Server Port:            3001

Document Path:          /write_sqlite
Document Length:        18 bytes

Concurrency Level:      100
Time taken for tests:   29.721 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      2180000 bytes
Total body sent:        1940000
HTML transferred:       180000 bytes
Requests per second:    336.46 [#/sec] (mean)
Time per request:       297.209 [ms] (mean)
Time per request:       2.972 [ms] (mean, across all concurrent requests)
Transfer rate:          71.63 [Kbytes/sec] received
                        63.74 kb/s sent
                        135.37 kb/s total

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1    6   2.9      6      31
Processing:     7  291 157.7    270     826
Waiting:        6  200 140.0    197     689
Total:         11  297 158.0    276     827

Percentage of the requests served within a certain time (ms)
  50%    276
  66%    332
  75%    396
  80%    449
  90%    541
  95%    579
  98%    619
  99%    707
 100%    827 (longest request)

秒間 336.46 となりました。
ファイルベースには劣りますが、なかなかの速度です。

Read

次に読み込みを行います。

[root@118-27-34-65 ~]# ab -c 100 -n 10000 http://118.27.4.116:3001/read_sqlite
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 118.27.4.116 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        
Server Hostname:        118.27.4.116
Server Port:            3001

Document Path:          /read_sqlite
Document Length:        200001 bytes

Concurrency Level:      100
Time taken for tests:   270.107 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      2002150000 bytes
HTML transferred:       2000010000 bytes
Requests per second:    37.02 [#/sec] (mean)
Time per request:       2701.073 [ms] (mean)
Time per request:       27.011 [ms] (mean, across all concurrent requests)
Transfer rate:          7238.70 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    2   3.9      1      31
Processing:    41 2694 432.6   2681    4265
Waiting:       26 1720 504.6   1751    3459
Total:         43 2696 432.5   2682    4266

Percentage of the requests served within a certain time (ms)
  50%   2682
  66%   2825
  75%   2916
  80%   2990
  90%   3181
  95%   3426
  98%   3639
  99%   3833
 100%   4266 (longest request)


結果は、37.02 となりました。
なんだこれ、さすがに遅すぎなんですけど・・・

SQLiteの場合、1万件のレコードを全件出力するのは、かなりの負荷を伴うようです。

Read & Write

概ね結果は予想できますが、読み書きの方も念のため、調べてみます。

[root@118-27-34-65 ~]# siege -f urls.txt -c 100 -t 30s
...
...
Lifting the server siege...
Transactions:                   6612 hits
Availability:                 100.00 %
Elapsed time:                  30.03 secs
Data transferred:             104.26 MB
Response time:                  0.45 secs
Transaction rate:             220.18 trans/sec
Throughput:                     3.47 MB/sec
Concurrency:                   98.69
Successful transactions:        6612
Failed transactions:               0
Longest transaction:            2.09
Shortest transaction:           0.02

Failもなくデータベースのロックも発生していないようです。

ファイルベースの1/3の性能といったところでしょうか。
読み込みの遅さに引っ張られて結果はあまりよくありませんでした。

ただ、遅いリクエストでも2秒とレスポンスの安定性は良いようですね。

SQLite Limit

あまりに遅いので、直近100件のみを取得する方向で調べてみます。

Read

[root@118-27-34-65 ~]# ab -c 100 -n 10000 http://118.27.4.116:3001/read_sqlite_limit
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 118.27.4.116 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        
Server Hostname:        118.27.4.116
Server Port:            3001

Document Path:          /read_sqlite_limit
Document Length:        2001 bytes

Concurrency Level:      100
Time taken for tests:   14.381 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      22110000 bytes
HTML transferred:       20010000 bytes
Requests per second:    695.38 [#/sec] (mean)
Time per request:       143.807 [ms] (mean)
Time per request:       1.438 [ms] (mean, across all concurrent requests)
Transfer rate:          1501.44 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1    3   2.2      2      25
Processing:    14  140  26.1    139     253
Waiting:        9  112  24.3    109     208
Total:         15  143  26.2    142     255

Percentage of the requests served within a certain time (ms)
  50%    142
  66%    152
  75%    158
  80%    163
  90%    176
  95%    189
  98%    212
  99%    217
 100%    255 (longest request)

結果は、695.38 となりました。
かなり速くなっています。

Read & Write

Lifting the server siege...
Transactions:                  12900 hits
Availability:                 100.00 %
Elapsed time:                  29.81 secs
Data transferred:              12.32 MB
Response time:                  0.23 secs
Transaction rate:             432.74 trans/sec
Throughput:                     0.41 MB/sec
Concurrency:                   98.80
Successful transactions:       12900
Failed transactions:               0
Longest transaction:            0.55
Shortest transaction:           0.03

読み書きの方も、順当に速くなっています。

MariaDBベース

本命のリレーショナルデータベースのMariaDBを試します。
MariaDBはファイルやSQLiteと異なり、サーバーに常駐する形で使用するシステムになります。
通常はDBネットワークなどを用意してネットワーク越しになると思いますが、今回は検証サーバー内に置いて使用します。

プログラムコード

データベースのテーブルは以下の通りです。

CREATE TABLE test_db.test (
  id MEDIUMINT NOT NULL AUTO_INCREMENT,
  category VARCHAR(10) NOT NULL, 
  text VARCHAR(200) NOT NULL, 
  PRIMARY KEY (id)
);

プログラムは次のように作成してみました。

const mysql = require('mysql');
const util = require('util');

var pool = mysql.createPool({
  host: '118.27.4.116',
  database : 'test_db',
  port     : '3306',
  user     : 'test_user',
  password: 'testpass123',
  charset: "utf8mb4"
});
pool.query = util.promisify(pool.query)

router.post('/write_maria', async function (req, res, next) {
  const text = req.body.text;
  const result = await insert_maria(text)
  res.send("書き込み完了")
});
router.get('/read_maria', async function (req, res, next) {
  const result = await get_maria_db_all()
  res.send(result)
});
router.get('/read_maria_limit', async function (req, res, next) {
  const result = await get_maria_db_limit()
  res.send(result)
});

async function get_maria_db_all () {
  try {
    var rows = await pool.query('SELECT text FROM test')
    //pool.end();
    if (!rows[0]) {
      return ([])
    } else {
      return (rows);
    }
  } catch (e) {
    console.log("ERROR: " + e)
    return ([])
  }
}
async function get_maria_db_limit () {
  try {
    var rows = await pool.query('SELECT text FROM test ORDER BY id LIMIT 100')
    //pool.end();
    if (!rows[0]) {
      return ([])
    } else {
      return (rows);
    }
  } catch (e) {
    console.log("ERROR: " + e)
    return ([])
  }
}
async function insert_maria(text) {
  try {
      pool.query('INSERT INTO test (category, text) VALUES ("test", ?)', text)
  } catch (e) {
      console.log("ERROR: " + e)
  }
}

Write

書き込みを検証します。

[root@118-27-34-65 ~]# ab -c 100 -n 10000 -p /root/postfile -T "application/x-www-form-urlencoded" http://118.27.4.116:3001/write_maria
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 118.27.4.116 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        
Server Hostname:        118.27.4.116
Server Port:            3001

Document Path:          /write_maria
Document Length:        18 bytes

Concurrency Level:      100
Time taken for tests:   13.982 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      2180000 bytes
Total body sent:        1930000
HTML transferred:       180000 bytes
Requests per second:    715.23 [#/sec] (mean)
Time per request:       139.816 [ms] (mean)
Time per request:       1.398 [ms] (mean, across all concurrent requests)
Transfer rate:          152.27 [Kbytes/sec] received
                        134.80 kb/s sent
                        287.07 kb/s total

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1    5   5.5      4      53
Processing:     8  135  75.3    105     483
Waiting:        5   99  70.5     81     475
Total:         11  139  75.6    109     486

Percentage of the requests served within a certain time (ms)
  50%    109
  66%    123
  75%    144
  80%    170
  90%    237
  95%    319
  98%    403
  99%    434
 100%    486 (longest request)

はや!
結果は 715.23 でした。

Read

[root@118-27-34-65 ~]# ab -c 100 -n 10000 http://118.27.4.116:3001/read_maria
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 118.27.4.116 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        
Server Hostname:        118.27.4.116
Server Port:            3001

Document Path:          /read_maria
Document Length:        200001 bytes

Concurrency Level:      100
Time taken for tests:   215.619 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      2002150000 bytes
HTML transferred:       2000010000 bytes
Requests per second:    46.38 [#/sec] (mean)
Time per request:       2156.195 [ms] (mean)
Time per request:       21.562 [ms] (mean, across all concurrent requests)
Transfer rate:          9067.94 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   1.6      1      51
Processing:   101 2146 187.0   2144    2702
Waiting:       66 2046 177.7   2038    2607
Total:        102 2147 186.9   2145    2704

Percentage of the requests served within a certain time (ms)
  50%   2145
  66%   2215
  75%   2259
  80%   2288
  90%   2360
  95%   2425
  98%   2482
  99%   2514
 100%   2704 (longest request)

結果は 46.38 となりました。
さすがにRDBといえどデータベースで全件は遅いですね。
傾向はSQLiteと同様となりました。

ただ、SQLiteよりは速い結果となりました。
メモリキャッシュが効いているのかもしれません。

Read & Write

[root@118-27-34-65 ~]# cat urls_maria.txt 

http://118.27.4.116:3001/write_maria
POST category=test&text=hogehoge
http://118.27.4.116:3001/read_maria
[root@118-27-34-65 ~]# siege -f urls_maria.txt -c 100 -t 30s ... ... Lifting the server siege... Transactions: 7289 hits Availability: 100.00 % Elapsed time: 29.45 secs Data transferred: 123.55 MB Response time: 0.39 secs Transaction rate: 247.50 trans/sec Throughput: 4.20 MB/sec Concurrency: 97.75 Successful transactions: 7289 Failed transactions: 0 Longest transaction: 1.66 Shortest transaction: 0.00

読み書きの方も順当な結果です。
読む方が遅いから仕方ないね。

MariaDB Limit

MariaDBも直近100件のみの取得を行ってみます。

Read

[root@118-27-34-65 ~]# ab -c 100 -n 10000 http://118.27.4.116:3001/read_maria_limit
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 118.27.4.116 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        
Server Hostname:        118.27.4.116
Server Port:            3001

Document Path:          /read_maria_limit
Document Length:        2001 bytes

Concurrency Level:      100
Time taken for tests:   18.671 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      22110000 bytes
HTML transferred:       20010000 bytes
Requests per second:    535.58 [#/sec] (mean)
Time per request:       186.712 [ms] (mean)
Time per request:       1.867 [ms] (mean, across all concurrent requests)
Transfer rate:          1156.42 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   0.7      1      26
Processing:    51  185  30.1    182     350
Waiting:       33  183  29.8    180     349
Total:         52  186  30.1    182     355

Percentage of the requests served within a certain time (ms)
  50%    182
  66%    194
  75%    203
  80%    209
  90%    227
  95%    243
  98%    261
  99%    271
 100%    355 (longest request)

結果は、535.58 でかなり速くなりました。

Read & Write

[root@118-27-34-65 ~]# cat urls_maria_limit.txt 

http://118.27.4.116:3001/write_maria
POST category=test&text=hogehoge
http://118.27.4.116:3001/read_maria_limit
[root@118-27-34-65 ~]# siege -f urls_maria_limit.txt -c 100 -t 30s ... ... Lifting the server siege... Transactions: 14244 hits Availability: 100.00 % Elapsed time: 29.80 secs Data transferred: 13.62 MB Response time: 0.21 secs Transaction rate: 477.99 trans/sec Throughput: 0.46 MB/sec Concurrency: 99.21 Successful transactions: 14244 Failed transactions: 0 Longest transaction: 0.62 Shortest transaction: 0.00

読み書きの方も速くなっています。

まとめ

結果をまとめると以下の通りとなります。

数値は Req/Sec

ReadWriteRead & Write
File (ALL)144.78879.62629.44
File (Limit100)105.60181.88
SQLite (ALL)37.02336.46220.18
SQLite (Limit100)695.38432.74
MariaDB (ALL)46.38715.23247.50
MariaDB (Limit100)535.58477.99

ファイルベースがシンプルなだけあって最も速い結果となりました。
特に全体読み込みを行う頻度が高い場合はこの方式の優位性が高いでしょう。

一方で最新100件のみ取得する場合はデータベースの方に優位性があります。RDBのMariaDBが最も速い結果となっています。

また、SQLiteでロックが発生しなかった点は収穫です。
てっきりSiegeによる同時読み書きでロックが発生すると予想していたのですが・・・

ネットワークコストを支払っているはずのMariaDBがSQLite並みに速かったことも驚きです。
実行環境がローカルである点も理由だと思いますが、想像以上に速かったと感じました。
キャッシュなどを利用できることを考えると、多くのサービスがRDBを選択していることも頷けるところです。

いずれも一長一短と言えると思います。データベースの結果としてはこのようになりましたが、単に性能だけで選ぶのではなく、この結果を踏まえて他の設計やサービスの形式を考慮していく必要がありそうです。
特にサービスとして、最新データだけ返すのか、全てのデータを返すのかの設計で決まると言えそうです。

検討

記事としては、まとめで終わりなのですが、私個人の実際の運用を考慮してみます。

スケーリング

形式スケーリング方法
Fileサーバーを増やすだけでスケールする
ただしnginxなどでドメインやパスによるサーバー分散が必要
サーバーの性能が処理上限になる
特にIO性能はRAID以外の選択肢が無い
SQLite同上
MariaDB書き込みはスケールできない。(高価なサーバーがー必要)
読み込みはスケールできるがマイグレーションコストが上がる(リードレプリカ)
カテゴリーごとなどで異なるDBを立てれば書き込みスケールも可能(広義のパーティショニング)
ただし、その場合はDBの一貫性が失われるため検索コストなどが上がると考えられる


バックアップ

形式バックアップ方法
Filescp / rsync など
デイリー実行などにしておくと、実行の間にサーバーが死ぬとデータが消える
ファイルベースなので管理が楽
SQLite同上
MariaDBmysqldump
実行の間にサーバーが死ぬとデータが消えるが、
レプリケーションを有効にすることでリアルタイムに同期できる
しかしお金がかかる

リアルタイムにバックアップができるMariaDBに優位性があります。しかしバックアップ用のサーバーが必要になるためコスト増です。
一方で、ファイルベースは、いざとなった場合にローカルのパソコンにバックアップしておくなどの方法もあり管理が楽です。ファイルサイズもMariaDBに比べると小さくなります。

移行

既存環境からオンプレやクラウドやVPSなど、サイトを別のシステムへの移行を迫られる場合があります。その手順をおおまかに確認しておきます。

形式
Fileサーバー作成
rsync
ドメイン切り替え
SQLite同上
MariaDBサーバー作成
エクスポート・送信・インポート ※時間がかかる
ドメイン切り替え

MariaDBはDBサイズ分+αのダンプファイルを作成して送信する必要があります。
ファイルベースの方が軽快に移行しやすいことを考えると優位性があると言えると思います。

データが壊れた時

実際に運用していると、ごく稀にですがバグなどでデータが破損する場合があります。

形式
File定期バックアップからコピー
SQLite定期バックアップからコピー
MariaDBレプリカに切り替え

データは損時はMariaDBに優位性があります。
データの喪失もありません。

維持費

MariaDBの場合は高性能なデータベースサーバーを用意する必要があり、費用として2倍くらいコストがかかる。

仕様変更の可能性

開発が進んでいくとテーブルなどの仕様を変更する場合がよくあります。この手順を考慮してみます。

形式
File破壊的な変更が必要になる可能性がある
ファイルが分散しているため全ファイルの変更が必要
SQLiteテーブルの定義を別ファイルで作り直す必要あり
ただ、ファイルが分散しているため全ファイルの変更が必要
MariaDBSQL文1つで変更できる

仕様変更についてはMariaDBに優位性があります。

結論

結論としては、

お金があればMariaDB
お金が無ければSQLite

これです。

ただ、MariaDBでもサーバー1台でスモールスタートはできますので、MariaDBを選択しようと思います。

参考文献

Webサーバー向けのベンチマークツールを使ってみよう(前編)――Apache BenchとSiege | さくらのナレッジ
WebサーバーやWebサービスの公開前には、そのサーバーがどの程度のアクセスにまで耐えられるかを事前に調査しておくことが好ましい。本記事では、こういった調査の際によく使われる「Apache Bench」および「Siege

コメント

タイトルとURLをコピーしました