aspec7's garage

エンジニア生活の中で学んだことの備忘録

MongoDBでレプリケーション その2

前回レプリケーションの基本的な設定を確かめたので、今回はフェイルオーバーについて調べました。

アービターがある場合の最低構成の場合は、下記のような感じでレプリカセットが実現されています。


アービターが、プライマリサーバを決定するので、プライマリが動作停止するとセカンダリサーバをプライマリに昇格させます。
この仕組みがあるので、アービターが仕事していれば自動フェイルオーバーが機能します。


では、アービターがダウンするとどうなってしまうのかを図にしてみたのが下記の図です。
アービターがダウンしただけの状態(①)では、サービスは停止されません。
この状態ではレプリカセットが機能しているので、プライマリもセカンダリも役割を果たします。
なので、このときアービターを復旧すれば問題ありません。
しかし、アービターがダウン中にプライマリがダウンする多重障害の場合(②)は問題が生じます。


アービターがダウンしている最中にプライマリがダウンすると、プライマリ選択する役割がいない状態なので、フェイルオーバーが出来ません。
つまりセカンダリだけの状態になってしまいます。
こうなってしまうと、書き込みが出来ない読み取り専用の状態になってしまうのでサービス継続が難しくなります。

実際に動きを見てみます。
3つのノードが動いている事を確認します。
この例では、ポート番号が30000がセカンダリ、30001がプライマリ、30002がアービターです。

$ ps -ax|grep mongod
16959 ?? 0:40.91 /usr/local/Cellar/mongodb/2.4.5/mongod --dbpath=db2/ --replSet=myrep --port=30001 --fork --logpath /Users/hoge/mongodb/mongodb30001.log --logappend --auth --keyFile /Users/hoge/mongodb/db2.key --config /usr/local/etc/mongod.conf
18833 ?? 0:03.87 /usr/local/Cellar/mongodb/2.4.5/mongod --dbpath=db3/ --replSet=myrep --port=30002 --fork --logpath /Users/hoge/mongodb/mongodb30002.log --logappend --auth --keyFile /Users/hoge/mongodb/db3.key --config /usr/local/etc/mongod.conf
18858 ?? 0:03.93 /usr/local/Cellar/mongodb/2.4.5/mongod --dbpath=db1/ --replSet=myrep --port=30000 --fork --logpath /Users/hoge/mongodb/mongodb30000.log --logappend --auth --keyFile /Users/hoge/mongodb/db1.key --config /usr/local/etc/mongod.conf


まず、アービターをダウンさせます。

$ kill 18833


DBの状態を確認します。

myrep:SECONDARY> rs.status()
{
"set" : "myrep",
"date" : ISODate("2013-07-21T05:37:17Z"),
"myState" : 2,
"syncingTo" : "localhost:30001",
"members" : [
{
"_id" : 0,
"name" : "localhost:30000",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 932,
"optime" : Timestamp(1374384014, 1),
"optimeDate" : ISODate("2013-07-21T05:20:14Z"),
"self" : true
},
{
"_id" : 1,
"name" : "localhost:30001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 930,
"optime" : Timestamp(1374384014, 1),
"optimeDate" : ISODate("2013-07-21T05:20:14Z"),
"lastHeartbeat" : ISODate("2013-07-21T05:37:16Z"),
"lastHeartbeatRecv" : ISODate("2013-07-21T05:37:17Z"),
"pingMs" : 0
},
{
"_id" : 2,
"name" : "localhost:30002",
"health" : 0,
"state" : 8,
"stateStr" : "(not reachable/healthy)",
"uptime" : 0,
"lastHeartbeat" : ISODate("2013-07-21T05:37:16Z"),
"lastHeartbeatRecv" : ISODate("2013-07-21T05:37:08Z"),
"pingMs" : 0
}
],
"ok" : 1
}


アービターがダウンしていても、プライマリとセカンダリが機能している事がわかります。

続けて、プライマリをダウンさせます。

$ kill 16959


DBの状態を確認します。

