しょ〜うぃん広場

おもにTech系なブログ、ときどき個人的なブログ

任意のSQL文で mysqldump できるツールを作った

現状

$ mysqldump --query '任意のSQL文' > mydump.sql

のような事をしたかったのだけど、mysqldump では --where オプションで

$ mysqldump mydatabase users --where 'id IN (SELECT user_id FROM hoge WHERE huga > 10) AND id > 1000'

ぐらいのことしかできず、キーが1つの場合の JOIN を IN に書き直せるぐらいが限界になる。

別の方法としては mysqldump を使わずに

$ mysql -e "SELECT * FROM users" mydatabase --xml > mydump.xml
$ mysql --local-infile -e "LOAD XML LOCAL INFILE 'mydump.xml' INTO TABLE users;" mydatabase 

とすると任意のSQL文が使えるが、XML形式でしか出力できないため大量のレコードを出力する際にはダンプのファイスサイズがかなり大きくなってしまう。中身はこんな感じ。

$ cat mydump.xml
<?xml version="1.0"?>
<resultset statement="select * from users
" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <row>
    <field name="id">1</field>
    <field name="first_name">sato</field>
    <field name="last_name">huga</field>
    <field name="age">19</field>
    <field name="country">JP</field>
    <field name="created_at">2016-08-13 08:45:58</field>
  </row>

myquerydump 作った

そこで showwin/myquerydump をつくった。 これを使うと

$ myquerydump mydatabase "SELECT * FROM users" > mydump.sql
$ mysql mydatabase < mydump.sql

のように export/import できる。 ダンプファイルの中身も mysqldump と同じように SQL で書かれているのでファイルサイズも大きくならない。

