「超人脳の作り方」を読んだ

苫米地英人著の本は、「スピリチュアリズム」と「洗脳原論」を以前に読んだけれども、また興味のあるテーマがあったのでお盆の間に読んだ。

知的生産力が無限大にアップする 超人脳の作り方

知的生産力が無限大にアップする 超人脳の作り方

超人脳というワードにひかれた訳ではないのだけど、『超人脳』はディベートのテキストと書かれているのをみて非常に興味を持った。
ディベートについては一度覗いてみたいと思っていたけれども、専門書を読んで取り組む余裕もなかったので、この本のようなレベルが丁度良かった。
「データ」「ワラント」「クレーム」の考え方は、きっちりと意識して実践していきたい。

サラダと厚揚げとシャンパン

夜ご飯
夜ご飯 posted by (C)cloned
数週間前の夜ご飯。見るからにひどい組み合わせなのだけど、シャンパンはどんなものに対しても柔軟においしいなと感じる。シャンパンは価格が高いので、普段はビールを楽しむことがほとんどだけど、たまには飲みたくなる。シャンパンで一番好きなのはGuy THIBAUTだけど、近所には売っていない。大阪箕面ボニリ・ジャパンが唯一の輸入元のようで、東京に居るとネットで買うしかなさそう。
厚揚げは表面を強火で焼いてあって少し焦げ目をつけてある。香ばしいところに鰹節と醤油が堪らない。
サラダはブロッコリーとアスパラ、あとはキュウリにベビーリーフがいる。生ハムはおまけ。
ブロッコリーとアスパラのサラダ
ブロッコリーとアスパラのサラダ posted by (C)cloned
米や肉がないけれども、たまにはちゃんと食べている。自分の中で一番外せないのがサラダで、それ以外は適当にローテーションしている感じ。とりたてて菜食主義ではないし肉も好きなのだけれども、がっつりしたものをお酒の相手にしてしまうとお腹がいっぱいになってしまうのであまり食べない。
シャンパン
シャンパン posted by (C)cloned
淡い黄金色に泡の昇るシャンパンは見ていて上品。また何かあったときには買ってきて飲むことにしよう。

Apple Hornのインサートブロックが割れた

メインギターのApple Horn-HGS Charcoal Blackのインサートブロックが割れてしまった。インサートブロックというのはロック式ギター(弦をナットで締め込んで固定してしまうギターのこと)のブリッジ側で弦を固定するための小さな直方体の部品。
弦を換えようとしたところ、インサートブロックの形状がおかしくなっていて動かなかったため、取り外してみたところ割れていた。インサートブロックがないと弦が張れないので、楽器店にいって発注しておいた。2週間前くらいに前に。
インサートブロック
インサートブロック posted by (C)cloned
真ん中が割れてしまったインサートブロック。右にあるのが購入したもの。結構派手に割れてしまった。インサートブロックを割ったのはこれで2回目なので自分がナットを締め過ぎということがわかった。弦がもし不意に外れてしまったら大変危険だという思いがあるからしっかり締めてしまうのが良くなかったのだろう。今後は少し手前で止めることにしよう。
Apple Hornは購入したときからずっとDean Markleyの弦を使っている。ゲージは10-46。ずっと以前はErnie Ballを使っていて、途中からD'Addarioの弦を使うようになった。Ernie Ballは張りたてだと高音の尖った金属音がしてきれいなのだけれど、錆びやすかったように思う。D’Addarioは少しのっぺりした音になるけど、頻繁に張り換えなくても音が安定していたので気に入っていた。Dean MarkleyはApple Hornの出荷時の弦で始めて使うことになったのだけど、アコースティックギターのような響きがするのですっかり気に入ってしまった。
そういえば最近ストラップを買った。ギターのストラップは細すぎたり太すぎたりしょぼかったりいかつすぎたりして、いつも気に入るものに出会わない。ところが、楽器店で軽くながめていたらPat Methenyのストラップがあってデザインも落ち着いた感じだった。値段も3,000円程度だったのでこれは良いと思って購入した。
Apple Horn-HGS Charcoal Black
Apple Horn-HGS Charcoal Black posted by (C)cloned
ここ2週間はIbanezの7弦ギターで我慢していたけれども、やっぱりApple Hornは最高に良い。