myrep:SECONDARY> rs.status()
{
"set" : "myrep",
"date" : ISODate("2013-07-21T05:37:42Z"),
"myState" : 2,
"syncingTo" : "localhost:30001",
"members" : [
{
"_id" : 0,
"name" : "localhost:30000",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 957,
"optime" : Timestamp(1374384014, 1),
"optimeDate" : ISODate("2013-07-21T05:20:14Z"),
"errmsg" : "db exception in producer: 10278 dbclient error communicating with server: localhost:30001",
"self" : true
},
{
"_id" : 1,
"name" : "localhost:30001",
"health" : 0,
"state" : 8,
"stateStr" : "(not reachable/healthy)",
"uptime" : 0,
"optime" : Timestamp(1374384014, 1),
"optimeDate" : ISODate("2013-07-21T05:20:14Z"),
"lastHeartbeat" : ISODate("2013-07-21T05:37:42Z"),
"lastHeartbeatRecv" : ISODate("2013-07-21T05:37:31Z"),
"pingMs" : 0
},
{
"_id" : 2,
"name" : "localhost:30002",
"health" : 0,
"state" : 8,
"stateStr" : "(not reachable/healthy)",
"uptime" : 0,
"lastHeartbeat" : ISODate("2013-07-21T05:37:42Z"),
"lastHeartbeatRecv" : ISODate("2013-07-21T05:37:08Z"),
"pingMs" : 0
}
],
"ok" : 1
}


プライマリがダウンしましたが、アービターもダウンしているので、セカンダリはそのままです。
この状態ではデータは検索出来ますが、更新処理が行えません。

myrep:SECONDARY> db.getMongo().setSlaveOk()
myrep:SECONDARY> db.sample.insert({no:2,value:100})
not master
myrep:SECONDARY> db.sample.find({no:1})
{ "_id" : ObjectId("51eb6f2a3cb9ecfa7cfa46ad"), "no" : 1, "value" : 1 }


この状態で、アービターを復旧すればセカンダリはプライマリに昇格できます。

アービター構成の場合は、アービターのサービス停止には気をつけないといけませんね。

MongoDBでレプリケーションやってみた

MongoDBのレプリケーションを試してみたかったので、下記のサイトを参考にちょっと試して見ました。

MongoDBでゆるふわDB体験

今回試した動作環境は、下記のような感じです。
Mac OS X 10.9
MacBook Air (11-inch, Mid 2013)
CPU:Core i7 1.7GHz
Memory:8GB
MongoDB2.4.5

とりあえずHomebrewでMongoDBをインストールします。

$ brew install mongodb


次にDB用ディレクトリを作成します。

$ mkdir ~/mongodb
$ mkdir ~/mongodb/db1
$ mkdir ~/mongodb/db2
$ mkdir ~/mongodb/db3


MongoDBを起動します。実験なので3つのノードをポート番号を変えて動かします。

$ cd ~/mongodb
$ mongod --dbpath=db1/ --replSet=myrep --port=30000 --fork --logpath ~/mongodb/mongodb30000.log --logappend
about to fork child process, waiting until server is ready for connections.
forked process: 10267
all output going to: /Users/hoge/mongodb/mongodb30000.log
child process started successfully, parent exiting
$ mongod --dbpath=db2/ --replSet=myrep --port=30001 --fork --logpath ~/mongodb/mongodb30001.log --logappend
about to fork child process, waiting until server is ready for connections.
forked process: 9992
all output going to: /Users/hoge/mongodb/mongodb30001.log
child process started successfully, parent exiting
$ mongod --dbpath=db3/ --replSet=myrep --port=30002 --fork --logpath ~/mongodb/mongodb30002.log --logappend
about to fork child process, waiting until server is ready for connections.
forked process: 10014
all output going to: /Users/hoge/mongodb/mongodb30002.log
child process started successfully, parent exiting


30000番ポートのノードに接続して設定を施します。
30002番のポートはアービターとして動かします。