$ cat mydump.sql
LOCK TABLES `users` WRITE;
INSERT INTO `users` VALUES ('1','sato','huga','19','JP','2016-08-13 08:45:58'),(…
UNLOCK TABLES;

ファイルサイズを比較してみると、このようなテーブルの300万レコードで以下のようになる。

mysql> desc users;
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| id         | int(11)     | NO   | PRI | NULL    | auto_increment |
| first_name | varchar(64) | YES  |     | NULL    |                |
| last_name  | varchar(64) | YES  |     | NULL    |                |
| age        | int(11)     | YES  | MUL | NULL    |                |
| country    | varchar(64) | YES  |     | NULL    |                |
+------------+-------------+------+-----+---------+----------------+
mysql> SELECT COUNT(*) FROM users;
+----------+
| count(*) |
+----------+
|  3000000 |
+----------+

$ ls -lh
104M  users.mysqldump
104M  users.myquerydump
551M  users.xml

XMLで出力した場合と比べて20%弱ぐらいのサイズになり、当然だが mysqldump と同じぐらいになる。

便利オプションの紹介

例えば

$ myquerydump mydatabase "SELECT users.name, prefectures.name FROM users INNER JOIN prefectures ON users.prefecture_id = prefecture.id ORDER BY users.name" > my.dump
$ cat my.dump
LOCK TABLES `users` WRITE;
INSERT INTO `users` VALUES …
UNLOCK TABLES;

のように複数テーブルのカラムを SELECT してダンプを取る時には大抵 INSERT 先は users テーブルでなかったりするので、勝手に INSERT INTO 'users' とされてしまうと困る場合がある。 そういうときには -t オプションでテーブル名を指定すると

$ myquerydump -t user_prefecture mydatabase "SELECT …" > my.dump
$ cat my.dump
LOCK TABLES `user_prefecture` WRITE;
INSERT INTO `user_prefecture` VALUES …
UNLOCK TABLES;

となって便利。

INSERT 前にテーブルを空にしたい場合には -add-delete-table オプションを付けると DELETE FROM が追加される。

$ myquerydump -add-delete-table -t user_prefecture mydatabase "SELECT …" > my.dump
$ cat my.dump
LOCK TABLES `user_prefecture` WRITE;
DELETE FROM `user_prefecture`;
INSERT INTO `user_prefecture` VALUES …
UNLOCK TABLES;

残りの細かなオプションは README#Usage を見て頂けると良いと思う。 基本的には mysqldump と同じオプション名になるように気をつけている。

問題点

速度が遅い!! 上の300万件ある users テーブルでこれぐらいの速度差がある。

$ mysql -e "SELECT * FROM users" mydatabase --xml > users.xml
=> 14 sec

$ myquerydump mydatabase "SELECT * FROM users" > users.dump
=> 78 sec

ファイスサイズが5倍になる mysql -e --xml を使うか、ダンプに5倍の時間がかかる myquerydump を使うかという選択をしなければいけない感じでイマイチ…

もう少し高速化して2倍ぐらいの時間でダンプが取れるようにしたい。

Webサイトの負荷検証ツールを作ってみた

先週仕事で、負荷検証をしてみたいかも!みたいな話がすこし上がって、どんなツールがあるのか調べてみたら、最近は Gatling というのがちょっとアツいらしい。 昔から使われている(らしい) JMeter はあまりパフォーマンスが出ないとか書いてある記事を見かけたけど、ぼくは使ったことがないのでよくわからない。

Gatling はパフォーマンスを重視して Scala で書かれているんだけど、リクエストのシナリオを書くのに ScalaDSL を書かないといけない。こんな感じ

class MySimulation extends Simulation {

  val conf = http.baseUrl("http://example.com")

  val scn = scenario("Gatling")
      .exec(http("index").get("/"))
      .during(10 minutes) {
    exec(
      http("json").get("/json")
        .check(jsonPath("$.id")
        .saveAs("id"))
    )
  }

  setUp(scn.inject(atOnceUsers(5)))
        .protocols(conf)
}

複雑なリクエストを送りたい時には使い勝手が良いのかもしれないけれど、ScalaDSL とかよくわからないし、書きたくないので、もっと簡単に負荷検証ができるツールを作ってみた。

hakari

ツールの名前は hakari で、測りたいから hakari にした。 Easy to Install, Easy to Write Scenario. をウリにしていて、カップラーメンを待つ3分の間にインストールから実行までできてしまう。

インストール

$ brew tap showwin/hakari
$ brew install hakari

リクエストシナリオを書く

# scenario.yml
TopPage:
  method: "GET"
  url: "http://example.com/"
Login:
  method: "POST"
  url: "http://example.com/login"
  parameter:
    email: "user@example.com"
    password: "secret_password"

負荷検証実行!!!

$ hakari
2016/02/21 18:12:47  hakari Start!  Number of Workers: 2
2016/02/21 18:13:48  hakari Finish!
TopPage
    200: 125 req, 238.66 ms/req

Login
    200: 110 req, 255.11 ms/req
    500: 15 req, 192.18 ms/req

あっという間に負荷検証できて、ビックリする。
上の例では、2並列リクエストでテストを行っていて、1分間にトップページが125回リクエストされて、平均 238ms でレスポンスが返ってきたことがわかる。

デフォルトでは2並列リクエストで1分間の負荷検証を行うけど、もう少し細かな設定もできて、HTTP header をカスタマイズしたかったら

# myconfig.yml
Header:
  Accept-Encoding: "gzip, deflate, sdch"
  Accept-Language: "ja,en-US;q=0.8,en;q=0.6"
  Cache-Control: "max-age=0"
  User-Agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.39 Safari/537.36"

と設定ファイルを書いて

$ hakari -c myconfig.yml -s scenario2.yml -w 10 -m 2

とすると、10並列リクエストで2分間 myconfig.yml に設定した HTTP header で scenario2.yml のシナリオを実行できる。

まとめ

hakari は Gatling や JMeter と比べると、結果表示部分のレポート機能がまだまだ弱いので、そこをもう少し強化する必要がある。逆に、レポート機能はそんなに充実してなくて良いので、手軽にどのぐらいのリクエストをさばけるのか負荷検証したいという場合には hakari を使うと良いと思う。

昨日の午後から開発し始めて、24時間でこのぐらいのツールが作れてしまうので、Go 言語はすごい。 ぼくはすごい Gopher ではないので、テストコードも書いていないし、おそらく Go らしいコードも書けていない。。もっと勉強しなければ。

便利!! と思った方は、遠慮せずに GitHub Star ください!! → showwin/hakari

ネットワーク速度を計測するコマンドラインツールを作ってみた

ネットワークの回線速度を計測するツールspeedtest.net が有名ですが、これはブラウザからしか計測できません。 定期的に回線速度を計測して記録したい時や、世界中のサーバーに対してネットワーク速度を計測したい時に、これをブラウザで行うのは大変なので、コマンドラインからネットワーク速度を計測できるツールを作成しました。

速度の計測には speedtest.net の資産を使っていて、ダウンロード/アップロードをする計測先のサーバーは speedtest.net に登録されているものの中から選択することができます。

インストール方法

Mac(Homebrew) の場合

$ brew tap showwin/speedtest
$ brew install speedtest
$ speedtest
Testing From IP: 124.27.198.183 (Fujitsu) [34.7000, 137.7333]

Target Server: [6691]    58.53km Shizuoka (Japan) by sudosan
Latency: 28.030145ms
Download Test: ................
Upload Test: ................

Download: 60.31 Mbit/s
Upload: 22.61 Mbit/s

とあっという間に計測できます。 他のOSの場合には こちら にパッケージ化したものがおいてあるので、そちらを使ってください。

使い方

現在地から一番近いサーバーに対して速度計測を行います。

$ speedtest

サーバー一覧を現在地から近い順に並び替えたものを表示します。

$ speedtest --list
Testing From IP: 124.27.199.165 (Fujitsu) [34.9769, 138.3831]
[6691]     9.03km Shizuoka (Japan) by sudosan
[6087]   120.55km Fussa-shi (Japan) by Allied Telesis Capital Corporation
[6508]   125.44km Yokohama (Japan) by at2wn
[6424]   148.23km Tokyo (Japan) by Cordeos Corp.
...
[4580]  2938.66km Ulaanbaatar (Mongolia) by Kewiko LLC
...

いきなり、モンゴルにあるサーバーとの回線速度を計測したくなっても、そのサーバーのIDを指定すれば簡単に計測できます。

$ speedtest --server 4580
Testing From IP: 124.27.198.183 (Fujitsu) [34.7000, 137.7333]

Target Server: [4580]  2938.66km Ulaanbaatar (Mongolia) by Kewiko LLC
Latency: 292.195371ms
Download Test: ........
Upload Test: ....

Download: 20.76 Mbit/s
Upload:  4.40 Mbit/s

意外と速いですね。

静岡と横浜と東京の平均値を計測したくなったら、複数のサーバーを指定することもできます。

$ speedtest --server 6691 --server 6508 --server 6424
Testing From IP: 124.27.198.183 (Fujitsu) [34.7000, 137.7333]

Target Server: [6691]    58.53km Shizuoka (Japan) by sudosan
Latency: 31.216696ms
Download Test: ................
Upload Test: ........

Target Server: [6508]   192.30km Yokohama (Japan) by at2wn
Latency: 16.12025ms
Download Test: ................
Upload Test: ................

Target Server: [6424]   208.44km Tokyo (Japan) by Cordeos Corp.
Latency: 15.109657ms
Download Test: ................
Upload Test: ................

[6691] Download: 60.06 Mbit/s, Upload: 24.17 Mbit/s
[6508] Download: 72.95 Mbit/s, Upload: 80.88 Mbit/s
[6424] Download: 69.51 Mbit/s, Upload: 75.39 Mbit/s
Download Avg: 67.51 Mbit/s
Upload Avg: 60.15 Mbit/s

便利ですね!!!

このツールすごいんですよ!

同じようなツールsivel/speedtest-cli というのがあり、こちらも speedtest.net のサーバーを使って同じように計測しているのですが、 speedtest.net の結果と比較してみると結構差がありました。 ぼくが今回作成したものは、ブラウザで speedtest.net を使って計測するときにかかる約半分の時間で計測が可能で、さらに既存の sivel/speedtest-cli よりも高い精度 (よりspeedtest.netで計測した値に近い) で計測することができます。 詳細が気になる方は 実験結果 をご覧ください。

これでネットワーク速度計測し放題ですね!!!

cookpad のオフィスに遊びに行ってきた

Facebookに 中国から日本に帰ってきた! って書いたら、去年のインターンでお世話になったcookpad人事の方に、「新オフィス遊びにおいでー」って言われたので遊びに行ってきた! (図々しくてすみません…笑)

写真撮ってくればよかったんだけど、忘れちゃって載せられる画像が1枚もない…笑

去年のインターンで一緒だった@s_osa_くんがバイトしてたから、ちょっと時間頂いて「研究しながら週四でバイトしてるよ〜(スゴイッ)」みたいな話とか聞いた。 (夕飯一緒に行けなくてゴメン、また今度行こう!!)

人事の方にひと通りオフィスの中をぐるっと紹介してもらって、なるほどなるほど〜って感じだった。 ワンフロア貸切だけど、全部が見渡せる感じではなくて、社員さんたちのスペースは大きく3つぐらいのブロックに分かれていた気がする。 (どのチームをどこに配置するのかってすごく重要だと思うんだけど、だれがそれ決めるの?って聞き忘れて残念だ…)

そのあとは @taiki45 さんと1時間ぐらい時間頂いてお話させてもらって、RailsのFat Modelはどう解決したらいいのか、E2EとかModelのテストってどこまで書いているのか、最近注目しているミドルウェアはなんですか?とかを聞いた。 大学は文系の専攻で、卒業して3年目でバリバリプログラム書いてる方みたいだったから、自分ももっと成長スピード上げていかないとなって刺激を受けた。

(cookpad は)リモートワークしないんですか? って聞いたら、抽象度の高い話はホワイトボードとかに絵とか図を書きながら議論した方が効率が良いでしょ? という回答をもらって、確かに自分たちもそれでちょっと困っていることはあるなと思った。自分は仕事でSlackとSkypeでしかコミュニケーションを取っていないから、自分の意見を伝えるのに必ず言葉にして発しないといけなくて、「こういう感じ」をうまく伝えられなかったり、「この辺がターゲット」をマトリックスのこの辺 みたいな形で伝えられないのは確かに弱点としてある。

あとは、グループ内のToDo管理もポストイットを使ったカンバン方式で管理していて、 これはなんでオンラインで管理しないんですか? と聞いたら、(ないと思うけど)だれかが cheat する可能性があるし、チームみんなでポストイットを囲んで、「このタスクは終わったよね」とか確認しながらタスク管理できるのが良いとのことだった。 自分の仕事では Pivotal Tracker + Slack でタスクに変化があれば、Slackに通知が来るようになっているけど、ドカドカっとだれかがタスク整理したりすると、何が起こったのか把握しずらいし、その辺りもリモートワークに改善の余地があるなぁと思った。

自分は大学を出てすぐにリモートワークをしてしまったから、良いチームがローカルワークでどうやって仕事を進めているのかの知見が少なくて、今回みたいな機会があるとすごく勉強になる。

インターンで5日間お世話になっただけなのに、1年後も覚えててくれてこういう機会まで設けてくれて本当にありがとうございます!圧倒的感謝!!! という感じなので、ブログの記事にしてみました。

また機会があれば、遊びに行かせてください〜!

YAPC::Asia 2015 に参加してきた!

今回のYAPC::Asiaが10周年目で、一応今年で最後!とのことなので参加してきました。 Perl Conference のはずなのに、最近はWeb全般のTalkが増えてきているので、自分はPerlを全然書かないけどかなり楽しめました。

1日目はずっとA会場にいて以下のトークを

2日目はD会場とE会場をウロウロして

を聞きました。

その中でも印象に残っているというか、影響を受けた話は fukayatsu さんの esa.io - 趣味から育てたWebサービスで生きていく です。 詳しくはスライドが上がっているので、上のリンクから見てもらえば良いのですが、概要としてはハッカソンでササッと作ったオンラインドキュメント管理サービスで、それをデザイナーの方と2人でブラッシュアップして、今は会社立ち上げてやってるよ!って話です。 2014/11(?)からサービスインして、現時点で約800チームが使っているみたいなので、1チーム平均5人だと仮定して、500円/月*人なので、単純に計算して200万円/月ぐらいの売上ですよね。

いいな〜 って感じですね。

Ruby on Railsの作者のDHHも言っているんですが、FacebookとかTwitterとかYouTubeとか、世界的に超成功するサービスを作るのはすごく大変だけど、適度に儲かるサービスなら頑張れば作れるよね!って思うんですよ。

それで年収1000~2000万円ぐらいになれば、生活に困ることはないし、自分の好きなサービスのメンテナンスしていればいいわけだし、めちゃめちゃ楽しい生活の始まりじゃないですか。

Mac + 4K@60Hzディスプレイには LG の 27MU67-B が最適

4月から社会人になり、リモートワークをしています。 ほとんど自宅で仕事をしているので、作業環境を整えたいなと思って4Kディスプレイを購入しました。

27MU67-B を買った背景

MacBook Pro Retina(15'' Late 2013) + 4K@60Hz な環境が欲しかったのですが、Macで4Kディスプレイを使うネットの記事を見ていると

表示の一部が乱れたうえ、さらに元の解像度に戻せなくなる

via Macで4Kクラスの超高解像度ディスプレイを試す

モニタ付属のDisplayPortケーブルでThunderbolt2経由接続をしてみましたが、解像度・発色ともに破綻しており、使い物になりませんでした。

via Dell UP2414Q のカスタマーレビュー

のような書き込みが割とたくさんあって、どの商品も個体差があり、Macと相性の良い4Kディスプレイはほとんどなさそうでした。

Yosemite v10.10.2 までは60Hzの場合は標準でMulti-Stream Transport (MST)という方式で4K出力をするようになっていたため、それとディスプレイの相性がなかなか合わないのではないかというのが個人的な意見です。 MSTについて簡単に説明すると、これはもともと外部ディスプレイを複数枚つなげるときに使う転送方式で、4Kディスプレイの場合には右半分と左半分を2枚のディスプレイとみなしてデータ転送をする方法です。

Appleもこういった問題が多発しているのに気づいたのかもしれませんが、Yosemite v10.10.3からSingle-Stream Transport (SST)での60Hzに対応しました。 SSTだとGPUの力が更に必要になるようで、SST@60Hzに対応しているMacの機種はMST@60Hzに対応しているものよりも更に少なくなります。(参考: Using 4K displays and Ultra HD TVs with your Mac) Screen Shot 2015-07-23 at 09.15.30

よく見ると、ぼくが使っているMacBook Pro Retina(15'' Late 2013)はSST@60Hzに対応していない模様… ただこれは、MBPR標準のIris ProのGPUを使った場合であって、上位モデルに付いている外部GPU (Late 2013であれば NVIDIA GeForce GT 750M) であれば、SSTで60Hzいけるんじゃないの? と個人的には勝手に思っていました。

そんな感じで (1) MacBook Pro Retina(15'' Late 2013)の上位モデルであれば、SSTで60Hz出力できるか (2) LG製27MU67-B はMacと相性が良いのか

を試すために、人柱になって 27MU67-B を買ってみました。

結果

7月11日に届いて、約2週間使ってみましたが100点中、90点です!

良い点

(1) 表示の一部が乱れたりするような障害は一切起きなかった 特に設定もなにもする必要がなく、DisplayPort(ディスプレイ) to Thunderbolt(MBPR) で繋ぐとすぐに表示されました。 対応するケーブルも付属していたため、ディスプレイ到着後にすぐに使えたのも好印象です。

(2) 4Kディスプレイの解像度が変更できる 当然といえば当然なのですが、こちらも問題なく自分の好みの解像度に変更できます。 Screen Shot 2015-07-23 at 09.49.55

(3) HDMI2.0に対応している 今のMacには関係ありませんが、このディスプレイはHDMI2.0に対応しているので、HDMIでも60Hzで表示することができます。(従来のHDMIは4K@30Hzまででした) これからHDMI2.0に対応したPCやゲーム機が出てくると思うので、長期間使えるディスプレイになるでしょう。

悪い点

(1) 入力ポート切り替えが面倒 以下のレビューにもあるように、入力ポートの切り替えにMENUに入って選択する必要があり、切り替えまでに最低でも8回のボタン入力が必要です。 複数のマシンでディスプレイを共有するという使い方には適していないかもしれません。

4台のPCに接続しているのですが、切り替えが非常に面倒です。
MENUから入って一段奥に入力切替があるのでボタンを数回押さないといけないのでストレスを感じます。

via LG 27MU67-B のカスタマーレビュー

(2) Macが発熱する やはりMBPR(Late 2013)のIris Proでは60Hzに対応していないようで、ディスプレイを繋ぐと自動的にGPUNVIDIA GeForce GT 750M に切り替わります。 ディスプレイ上ではほとんど静的な画面を写しているだけなので、GPUの使用率は5%前後ぐらいなのですが、それでもファンの回転数が4000~5000rpmでGPUダイの温度も70℃弱になります。 GPUが力不足という事はなさそうですが、750Mの発熱によるファンの回転音が少し気になります。 (感覚的には、重い処理を走らせてCPUをブン回した時ぐらいのファンの回転音が常にしている感じです)

夏なので高温になりやすいという季節的な問題もあるかと思いますが、ぼくはフリップスタンドを使うことでこの問題が解決できました。
Screen Shot 2015-07-23 at 10.48.23

こういったものがあるのは今回初めて知ったのですが、結構人気の商品のようです。 (購入時には20個在庫があったのですが、翌日見たら在庫が2個になっていました)

フリップスタンドを使うことで、Mac本体の下部に隙間を作ることで排熱効率が良くなります。 これを使うとファンの回転数が3000rpmぐらいに落ち着き、ファンの音もほとんど気にならないぐらいになりました。 (音に注意すると、あっ回ってる! とわかるぐらいの音量です)

まとめ

悪い点の(2)が解決するまでは、80点ぐらいだったのですが、それが解決できたので90点になりました。 そもそもまともに動くかどうかわからない状態での購入だったので、その反動で自分の中での評価が若干高めになっています。

HDMIを使って30Hzでも接続してみましたが、マウスポインタが滑らかに動かず、これでは普段使っていてストレスが溜まるなぁ…という感じだったので、60Hzで使用できる環境を整えることが大事だと感じました。

27インチで6万円(執筆時)となかなかの安さなので、手を出しやすいモデルかなと思います。

Mouをコマンドラインから簡単に使えるようにした

結論から言うと、この記事はMouを[mou]というコマンドで起動できるようにするための記事です。

TextMateとかだと

$ mate filename

ってやると、filenameのファイルをTextMateで開ける。

これをマークダウンエディタのMouでもやりたかった。 調べてみたら下の参考1が見つかったんだけど、このページだけだとやり方がよく分からなかったので参考2のページも参考にしながら、実現させた。
【参考1】: コマンドラインでMouを開く
【参考2】: 自作のスクリプトをコマンド検索パスに保存する

そもそもMouにはコマンドラインから起動できるらしいのだけど、コマンドが

$ open -a Mou               # Mouを開く
$ open -a Mou filename.md    # filename.mdを開く

とか、ちょっと長くて面倒なコマンドになっている。 しかも下のコマンドでは、既存のファイルを開くだけで、新しいファイルは作成できない。。。

ということで、 ・[mou]だけ打てば良くて ・新しいファイルも作成できる コマンドを作ることにする。

作業開始

適当なディレクトリで

$ vim mou

mou

#!/bin/bash

if test $# -eq 0; then
    `open -a Mou`
elif test $# -eq 1; then
    `touch ${1}`
    `open -a Mou ${1}`
fi

中身はこれだけ。 touch ${1} の部分はなくてもいいけど、これを書かないと新しくファイルを作りたいときにエラーになる。 すでにファイルが存在する場合にtouchをしても、タイムスタンプが更新されるだけなので、問題なし。

書き終わったらmouファイルの実行権限を変更しておく。

$ chmod a+x mou

でもこのままだと、コマンドを打つときに

./mou filename.md

とかしないといけなくて気持ち悪いし、そもそもmouのファイルがあるディレクトリにいないと絶対パスを打たないといけない… クソやん!!!!

ってことで、mouコマンドを使えるようにする。

$ sudo cp mou /usr/local/bin/mou

このコマンド一発で完了。 (このやり方を調べるのに10分ぐらいかかったんだけど…笑)

これで

mou filename.md

ができるようになりました! 便利!!!!!!