PHPのPDOやMySQLiでlocalhostの名前解決が出来ない原因

“localhost”という名前を使ったときに、エラーになることがありました。

$pdo = new PDO('mysql:host=localhost;port=8889;dbname=mysql', "root", "root");

↑これで、localhostの名前解決をしてくれません。どうやらUnixDomainSocketに繋がるようです。”localhost”の代わりに、”127.0.0.1″だと問題なく出来ました。

原因としては、↓のようです。

When “localhost” is specified, MySQL uses a socket connections (e.g. /var/run/mysqld/mysqld.sock). “127.0.0.1” forces it to connect using a TCP connection. Your problem may occur because your MySQL client and MySQL server are configured to use different socket paths. This may happen e.g. if PHP and MySQL are installed from different sources, or if either of them somehow picks up its configuration from the wrong config file.

https://drupal.org/node/683564


環境構築の過程でうまくいかなかった場合、”localhost”を指定してもUnixDomainSocketが優先されることもあるようですね。。。名前解決を期待せずに”127.0.0.1″を素直に使うと良いかもしれません。

PHP の MySQLi を使って、MySQL のバージョンとサイズを求める

表題のように、MySQL のバージョンとサイズを求めてみました。
mysqli は PDO と違って少々分かりにくかったです。。。
ホストやポート、データベース名は適宜書き換えてください。

コード

<?php

$mysqli = new mysqli("localhost:8889", "root", "root", "mysql");
$query = $mysqli->prepare("select version()");
$query->execute();
$query->bind_result($version);
$query->fetch();
echo 'MySQL version: ' . $version . "\n";
$query->close();

$query = $mysqli->prepare("SELECT concat(round(sum(data_length+index_length)/(1024*1024),2)) total_size FROM information_schema.TABLES");
$query->execute();
$query->bind_result($current_usage);
$query->fetch();
$query->close();
echo 'MySQL Current Usage (MB): ' . $current_usage . "\n";


実行結果
MySQL version: 5.5.29
MySQL Current Usage (MB): 0.44

Redis 2.8系で Redis Sentinel を試してみた

前回の記事では、serf を試してみました。serf は、

– ノードのリストを提示
– 障害検知
– イベントフック

をしてくれるものでした。しかしながらノード単位の検出です。もう少し細かい粒度で、つまりプロセス単位でやりたいですね。。。

私はよく Redis を使います。そこで今回は、Redis プロセスを監視し、フェイルオーバーしてくれる Redis Sentinel を使ってみました。イベントフックも出来るようですが試していません、そのうちやります。

今回試したことは、
– フェイルオーバー
のみです。

Redis の2.6系と2.8系では、項目名など異なるようなので注意してください。
公式ドキュメントとして、Redis Sentinel Documentation を参考にしました。

今回は一台のマシンで試してみました。
本実験で一台のマシンに存在するプロセスは、

– Redis のマスター (1個。127.0.0.1:6379)
– Redis のスレイブ (2個。127.0.0.1:16379, 127.0.0.1:16380)
– Redis Sentinel (3個。127.0.0.1:26379, 127.0.0.1:26380, 127.0.0.1:26381)

です。合計6個です。

まず、マスター*1とスレイブ*2を立ち上げます。ターミナルのタブを大量に使います^^;

# マスター
# redis-2.8.x (x は人それぞれ)のディレクトリに移動して
./src/redis-server

# (新しくターミナルのタブを開いて) スレイブその1
./src/redis-server --port 16379 --slaveof 127.0.0.1 6379

# (新しくターミナルのタブを開いて) スレイブその2
./src/redis-server --port 16380 --slaveof 127.0.0.1 6379

実際に動作するか確認してみます。

# マスターに対して、値をセットしてみます。
$ ./src/redis-cli
127.0.0.1:6379> set key1 value1
OK
127.0.0.1:6379> get key1
"value1"

# スレイブその1から、値を見てみます (↑の値、value1 が出てくればOKです)
$ ./src/redis-cli -p 16379
127.0.0.1:16379> get key1
"value1"

# スレイブその2
$ ./src/redis-cli -p 16380
127.0.0.1:16380> get key1
"value1"

次に、Sentinel を3個立ち上げます。なぜ1つではなく複数の Sentinel を立ち上げるかといえば、投票を行い閾値を超えた充足があるかどうか判定するとのことです。

まず、コンフィグファイルです。3つ必要です。以下のようなファイルを作ります。

sentinel01.conf
# port <sentinel-port>
port 26379
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 5000
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 900000
# sentinel parallel-syncs <master-name> <numslaves>
sentinel config-epoch mymaster 2

sentinel02.conf
# port <sentinel-port>
port 26380
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 5000
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 900000
# sentinel parallel-syncs <master-name> <numslaves>
sentinel config-epoch mymaster 2