$ mongo --port=30000
> config = {
_id : "myrep",
members : [
{ _id : 0, host : "localhost:30000" },
{ _id : 1, host : "localhost:30001" },
{ _id : 2, host : "localhost:30002", arbiterOnly : true }
]
}
> rs.initiate(config)
{
"info" : "Config now saved locally. Should come online in about a minute.",
"ok" : 1
}


ここで設定内容が反映されてレプリケーションされているか確認します。

> rs.status()
{
"set" : "myrep",
"date" : ISODate("2013-07-18T11:05:48Z"),
"myState" : 1,
"members" : [
{
"_id" : 0,
"name" : "localhost:30000",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 927,
"optime" : Timestamp(1374145527, 1),
"optimeDate" : ISODate("2013-07-18T11:05:27Z"),
"self" : true
},
{
"_id" : 1,
"name" : "localhost:30001",
"health" : 1,
"state" : 3,
"stateStr" : "RECOVERING",
"uptime" : 17,
"optime" : Timestamp(0, 0),
"optimeDate" : ISODate("1970-01-01T00:00:00Z"),
"lastHeartbeat" : ISODate("2013-07-18T11:05:47Z"),
"lastHeartbeatRecv" : ISODate("2013-07-18T11:05:47Z"),
"pingMs" : 0,
"lastHeartbeatMessage" : "initial sync need a member to be primary or secondary to do our initial sync"
},
{
"_id" : 2,
"name" : "localhost:30002",
"health" : 1,
"state" : 3,
"stateStr" : "RECOVERING",
"uptime" : 15,
"lastHeartbeat" : ISODate("2013-07-18T11:05:47Z"),
"lastHeartbeatRecv" : ISODate("2013-07-18T11:05:48Z"),
"pingMs" : 0
}
],
"ok" : 1
}


30000番ポートの"stateStr"を見ると"PRIMARY"になっている事がわかります。
30001と30002番ポートはまだ準備が整っていないので、"stateStr"が"RECOVERING"になっています。
今一度確認してみます。

myrep:PRIMARY> rs.status()
{
"set" : "myrep",
"date" : ISODate("2013-07-18T11:13:13Z"),
"myState" : 1,
"members" : [
{
"_id" : 0,
"name" : "localhost:30000",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1372,
"optime" : Timestamp(1374145527, 1),
"optimeDate" : ISODate("2013-07-18T11:05:27Z"),
"self" : true
},
{
"_id" : 1,
"name" : "localhost:30001",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 462,
"optime" : Timestamp(1374145527, 1),
"optimeDate" : ISODate("2013-07-18T11:05:27Z"),
"lastHeartbeat" : ISODate("2013-07-18T11:13:12Z"),
"lastHeartbeatRecv" : ISODate("2013-07-18T11:13:11Z"),
"pingMs" : 0,
"syncingTo" : "localhost:30000"
},
{
"_id" : 2,
"name" : "localhost:30002",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"uptime" : 460,
"lastHeartbeat" : ISODate("2013-07-18T11:13:12Z"),
"lastHeartbeatRecv" : ISODate("2013-07-18T11:13:13Z"),
"pingMs" : 0
}
],
"ok" : 1
}


今度は、準備が整ったので30001番は"SECONDARY"、30002番は"ARBITER"となっている事が確認出来ました。
なにげにシェルのプロンプトも">"から"myrep:PRIMARY"になっています。

続いてプライマリ側にデータを入れて確認します。

myrep:PRIMARY> use test_db
switched to db test_db
myrep:PRIMARY> for (var i = 1; i <= 100000; i++) {
... db.sample.insert(
... {
... "no":i,
... "value":Math.floor(Math.random())
... }
... )
... }
myrep:PRIMARY> db.sample.count()
100000


セカンダリに接続して確認します。

$ mongo --port=30001
myrep:SECONDARY> use test_db
myrep:SECONDARY> db.getMongo().setSlaveOk()
myrep:SECONDARY> db.sample.count()
100000