Yahoo! Topics BarのFirefox3.5対応バージョンをリリース

Yahoo! Topics Bar
Yahoo! Topics BarをFirefox3.5に対応させました。もしインストールされている方がいらっしゃいましたら大変お手数ですが、上記リンクより最新版をインストールください。自動アップデートには未だに対応していません。今回はバージョン番号の変更のみで、内容に変化はありません。WindowsMacFirefoxで動作確認しています。
改めてYahoo! Topics Barについて少し説明。

Yahoo! Topics BarはYahoo! JAPANトップページに掲載されるトピックスを定期的に取得して表示するだけの小さな拡張です。メールやRSSリーダのように更新通知は全くなく、何事もなかったかのように定期的にトピックスが入れ替わります。目に入ったタイトルに興味があればクリックしてYahoo!トピックスの該当ページに飛ぶことができます。一度クリックしたタイトルは薄い灰色になってより一層目立たなく表示されます(簡易的な既読管理です)。
もの凄く平たく言えば、定期的にYahoo!のトピックスを確認するのは大変だしRSSでがっつり閲覧する気もないけれども、なんとなく世間的に大きなニュースは目にしておきたい、という中途半端な要望にお答えする拡張です。
自分の為に作ったもので自分自身が未だにヘビーユーザーだけれども、もしどなたか使ってくれているのであればうれしいです。

iPhone 3GSを購入

iPhone 3GS
iPhone 3GS posted by (C)cloned
id:spiritlooseさんに釣られて購入した。というのは半分本当で半分嘘だけれども、3GSが出たのと料金体系が少し安くなっているようなので、購入することにした。
携帯電話などの申し込みではいつもそうだけれども、内容説明する前にまずは署名をさせようとする。途中で「それだったらやめます」と言い難くするというか後戻りできない空気にするのが目的だろうと思うけれども、毎度嫌だなと思う。
2年契約が常に更新されるというのには驚いた。3年目にやめても5年目にやめても9,975円支払えというのは辛いなぁ。あと2年と言いながら26ヶ月とかさ。しかも家に帰ってきて書類みたら署名してない注意事項のものが1枚あるけれども良いのだろうか。
というのは全部ソフトバンクの問題でiPhoneの問題ではないから、まぁ良いか。割れませんように。

参照と更新が頻繁に発生するテーブルでMyISAMとInnoDBを比較 その2

前回のエントリコメント頂いたので、試してみた。

my-huge.cnfに追加した項目は次の通り。

sync_binlog=1
innodb_buffer_pool_size = 1G
query_cache_size=0

テスト方法は前回と同じ。まずは、SELECTのみ。

スレッド数(1スレッド辺り200クエリ) MyISAM InnoDB
1 320ms 165ms
5 1030ms 518ms
20 1844ms 1747ms
50 4309ms 4320ms

InnoDBが速くなった、というのは自然として、MyISAMも速くなった。謎なのでもう少し時間のあるときにちゃんと調べる必要がある。

参照と更新が頻繁に発生するテーブルでMyISAMとInnoDBを比較

[追記 2012/09/29]
最近でもこの記事を参照してくださる方がいるので追記します。下記エントリを書いた時点では非常に局所的なケースで重い現象に悩まされていたことを前提に調査しており、その延長線上で「一律InnoDBというのは言い過ぎな印象を受けるパフォーマンス差に感じる」ということを書いてしまっていますが、その後色々と勉強した結果、特定箇所のニッチなベンチマークではなく一般的な運用上の負荷を焦点にした場合はInnoDBは適切に設定しておれば十分にパフォーマンスがある(もしくはInnoDBの方が有利)というのが現在の意見です。

「MyISAM InnoDB」で検索するとあちらこちらであるように、今時は理由がなければInnoDB、ということでMyISAMのテーブルをいくつかInnoDBに変更したところ、かなりパフォーマンスが落ちるケースがあった。
InnoDBにしたら軒並み遅くなったということではなくて、遅くなったのは参照と更新が同程度頻繁に発生するテーブルだった。InnoDBトランザクションのオーバーヘッドがあるので、ある程度遅くなることは仕方がないかもしれないけれども、かなり速度にダメージがあったので調べることにした。