sentinel03.conf
# port <sentinel-port>
port 26381
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 5000
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 900000
# sentinel parallel-syncs <master-name> <numslaves>
sentinel config-epoch mymaster 2

># sentinel monitor
>sentinel monitor mymaster 127.0.0.1 6379 2

↑この が閾値となります。この場合ですと sentinel プロセスの2個以上が判断を下したら、そのとおりに行うという意味になります。今回は3つの sentinel プロセスを使いますので、多数決として、quorum には2を選びました。sentinel プロセスが5個なら、quorum は3が妥当なところかなと思います。

なお、このconfファイルは、運用していてファイルオーバーなどが起きると書き換えが起こります。具体例を挙げますと、フェイルオーバーして slave がマスターに昇格すると、.confファイルの監視対象のマスターのアドレスとポートが書き換えられます。一旦sentinelを落としても次回起動時は、素直に.confファイルを読むだけで問題なく動作するというわけです。手動書き換えが必要ないということです。

さて、↑に挙げた3つのコンフィグファイルを使って、sentinel プロセスを立ち上げます。

# sentinel その1の立ち上げ
$ ./src/redis-sentinel sentinel01.conf

# 別のターミナルのタブで、sentinel その2の立ち上げ
$ ./src/redis-sentinel sentinel02.conf

# 別のターミナルのタブで、sentinel その3の立ち上げ
$ ./src/redis-sentinel sentinel03.conf

↑で、その3を立ち上げた時には以下のようになります。

$ ./src/redis-sentinel sentinel03.conf 
[4288] 02 Jan 02:47:23.210 * Max number of open files set to 10032
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 2.8.2 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26381
 |    `-._   `._    /     _.-'    |     PID: 4288
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

[4288] 02 Jan 02:47:23.211 # Sentinel runid is a8b457ca95c446dccb9a35fbc5c3befaed7899c1
[4288] 02 Jan 02:47:23.212 * +slave slave 127.0.0.1:16379 127.0.0.1 16379 @ mymaster 127.0.0.1 6379
[4288] 02 Jan 02:47:23.212 * +slave slave 127.0.0.1:16380 127.0.0.1 16380 @ mymaster 127.0.0.1 6379
[4288] 02 Jan 02:47:23.490 * +sentinel sentinel 127.0.0.1:26380 127.0.0.1 26380 @ mymaster 127.0.0.1 6379
[4288] 02 Jan 02:47:24.716 * +sentinel sentinel 127.0.0.1:26379 127.0.0.1 26379 @ mymaster 127.0.0.1 6379

↑slave一覧と、sentinel一覧が表示されていますね。どうやらRedisのpubsub機能を用いて、リスト管理しているようです。賢いですね。

さて、ひとまず
– Redis のマスター * 1
– Redis のスレイブ * 2
– マスターを監視する Sentinel * 3
を立ち上げました。

次に、マスターを落としてみて、slave のどちらかが昇格するかどうか見てみます。

# マスターのプロセスを探します
$ ps aux | grep redis-server
root      3793  0.0  0.0  37944  2344 pts/0    Sl+  02:29   0:00 ./src/redis-server *:6379
root      3895  0.0  0.0  37944  2372 pts/1    Sl+  02:30   0:00 ./src/redis-server *:16379                              
root      3899  0.0  0.0  37944  2372 pts/2    Sl+  02:30   0:00 ./src/redis-server *:16380                              
root      4454  0.0  0.0   7828   880 pts/9    S+   02:54   0:00 grep redis-server

# ↑より、どうやら 3793 のプロセスIDのようなので、それを kill します。
$ kill -9 3793

↑が行われたとき、sentinel プロセスがどういうログを出力しているか見てみます。