ちゃんとデータが転送されている事が確認出来ました。
ちなみにスレーブ側では、このコマンド「db.getMongo().setSlaveOk()」を実行するのを忘れると下記のような感じで怒られます。

myrep:SECONDARY> db.sample.count()
Thu Jul 18 20:19:00.486 JavaScript execution failed: count failed: { "note" : "from execCommand", "ok" : 0, "errmsg" : "not master" } at src/mongo/shell/query.js:L180


次にフェイルオーバーを試して見ます。
プライマリーのノードをkillします。

$ kill プライマリのプロセスID


セカンダリから状態を確認してみます。
30000番ノードがダウンして、30001番がプライマリに昇格しているのが確認出来ました。

myrep:SECONDARY> rs.status()
{
"set" : "myrep",
"date" : ISODate("2013-07-18T11:21:53Z"),
"myState" : 1,
"members" : [
{
"_id" : 0,
"name" : "localhost:30000",
"health" : 0,
"state" : 8,
"stateStr" : "(not reachable/healthy)",
"uptime" : 0,
"optime" : Timestamp(1374146313, 9538),
"optimeDate" : ISODate("2013-07-18T11:18:33Z"),
"lastHeartbeat" : ISODate("2013-07-18T11:21:52Z"),
"lastHeartbeatRecv" : ISODate("2013-07-18T11:21:38Z"),
"pingMs" : 0
},
{
"_id" : 1,
"name" : "localhost:30001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1876,
"optime" : Timestamp(1374146313, 9538),
"optimeDate" : ISODate("2013-07-18T11:18:33Z"),
"self" : true
},
{
"_id" : 2,
"name" : "localhost:30002",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"uptime" : 974,
"lastHeartbeat" : ISODate("2013-07-18T11:21:52Z"),
"lastHeartbeatRecv" : ISODate("2013-07-18T11:21:53Z"),
"pingMs" : 0
}
],
"ok" : 1
}


ダウンした方は、再度起動すると自動的に復帰します。
ついでなので、新プライマリ側(30001番)にデータを追加してから、30000番側を起動してみます。
まずテストデータを追加します。

myrep:PRIMARY> for (var i = 100001; i <= 200000; i++) {
... db.sample.insert(
... {
... "no":i,
... "value":Math.floor(Math.random())
... }
... )
... }
myrep:PRIMARY> db.sample.count()
200000


次にダウンさせたサーバを起動します。

$ mongod --dbpath=db1/ --replSet=myrep --port=30000 --fork --logpath ~/mongodb/mongodb30000.log --logappend
about to fork child process, waiting until server is ready for connections.
forked process: 10267
all output going to: /Users/hoge/mongodb/mongodb30000.log
child process started successfully, parent exiting


30001番ノードで状態確認します。

myrep:PRIMARY> rs.status()
{
"set" : "myrep",
"date" : ISODate("2013-07-18T11:24:33Z"),
"myState" : 1,
"members" : [
{
"_id" : 0,
"name" : "localhost:30000",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 7,
"optime" : Timestamp(1374146597, 166),
"optimeDate" : ISODate("2013-07-18T11:23:17Z"),
"lastHeartbeat" : ISODate("2013-07-18T11:24:32Z"),
"lastHeartbeatRecv" : ISODate("2013-07-18T11:24:32Z"),
"pingMs" : 0,
"lastHeartbeatMessage" : "syncing to: localhost:30001",
"syncingTo" : "localhost:30001"
},
{
"_id" : 1,
"name" : "localhost:30001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 2036,
"optime" : Timestamp(1374146600, 11604),
"optimeDate" : ISODate("2013-07-18T11:23:20Z"),
"self" : true
},
{
"_id" : 2,
"name" : "localhost:30002",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"uptime" : 1134,
"lastHeartbeat" : ISODate("2013-07-18T11:24:32Z"),
"lastHeartbeatRecv" : ISODate("2013-07-18T11:24:33Z"),
"pingMs" : 0
}
],
"ok" : 1
}


30000番ノードがセカンダリで接続した事が確認出来ました。