細かいケースを比較したベンチが取りたいというよりは、実際に近い環境でどういう症状がでるのかを確認したかったので、Javaでスレッドを複数立ち上げてSELECTとUPDATEが入り乱れるようにして測定してみた。検証に使ったコードはだらだらと書いたひどいコードだけど最後に貼っておきます。

動作環境はMacParallels Desktopに2GBのメモリを割り当てたCentOS 5.3。インストールしたMySQLMySQL-server-community-5.1.34-0.rhel5.x86_64.rpmでmy.cnfはmy-huge.cnf.shをそのまま利用。作成したテーブルは次の2つ。

CREATE TABLE t_myisam (
id INT UNSIGNED NOT NULL,
value VARCHAR(100) NOT NULL,
created_at DATETIME NOT NULL,
updated_at TIMESTAMP,
PRIMARY KEY(id)
) ENGINE=MyISAM;
CREATE TABLE t_innodb (
id INT UNSIGNED NOT NULL,
value VARCHAR(100) NOT NULL,
created_at DATETIME NOT NULL,
updated_at TIMESTAMP,
PRIMARY KEY(id)
) ENGINE=InnoDB;

それぞれ100万件のデータを入れて測定。Javaの処理は、SELECTとUPDATEをx回繰り返すスレッドをy個作成するようになっている。idとvalueは常にランダムな値が使われる。数値は3回実行して一番速いものを採用。

スレッド数(1スレッド辺り200クエリ) MyISAM InnoDB
1 393ms 694ms
5 1331ms 2238ms
20 4924ms 7278ms
50 9873ms 22606ms

それなりにInnoDBが遅い。UPDATEを外してSELECTのみにしてみた。

スレッド数(1スレッド辺り200クエリ) MyISAM InnoDB
1 324ms 198ms
5 986ms 995ms
20 3567ms 3261ms
50 8949ms 9859ms

InnoDBのプライマリキーはクラスターインデックスなので速いと思ったけれども、MyISAMと同程度。逆にUPDATEのみにしてみた。

スレッド数(1スレッド辺り200クエリ) MyISAM InnoDB
1 217ms 459ms
5 979ms 1608ms
20 2037ms 5362ms
50 6075ms 14252ms

InnoDBはSELECTとUPDATEをそれぞれ足した時間がかかっているけれども、MyISAMはうまく並列処理をしている様子。あと、MyISAMはSELECTよりもUPDATEの方が速い。

次にプライマリキー以外にインデックスがある場合を測定してみる。

mysql> CREATE INDEX value ON t_myisam (value);
Query OK, 1000000 rows affected (9.79 sec)
Records: 1000000  Duplicates: 0  Warnings: 0
mysql> CREATE INDEX value ON t_innodb (value);
Query OK, 1000000 rows affected (2 min 13.81 sec)
Records: 1000000  Duplicates: 0  Warnings: 0

InnoDBのインデックス作成にかなりの時間がかかった。SELECTとUPDATEの両方を行ってみる。

スレッド数(1スレッド辺り200クエリ) MyISAM InnoDB
1 343ms 769ms
5 1351ms 2636ms
20 4977ms 11536ms
50 11539ms 33084ms

インデックスを作成する前に比べてMyISAMはそれほど遅くならないけれど、InnoDBはかなり遅くなった。

速度だけでストレージエンジンを選択することはできないし、MyISAMInnoDBだけを比べてもかなりサポートしている機能が違うけれども、一律InnoDBというのは言い過ぎな印象を受けるパフォーマンス差に感じる。用途によってはMyISAMは存分に活躍すると思う。

今回の例とは異なるけれども、MySQLによるデータウェアハウス構築 (Yahoo! JAPAN Tech Blog)も事例としては参考になるかな。

最後に検証に使ったJavaコード。