[4288] 02 Jan 02:47:23.211 # Sentinel runid is a8b457ca95c446dccb9a35fbc5c3befaed7899c1
[4288] 02 Jan 02:47:23.212 * +slave slave 127.0.0.1:16379 127.0.0.1 16379 @ mymaster 127.0.0.1 6379
[4288] 02 Jan 02:47:23.212 * +slave slave 127.0.0.1:16380 127.0.0.1 16380 @ mymaster 127.0.0.1 6379
[4288] 02 Jan 02:47:23.490 * +sentinel sentinel 127.0.0.1:26380 127.0.0.1 26380 @ mymaster 127.0.0.1 6379
[4288] 02 Jan 02:47:24.716 * +sentinel sentinel 127.0.0.1:26379 127.0.0.1 26379 @ mymaster 127.0.0.1 6379
[4288] 02 Jan 02:54:24.790 # +sdown master mymaster 127.0.0.1 6379
[4288] 02 Jan 02:54:24.848 # +odown master mymaster 127.0.0.1 6379 #quorum 2/2
[4288] 02 Jan 02:54:24.848 # +new-epoch 3
[4288] 02 Jan 02:54:24.848 # +try-failover master mymaster 127.0.0.1 6379
[4288] 02 Jan 02:54:24.848 # +vote-for-leader a8b457ca95c446dccb9a35fbc5c3befaed7899c1 3
[4288] 02 Jan 02:54:24.849 # 127.0.0.1:26379 voted for a8b457ca95c446dccb9a35fbc5c3befaed7899c1 3
[4288] 02 Jan 02:54:24.849 # 127.0.0.1:26380 voted for a8b457ca95c446dccb9a35fbc5c3befaed7899c1 3
[4288] 02 Jan 02:54:24.948 # +elected-leader master mymaster 127.0.0.1 6379
[4288] 02 Jan 02:54:24.948 # +failover-state-select-slave master mymaster 127.0.0.1 6379
[4288] 02 Jan 02:54:25.006 # +selected-slave slave 127.0.0.1:16380 127.0.0.1 16380 @ mymaster 127.0.0.1 6379
[4288] 02 Jan 02:54:25.006 * +failover-state-send-slaveof-noone slave 127.0.0.1:16380 127.0.0.1 16380 @ mymaster 127.0.0.1 6379
[4288] 02 Jan 02:54:25.062 * +failover-state-wait-promotion slave 127.0.0.1:16380 127.0.0.1 16380 @ mymaster 127.0.0.1 6379
[4288] 02 Jan 02:54:25.815 # +promoted-slave slave 127.0.0.1:16380 127.0.0.1 16380 @ mymaster 127.0.0.1 6379
[4288] 02 Jan 02:54:25.815 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
[4288] 02 Jan 02:54:25.872 * +slave-reconf-sent slave 127.0.0.1:16379 127.0.0.1 16379 @ mymaster 127.0.0.1 6379
[4288] 02 Jan 02:54:26.817 * +slave-reconf-inprog slave 127.0.0.1:16379 127.0.0.1 16379 @ mymaster 127.0.0.1 6379
[4288] 02 Jan 02:54:27.842 * +slave-reconf-done slave 127.0.0.1:16379 127.0.0.1 16379 @ mymaster 127.0.0.1 6379
[4288] 02 Jan 02:54:27.893 # +failover-end master mymaster 127.0.0.1 6379
[4288] 02 Jan 02:54:27.893 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 16380
[4288] 02 Jan 02:54:27.893 * +slave slave 127.0.0.1:16379 127.0.0.1 16379 @ mymaster 127.0.0.1 16380
[4288] 02 Jan 02:54:27.900 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 16380
[4288] 02 Jan 02:54:32.963 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 16380

↑では、
>[4288] 02 Jan 02:54:24.790 # +sdown master mymaster 127.0.0.1 6379
で127.0.0.1:6379のプロセスが落ちている、と判断しています。sdown は subjectively downですね。次に、
>[4288] 02 Jan 02:54:24.848 # +odown master mymaster 127.0.0.1 6379 #quorum 2/2
となっています。odown、つまり objectively down となります。quorum を満たしたことが分かります(他の2つのsentinelプロセスも、sdown 判定したということ)。
そして、リーダーを選出したりスレイブの書き換えが行われています。
>sentinel down-after-milliseconds mymaster 5000
と設定したとおり、約5秒で切り替わっていますね。

>[4288] 02 Jan 02:54:25.815 # +promoted-slave slave 127.0.0.1:16380 127.0.0.1 16380 @ mymaster 127.0.0.1 6379
とありますが、2つのスレイブのうち、127.0.0.1:16380 の方がマスターに昇格したようです。
実際に見てみます↓

$ ./src/redis-cli -p 16380
127.0.0.1:16380> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=16379,state=online,offset=69658,lag=0
master_repl_offset:69658
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:69657
127.0.0.1:16380> 

確かに、”role:master”となっていますね。さらに、slaveも再設定されています。いたせりつくせりですね、さすが Redis という感じがします。

ちなみに、自動的に書き換わった sentinel のコンフィグファイルを見てます。

$ cat sentinel01.conf 
# port <sentinel-port>
port 26379
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 16380 2
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 5000
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 900000
# sentinel parallel-syncs <master-name> <numslaves>
sentinel config-epoch mymaster 3
# Generated by CONFIG REWRITE
dir "/root/my_repos/software/redis-2.8.2"
sentinel known-slave mymaster 127.0.0.1 16379
sentinel known-slave mymaster 127.0.0.1 6379
sentinel known-sentinel mymaster 127.0.0.1 26380 0a6f3fbd3ed7f5323918ef4f1cadc3a798b68bfa

sentinel known-sentinel mymaster 127.0.0.1 26381 a8b457ca95c446dccb9a35fbc5c3befaed7899c1

↑実際に書き換わっていますね。

今回は以上になります。イベントフックや、アプリケーション・サーバーがどのようにして昇格したマスターにアクセスするか(/etc/hosts を書き換えるとか、iptables だとか、VIP だとかいろいろとありますね)についてはいつか検証して見る予定です。