最後に起動した30000番ノードに接続して、サーバの状態を確認します。

$ mongo --port=30000
myrep:SECONDARY> use test_db
switched to db test_db
myrep:SECONDARY> db.getMongo().setSlaveOk()
myrep:SECONDARY> db.sample.count()
200000


ダウン中に追加されたデータも、無事に同期されている事を確認出来ました。


非常に手軽にレプリケート機能をためせるあたりは流石だと思います。
ちなみにインストールからテスト完了まで、たらたらやって20分程度です。

Homebrewのアップデートエラー

前回PHPUnitをHomebrewでインストールしようとしたときにエラーが出たので、手動でFomulaを修正して対処していたけども、その後アップデートされたので以下のようにエラーが出るようになってしまいました。

$ brew update
error: Your local changes to the following files would be overwritten by merge:
Formula/phpunit.rb
Please, commit your changes or stash them before you can merge.
Aborting
Error: Failed to update tap: josegonzalez/php


ということで、brewはgitで管理されているので、手動で訂正した編集箇所を取り消します。
まずは上記のエラーが出ているファイルを調べます。

$ ls -l /usr/local/Library/Formula/phpunit.rb
lrwxr-xr-x 1 hoge admin 43 7 2 06:58 /usr/local/Library/Formula/phpunit.rb -> ../Taps/josegonzalez-php/Formula/phpunit.rb


「/usr/local/Library/Taps/josegonzalez-php/Formula/」に保存されている事が分かりました。
そこへ移動して、gitコマンドで変更内容を確認してみます。

$ cd /usr/local/Library/Taps/josegonzalez-php/Formula/
$ git stats -s
M abstract-php.rb
M phpunit.rb


変更した事が確認出来ました。
次にこの編集内容を取り消します。

$ git reset --hard
HEAD is now at 67eee32 Upgrade php5*-http and php5*-pthreads


これで編集内容を取り消したので、改めて「brew update」を実行します。

$ brew update
Updated Homebrew from 7fc8cc09 to 7fc8cc09.
==> Updated Formulae
josegonzalez/php/php53-imagick josegonzalez/php/php55-imagick
josegonzalez/php/php54-imagick josegonzalez/php/phpunit


今度はエラーも出る事無くアップデートする事が出来ました。

HomebrewからPHPUnit-3.7.22のインストール

HomebrewからPHPUnit-3.7.22をインストールしようとしたら、

Error: phpunit.phar does not exist

と怒られたので、

brew edit phpunit

を実行して以下のように編集した。

変更前:

libexec.install "phpunit.phar"

変更後:

libexec.install "phpunit-#{version}.phar"

上記でインストール完了。

Sublime Text 2 でPHPUnit

Sublime Text 2からPHPUnitプラグインを使って出来る事を調べてみたのでメモ。

ローカルでPHPUnit自体が動作するようにセットアップしている状態を前提とします。

Sublime Text 2にPHPUnitプラグインのセットアップ
    パッケージマネージャからPHPUnitをインストールします。





PHPUnitプラグインのコンフィグ
    コンフィグを開きます。
 
     コンフィグに以下の設定を追記します。phpunitへのパスは環境に合わせて変更のこと。

"path_to_phpunit": "/path/to/phpunit"


これでSublime Text 2からPHPUnitを実行する準備が整いました。

PHPUnitプラグインで出来る事

コンテキストメニューにある「PHPUnit -> Open Test Class」を実行する事で、実装クラスからテストクラスを開くことができます。
※ 実装クラスファイル上で右クリック。

 

対応しているクラスファイルが開きます。
 
逆にテストクラスからも、実装クラスを開く事が出来ます。
こんな感じで、画面分割して左と右に実装クラスとテストクラスを表示しておくと便利ですね。
 
プロジェクト内のphpunit.xmlを開くには、やはりコンテキストメニューから実行します。


phpunit.xmlは、プロジェクトに合わせて設定します。
サンプルとして以下のような感じにしました。




tests/




tests


src












単体で動かす場合はコンテキストメニューから「Run Tests ...」を。

 

