背景
掲示板・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
Read | Write | Read & Write | |
File (ALL) | 144.78 | 879.62 | 629.44 |
File (Limit100) | 105.60 | 181.88 | |
SQLite (ALL) | 37.02 | 336.46 | 220.18 |
SQLite (Limit100) | 695.38 | 432.74 | |
MariaDB (ALL) | 46.38 | 715.23 | 247.50 |
MariaDB (Limit100) | 535.58 | 477.99 |
ファイルベースがシンプルなだけあって最も速い結果となりました。
特に全体読み込みを行う頻度が高い場合はこの方式の優位性が高いでしょう。
一方で最新100件のみ取得する場合はデータベースの方に優位性があります。RDBのMariaDBが最も速い結果となっています。
また、SQLiteでロックが発生しなかった点は収穫です。
てっきりSiegeによる同時読み書きでロックが発生すると予想していたのですが・・・
ネットワークコストを支払っているはずのMariaDBがSQLite並みに速かったことも驚きです。
実行環境がローカルである点も理由だと思いますが、想像以上に速かったと感じました。
キャッシュなどを利用できることを考えると、多くのサービスがRDBを選択していることも頷けるところです。
いずれも一長一短と言えると思います。データベースの結果としてはこのようになりましたが、単に性能だけで選ぶのではなく、この結果を踏まえて他の設計やサービスの形式を考慮していく必要がありそうです。
特にサービスとして、最新データだけ返すのか、全てのデータを返すのかの設計で決まると言えそうです。
検討
記事としては、まとめで終わりなのですが、私個人の実際の運用を考慮してみます。
スケーリング
形式 | スケーリング方法 |
File | サーバーを増やすだけでスケールする ただしnginxなどでドメインやパスによるサーバー分散が必要 サーバーの性能が処理上限になる 特にIO性能はRAID以外の選択肢が無い |
SQLite | 同上 |
MariaDB | 書き込みはスケールできない。(高価なサーバーがー必要) 読み込みはスケールできるがマイグレーションコストが上がる(リードレプリカ) カテゴリーごとなどで異なるDBを立てれば書き込みスケールも可能(広義のパーティショニング) ただし、その場合はDBの一貫性が失われるため検索コストなどが上がると考えられる |
バックアップ
形式 | バックアップ方法 |
File | scp / rsync など デイリー実行などにしておくと、実行の間にサーバーが死ぬとデータが消える ファイルベースなので管理が楽 |
SQLite | 同上 |
MariaDB | mysqldump 実行の間にサーバーが死ぬとデータが消えるが、 レプリケーションを有効にすることでリアルタイムに同期できる しかしお金がかかる |
リアルタイムにバックアップができるMariaDBに優位性があります。しかしバックアップ用のサーバーが必要になるためコスト増です。
一方で、ファイルベースは、いざとなった場合にローカルのパソコンにバックアップしておくなどの方法もあり管理が楽です。ファイルサイズもMariaDBに比べると小さくなります。
移行
既存環境からオンプレやクラウドやVPSなど、サイトを別のシステムへの移行を迫られる場合があります。その手順をおおまかに確認しておきます。
形式 | |
File | サーバー作成 rsync ドメイン切り替え |
SQLite | 同上 |
MariaDB | サーバー作成 エクスポート・送信・インポート ※時間がかかる ドメイン切り替え |
MariaDBはDBサイズ分+αのダンプファイルを作成して送信する必要があります。
ファイルベースの方が軽快に移行しやすいことを考えると優位性があると言えると思います。
データが壊れた時
実際に運用していると、ごく稀にですがバグなどでデータが破損する場合があります。
形式 | |
File | 定期バックアップからコピー |
SQLite | 定期バックアップからコピー |
MariaDB | レプリカに切り替え |
データは損時はMariaDBに優位性があります。
データの喪失もありません。
維持費
MariaDBの場合は高性能なデータベースサーバーを用意する必要があり、費用として2倍くらいコストがかかる。
仕様変更の可能性
開発が進んでいくとテーブルなどの仕様を変更する場合がよくあります。この手順を考慮してみます。
形式 | |
File | 破壊的な変更が必要になる可能性がある ファイルが分散しているため全ファイルの変更が必要 |
SQLite | テーブルの定義を別ファイルで作り直す必要あり ただ、ファイルが分散しているため全ファイルの変更が必要 |
MariaDB | SQL文1つで変更できる |
仕様変更についてはMariaDBに優位性があります。
結論
結論としては、
お金があればMariaDB
お金が無ければSQLite
これです。
ただ、MariaDBでもサーバー1台でスモールスタートはできますので、MariaDBを選択しようと思います。
コメント