package net.clonedoppelganger.dbtest;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DB {
static {
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
private static final int DATA_MAX = 1000000;
private Connection conn = null;
private String table = null;
public DB(String table) {
this.table = table;
}
public void open() throws SQLException {
conn = DriverManager.getConnection(
"jdbc:mysql://xxx.xxx.xxx.xxx/xxx?user=xxx&password=xxx");
}
public void close() throws SQLException {
if (conn != null) {
conn.close();
}
}
public void importData() throws SQLException {
String truncate = "TRUNCATE TABLE %s";
String insert = "INSERT INTO %s VALUES (?, ?, NOW(), NULL)";
PreparedStatement stmt = null;
String query = String.format(truncate, table);
try {
stmt = conn.prepareStatement(query);
stmt.executeUpdate();
} finally {
if (stmt != null) {
stmt.close();
}
}
for (int i = 1; i <= DATA_MAX; i++) {
query = String.format(insert, table);
try {
stmt = conn.prepareStatement(query);
stmt.setInt(1, i);
stmt.setString(2, String.valueOf(Math.random()));
stmt.executeUpdate();
} finally {
if (stmt != null) {
stmt.close();
}
}
}
}
public void select() throws SQLException {
String select = "SELECT * FROM %s WHERE id = ?";
PreparedStatement stmt = null;
ResultSet rs = null;
String query = String.format(select, table);
int id = (int)(Math.random() * (DATA_MAX + 1));
try {
stmt = conn.prepareStatement(query);
stmt.setInt(1, id);
rs = stmt.executeQuery();
rs.next();
} finally {
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
}
}
public void update() throws SQLException {
String select = "UPDATE %s SET value = ? WHERE id = ?";
PreparedStatement stmt = null;
String query = String.format(select, table);
int id = (int)(Math.random() * (DATA_MAX + 1));
try {
stmt = conn.prepareStatement(query);
stmt.setString(1, String.valueOf(Math.random()));
stmt.setInt(2, id);
stmt.executeUpdate();
} finally {
if (stmt != null) {
stmt.close();
}
}
}
public static void ready(String table) {
DB db = null;
try {
db = new DB(table);
db.open();
db.importData();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (db != null) {
try {
db.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
int tryCount = 3;
int threadCount = 50;
int queryCount = 200;
String[] tables = {"t_myisam", "t_innodb"};
for (int i = 0; i < tables.length; i++) {
String table = tables[i];
//DB.ready(table);
long[] results = new long[tryCount];
for (int j = 0; j < tryCount; j++) {
Thread[] threads = new Thread[threadCount];
for (int k = 0; k < threadCount; k++) {
threads[k] = new Tester(table, queryCount);
}
long start = System.currentTimeMillis();
for (int k = 0; k < threadCount; k++) {
threads[k].start();
}
for (int k = 0; k < threadCount; k++) {
try {
threads[k].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
results[j] = end - start;
}
java.util.Arrays.sort(results);
for (int j = 0; j < results.length; j++) {
System.out.println(table + ": " + results[j] + "ms");
}
}
}
}
class Tester extends Thread {
private String table = null;
private int queryCount = 1;
public Tester(String table, int queryCount) {
this.table = table;
this.queryCount = queryCount;
}
public void run() {
DB db = null;
try {
db = new DB(table);
db.open();
for (int i = 0; i < queryCount; i++) {
try {
db.select();
db.update();
} catch (SQLException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (db != null) {
try {
db.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}

ジャパン・ビアフェスティバル2009in東京に行ってきた

6月7日(日)にジャパン・ビアフェスティバル2009in東京に行ってきた。写真をみる限りとんでもない人混みだと思っていたので少し遠慮気味な気持ちもあったけれど、ビール大好きなmochaさんとsamisさんとご一緒できることになったので喜んで参加した。
日曜日だったためか思ったよりも人混みではなく、飲みたいビールはすぐに飲むことができた。
知らないビールでおっと思ったのは牛久シャトービール。写真はここのグランベリーラガー。ランビックのフルーツものに比べて酸っぱさが全くないすっきりフルーツ味。
ビアフェス2009
ビアフェス2009 posted by (C)cloned
かなりたくさんのビールを飲んでわかったのが、有名どころはやはり非常に安定感のある味がするということ。まずいというのはないにしても、あまり名の知れていないビールはどことなくバランスが悪い感じを受けた。地ビールだけでもなかなか奥が深い。
関係ないけれども、うしとらの名刺が貼ってあるところがいくつかあって驚いた。