まとめて動かす場合は「Run All Unit Tests...」を。

 

実行すると以下のようにコンソール上に結果が表示されます。
 
他にテストケースを作る為の便利ツールとしてコードスニペット機能があります。
エディタ上で以下の2つキーワードを入力してtabキーを押すとテストケースクラスとメソッドのスケルトンを自動生成してくれます。
 
    • phpunit-testcase:テストクラスの生成
class [ClassName]Test extends PHPUnit_Framework_TestCase
{
public function setUp()
{
// your code here
}

public function tearDown()
{
// your code here
}
}
/**
* @covers Class::Method
*/
public function test[testcase-name]()
{
// ----------------------------------------------------------------
// setup your test
//
// explain your test setup here if needed ...

// ----------------------------------------------------------------
// perform the change
//
// explain your test here if needed ...

// ----------------------------------------------------------------
// test the results
//
// explain what you expect to have happened

$this->markTestIncomplete('Not yet implemented');
}


だいたいこんな感じです。

PHP 5.5 のGeneratorを試してみました

PHP5.5で追加されたジェネレータを試して見ました。

ただ試すだけでは何なので、メモリの消費量と処理スピードを計測する形のサンプルです。
処理としては、CSVデータをランダム値で生成して、それを配列とジェネレータで処理した場合で比較しました。

define('CSV_DELIMITER', ',');

/**
* CSVデータをランダム作成
*
* @return string CSVデータを文字列で返す
*/
function makeCsv()
{
$row_count = 100000; // CSV行数
$column_count = 50; // CSV列数
$csv_string = NULL;
for ($row = 1; $row <= $row_count; $row++) {
$row_data = $row;
for ($col = 1; $col < $column_count; $col++) {
$row_data .= CSV_DELIMITER . rand(0, 10000);
}
$csv_string .= $row_data . PHP_EOL;
}
return $csv_string;
}

/**
* CSV => Array
*
* @param resource $csv_file CSVファイルハンドル
*
* @return array 配列データ(全レコード)
*/
function useArray($csv_file)
{
$csv = [];
while (($data = fgetcsv($csv_file, 0, CSV_DELIMITER)) !== FALSE) {
$csv[] = $data;
}
return $csv;
}

/**
* CSV => Generator
*
* @param resource $csv_file CSVファイルハンドル
*
* @return array 配列データ(1レコード)
*/
function useGenerator($csv_file)
{
while (($data = fgetcsv($csv_file, 0, CSV_DELIMITER)) !== FALSE) {
yield $data;
}
}

/**
* 実行時間計測
*
* @param resource $csv_file CSVファイルハンドル
* @param callable $callable 実行対象
*
* @return array 配列データ(1レコード)
*/
function timer($csv_file, callable $callable) {
$start = microtime(TRUE);
// 100回ループ
for ($i = 0; $i < 100; $i++) {
$callable($csv_file);
}
$stop = microtime(TRUE);
return $stop - $start;
}

// 計測用CSVデータの準備
echo 'NOW LOADING ....' . PHP_EOL;
$csv = makeCsv();
$tmp = tmpfile();
fwrite($tmp, $csv);

echo 'Go Measurement!!!' . PHP_EOL;

// メモリ消費量の計測
echo '--- Memory Check ---' . PHP_EOL;

echo ' Case Array ...' . PHP_EOL;
rewind($tmp);
$before = memory_get_usage();
$result = useArray($tmp);
$after = memory_get_usage();
echo ' ' . ($after - $before) . ' bytes' . PHP_EOL;
unset($result);

echo ' Case Generator ...' . PHP_EOL;
rewind($tmp);
$before = memory_get_usage();
$result = useGenerator($tmp);
$after = memory_get_usage();
echo ' ' . ($after - $before) . ' bytes' . PHP_EOL;
unset($result);

// 実行速度の計測
echo '--- Speed Check ---' . PHP_EOL;
echo ' Case Array ...' . PHP_EOL;
rewind($tmp);
$func = function($tmp) {foreach (useArray($tmp) as $time) {}};
echo ' ' . timer($tmp, $func) . ' sec' . PHP_EOL;

