RDB v.s KVS
KVS触っていて、「RESTAPIがあるのっていいなー」とは思う。複雑なクエリは出せないけど、curlコマンド一発でアクセスできる。だったら複雑ではないクエリなら、mysqlにそういうRESTのかぶせものがあってもいい。
mysql-to-restは nodeで使える、mysqlにREST をかぶせるライブラリ
以下、mysql-to-restを使ったサンプルソース。依存モジュールはmysql-to-restのREADMEにしたがって入れてください。手元にポケモンGOのポケモンの出現情報をためたテーブルがあるのでそれを取得してみた。
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : '',
database : 'sample',
timezone : 'Asia/Tokyo' //これいれないとUTCで出ちゃう
});
connection.connect();
var express = require('express');
var mysqltorest = require('mysql-to-rest');
var app = express();
app.set('trust proxy', 'loopback'); // 同一セグメントからのみの接続を許可
// app.set('trust proxy', 'loopback,192.168.0.1'); // 同一セグメントプラス特定IPのみ許可
var api = mysqltorest(app,connection);
app.listen(8000);
~
実行
> node sample.js
テスト
appearances というテーブルの、主キーが、11,12,13というレコードを取得
curl http://localhost:8000/api/appearances/11,12,13
レスポンス
{
"result": "success",
"json": [
{
"id": 11,
"pokemon_id": 69,
"pokemon_name": "マダツボミ",
"lat": 35.64469,
"lon": 139.687588,
"distance": 588.94278,
"expired": "2016-07-30 12:40:18",
"query_lat": 35.644393,
"query_lon": 139.681088,
"landmark": "世田谷公園",
"created_at": "2016-07-30 12:34:54",
"updated_at": "2016-07-30 12:34:54"
},
{
"id": 12,
"pokemon_id": 74,
"pokemon_name": "イシツブテ",
"lat": 35.646162,
"lon": 139.68679,
"distance": 552.19259,
"expired": "2016-07-30 12:37:13",
"query_lat": 35.644393,
"query_lon": 139.681088,
"landmark": "世田谷公園",
"created_at": "2016-07-30 12:34:54",
"updated_at": "2016-07-30 12:34:54"
},
{
"id": 13,
"pokemon_id": 23,
"pokemon_name": "アーボ",
"lat": 35.649789,
"lon": 139.68368,
"distance": 644.8466,
"expired": "2016-07-30 12:36:55",
"query_lat": 35.644393,
"query_lon": 139.681088,
"landmark": "世田谷公園",
"created_at": "2016-07-30 12:34:54",
"updated_at": "2016-07-30 12:34:54"
}
],
"table": "appearances",
"length": 3
}
他にもいろんなクエリのサンプル
- ポケモンIDが11より小さいレコードを取得
- http://localhost:8000/api/appearances/?pokemon_id[SMALL]=11
- idというフィールド名の降順に並べて100件取得
- http://localhost:8000/api/appearances/?_limit=100&_order[id]=desc
- 作成日が2016-07-30 15:58:00 以降のレコード
- http://localhost:8000/api/appearances/?_limit=1000&_order[id]=desc&created_at[GREAT]=2016-07-30 15:58:00
- 時刻もそのままの表記で渡せてる
実行パフォーマンス
先ほどのキーダイレクトで3件取得するというサンプルで負荷テスト。MBA 1.6 GHz Intel Core i5 のメモリ4G というなんてことのないマシン。
ab -n 1000 -c 10 http://localhost:8000/api/appearances/11,12,13
Requests per second: 473.91 [#/sec] (mean)
Time per request: 21.101 [ms] (mean)
Time per request: 2.110 [ms] (mean, across all concurrent requests)
Transfer rate: 606.28 [Kbytes/sec] received
同じクエリを流し続けているので、クエリキャッシュがきいてるはずだけど、十分早い。ちょっと重めのSQLになれば、ほとんどDB側の時間になるはず。
複雑なSQLは流せない
JOINをしたかったら?
- 画面の一覧に出すレコードはどうせlimit 20とかそんなもん
- 取得したあとに、20のレコードにある外部キーで、改めてキーダイレクトで取得
- 取得したあとにプログラム側で結合
ドン臭いけど、JOINはある日突然パフォーマンスが落ちることがある。集十件のレコードを主キー取得するのはいつも一瞬。
もしくは参照頻度があまりに大きかったら、正規化崩してJOIN済みのデーブルを作っちゃっててもいい。
そこまでやるならとっととKSVを使えばいいじゃない
確かにそうなんだけど、phpmyadmin に相当するいいものがなかったり、SequelProに相当するいいツールがなかったり、オンプレミスとクラウドとローカル開発環境とを同じアーキテクチャでやろうとすると、ちょうどいいのがなかったりとか。そしてやっぱり「表」は楽ちんで、CSVに吐き出してエクセルで見るという導線はやっぱり便利。
そしていざって時に、遅くてもいいからフィールドをLIKE検索したり、groupやcountしたりする。そんなわけでまだまだmysql使っていそうな気がする。