echo ' Case Generator ...' . PHP_EOL;
rewind($tmp);
$func = function($tmp) {foreach (useGenerator($tmp) as $time) {}};
echo ' ' . timer($tmp, $func) . ' sec' . PHP_EOL;

fclose($tmp);

実行結果はこんな感じになりました。

NOW LOADING ....
Go Measurement!!!
--- Memory Check ---
Case Array ...
916266008 bytes
Case Generator ...
872 bytes
--- Speed Check ---
Case Array ...
3.724415063858 sec
Case Generator ...
1.3189570903778 sec

Kindle4で洋書を読んでみた

先週、Steve Jobsの伝記が発売されたので、Kindleで購入してみました。
Kindleで予約して、配信開始後にインターネットに繋ぐとダウンロードは始まりすぐ読めるようになるのは、デジタルならではの体験でした。
とはいえ、英語で書かれた本を読むのは結構大変です。
わからない単語をいちいち検索して調べていたのでは、書かれた内容を理解する前に疲れてしまいます。

というわけで、今回は英和辞書の”英辞郎 Kindle対応版(サイト)”を購入して使ってみました。
これをKindleのDocumentに保存して設定を行えばKindle上で辞書が引けます。

まずダウンロードした英辞郎をPCに接続したKindle4に保存します。
PCを使っての操作はここまで。

次に、Kindle4のホームボタンを押してホーム画面を呼び出します。
ホーム画面上で、メニューボタンを押して「Settings」を選択します。

一番右のホームボタンを押してから、ホーム画面の状態でその隣のメニューボタンを押します。
メニューにある「Settings」を選択します。

「Settings」の画面の2ページ目、下から2段目にある「Dictionaries」を選択します。

「Dictionaries」を選択します。

開いた画面では、利用する辞書の名前が表示されています。そのままEnglish change defaultの行を選択します。

(すでに英辞郎に設定しているので写真は英辞郎が表示されています。)
 

登録されている辞書の中から英辞郎を選択します。
選択すると上記のデフォルト画面に戻って、利用辞書が英辞郎に設定された状態になります。

設定はこれだけです。
早速使ってみます。
購入した洋書を開きます。
ページが開いている状態でカーソルキーのを押すと、ページ内をカーソル移動できるようになります。
先に左右カーソルキーを押してしまうとセクション移動されてしまいます。間違えて押してしまったら、戻るキーで戻ります。
カーソル移動モードの状態になれば、左右キーでカーソルの移動も出来ます。

カーソルは単語区切りで移動します。
調べたい単語の先頭まで移動すると、その意味が辞書表示できるのが確認できます。
カーソル位置が、ページ上部なら画面下に、ページ下部なら画面上に辞書が表示されます。
最初は簡易表示の状態です。
更に詳細を調べる場合には、決定キー「・」を押します。
切り替わった表示の一番右にある「full definition」を選択します。

簡易表示が全画面の詳細表示に切り替わります。

という感じで、読みながらカーソルを気になった単語の銭湯に持ってくるだけで辞書が簡単に引けます。
いちいち文字を入力する必要が無いことで、読むリズムを極力崩さずに読んでいくことが出来ます。
ちなみに辞書に登録されていない単語は当然引くことが出来ません。
meとかinといった基本的な単語も引けませんが、困るレベルでは無いと思います。

洋書を読むことがツールひとつで劇的に改善されました。
不満な点も少しあります。
ひとつは、カーソルキーの操作感がいまいちな感触である点。個体差があるかも知れないですが、ボタンが少し堅いんです。
片手で持つとその硬さゆえに、持ち方を変えないといけません。
もう一つはPDFでは、機能しない点。
と不満点もありますが、それを差し引いても英語の文献を気軽に楽しむ環境が用意できるのは素晴らしいです。
またひとつ、ライフスタイルにちょっとした変化が加わった気分です。