mysqlでinnodbのindexが破損したらしい

mysqlで下のようなエラーが上がってしまった。なんかinnodbのindexが壊れてしまったらしい。

101126  8:40:35  InnoDB: Page checksum 2191097623, prior-to-4.0.14-form checksum
1133608065
InnoDB: stored checksum 407406258, prior-to-4.0.14-form stored checksum 11336080
65
InnoDB: Page lsn 27 2241249557, low 4 bytes of lsn at page end 2241249557
InnoDB: Page number (if stored to page already) 161913,
InnoDB: space id (if created with >= MySQL-4.1.1 and stored already) 192
InnoDB: Page may be an index page where index id is 517
InnoDB: (index "post_date" of table "wordpress"."wp_posts")
InnoDB: Database page corruption on disk or a failed
InnoDB: file read of page 161913.
InnoDB: You may have to recover from a backup.
InnoDB: It is also possible that your operating
InnoDB: system has corrupted its own file cache
InnoDB: and rebooting your computer removes the
InnoDB: error.
InnoDB: If the corrupt page is an index page
InnoDB: you can also try to fix the corruption
InnoDB: by dumping, dropping, and reimporting
InnoDB: the corrupt table. You can use CHECK
InnoDB: TABLE to scan your table for corruption.
InnoDB: See also http://dev.mysql.com/doc/refman/5.1/en/forcing-recovery.html
InnoDB: about forcing recovery.
InnoDB: Ending processing because of a corrupt database page.

ググったけどなかなか良い情報にたどり着けない。
mysqldのインスタンスも死んでるから、drop index -> add indexとかもできなかった。

公式マニュアルを参考するとinnodb_force_recoveryオプションを付けて起動すると強制的に起動できるらしい。
この変数はレベルのようになっているらしくて、数字が高いほど強制度合いが高いらしい。
このオプションがついている間は他の更新を止めてくれるらしい。

で、my.cnfにオプションを書いて起動させる。 -> バッチリ起動する。
まずはインデックスを作り直そうと思ってdrop indexさせようとする。

mysql> ALTER TABLE `wordpress`.`wp_posts` DROP INDEX `type_date`;
ERROR 1031 (HY000): Table storage engine for 'wp_posts' doesn't have this option

と思ったら通らなかったです。
innodb_force_recoveryを4で起動してたから、1でならいけるかな、と思ったけどダメでした。
どうすれば良いのか分からなかったので適当にググると
Recovering Innodb table Corruption
というサイトが。
早速試してみる。


mysql> check table wordpress.wp_posts;
+--------------------------+-------+----------+-------------------------------------------------------+
| Table                    | Op    | Msg_type | Msg_text                                              |
+--------------------------+-------+----------+-------------------------------------------------------+
| wordpress.wp_posts       | check | Warning  | InnoDB: The B-tree of index 'post_date' is corrupted. |
| wordpress.wp_posts       | check | error    | Corrupt                                               |
+--------------------------+-------+----------+-------------------------------------------------------+
2 rows in set (1 min 19.67 sec)

mysql> optimize table wordpress.wp_posts;
+--------------------------+----------+----------+-------------------------------------------------------------------+
| Table                    | Op       | Msg_type | Msg_text                                                          |
+--------------------------+----------+----------+-------------------------------------------------------------------+
| wordpress.wp_posts       | optimize | note     | Table does not support optimize, doing recreate + analyze instead |
| wordpress.wp_posts       | optimize | error    | Got error -1 from storage engine                                  |
| wordpress.wp_posts       | optimize | status   | Operation failed                                                  |
+--------------------------+----------+----------+-------------------------------------------------------------------+
3 rows in set, 2 warnings (0.05 sec)

mysql> ANALYZE TABLE wordpress.wp_posts;
+--------------------------+---------+----------+----------+
| Table                    | Op      | Msg_type | Msg_text |
+--------------------------+---------+----------+----------+
| wordpress.wp_posts       | analyze | status   | OK       |
+--------------------------+---------+----------+----------+
1 row in set (0.01 sec)

ここまで来たけど結局直りませんでした。

phpのopenlogのコストを見る

phpのopenlogのコストを見る。


$start = microtime(true);
openlog("myScripLog", LOG_PID, LOG_USER);
for($i=0; $i<100000; ++$i){
syslog(LOG_INFO, 'opened');
}
echo 'time past '.(microtime(true) - $start);

これで結果が

time past 0.901854038239

対して時間がかかってない。

今度は毎度ログを開き直す。


$start = microtime(true);
for($i=0; $i<100000; ++$i){
openlog("myScripLog", LOG_PID, LOG_USER);
syslog(LOG_INFO, 'opened');
closelog();
}
echo 'time past '.(microtime(true) - $start);

time past 2.22855091095

うーん、二倍ちょっとのコストになっちゃった。

ここでcloselogのコストが気になったので。


<?php
$start = microtime(true);
for($i=0; $i<100000; ++$i){
 openlog("myScripLog".rand(0,1), LOG_PID, LOG_USER);
 syslog(LOG_INFO, 'opened'.$i);
 //closelog();
}
echo 'time past '.(microtime(true) - $start);

%php syslog.php
time past 1.11285591125

意外にも早いぞ。

どうやらcloselog()しなければ早くいけるのか?な?

phpで一つのプロセスから複数のopenlogをするとごちゃごちゃになる

phpで一つのプロセスから複数のopenlogをするとごちゃごちゃになる。

openlog("myScripLog", LOG_PID, LOG_LOCAL0);
syslog(LOG_INFO, 'opened');
openlog("myScripLog", LOG_PID, LOG_USER);
syslog(LOG_INFO, 'dupricated opening.');

後からopenlogしたやつを上書きでopenlogする。
このケースだと最初のopenedはlocal0に書き出され、dupricated openingはuserに書き出される。
エラーは特に見られなかった。おかげさまで参った参った。

pearのLogを使うとどうだろう。

require_once('Log.php');
$log_local0 = Log::singleton('syslog', LOG_LOCAL0, 'myScriptLog');
$log_user = Log::singleton('syslog', LOG_USER, 'myScriptLog');

$log_local0->log('log to local0', LOG_INFO);
$log_user->log('log to user', LOG_INFO);
$log_local0->log('log to local0', LOG_INFO);
$log_user->log('log to user', LOG_INFO);

これだと、最初のログはLocal0に出るけど、そのあとの三つのログはuser側に流れちゃう。
これ直すとしたら面倒だなぁ・・・。メモだけにしておこう。

phpのログファシリティとか

http://www.php.net/manual/ja/function.openlog.php

LOG_AUTH 32 セキュリティ/認証用メッセージ (定数 LOG_AUTHPRIV が定義されているシステムでは、代わりにそれを使用してください)
LOG_AUTHPRIV 80 セキュリティ/認証 メッセージ (プライベート)
LOG_CRON 72 クロックデーモン (cron や at)
LOG_DAEMON 24 他のシステムデーモン
LOG_KERN 0 カーネルメッセージ
LOG_LOCAL0LOG_LOCAL7 128-184 ローカルでの使用のために確保されているもので、Windows では使用できません
LOG_LPR 48 ラインプリンタサブシステム
LOG_MAIL 16 メールサブシステム
LOG_NEWS 56 USENET ニュース サブシステム
LOG_SYSLOG 40 syslogd で内部的に生成されたメッセージ
LOG_USER 8 一般的なユーザレベルのメッセージ
LOG_UUCP 64



BBC radioをブラウザ以外で視聴したい

BBC radioをブラウザ以外で視聴したい。

ストリームのURLがあった。

http://bbcstreams.com/

FreeBSDでntpサーバを立ててみる

FreeBSDでntpサーバを立ててみる。

内部のLANの台数がそれなりになってきたので、内部LAN向けにntpサーバを立ててみようと。

今回サーバにするホストはFreeBSD7.3-Rなので標準でntpdが付属してきた。

「NTPD」の設定

confはほぼ参考サイト通りに。

restrict default nomodify notrap noquery
restrict 127.0.0.1
restrict 192.168.0.0 mask 255.255.0.0 nomodify notrap

driftfile /etc/ntp/drift

server ntp.asahi-net.or.jp
# http://www.ocn.ne.jp/ocnweb/support/staff2/ntp.html
server ntp-tk01.ocn.ad.jp
#server ntp-tk02.ocn.ad.jp
server ntp-os01.ocn.ad.jp

#logfile /var/log/ntp

logfileを指定しないとsyslogにはき出す。自分の環境ではログサーバに集約してるのでsyslogに流すようにする。

syslogに流す内容も色々指定できるらしい。詳しくはman ntp.confを参照。

DBのコネクションを張るコストについてちょっとだけ考察してみた

DBのコネクションを張るコストについてちょっとだけ考察してみた。

お題はMDB2でコネクションを新規に張るのと、コネクションを張りっぱなしでクエリを投げるのでどれぐらいコストが高くなるのか。

たぶんmysql側でコネクションプーリング?があるような気が。アプリ側(php側)からみたらプーリングされてないけど、たしかサーバ側でコネクションを張るコストを抑える努力をしてたような、してなかったような。

回数は10,000回。クエリは単純なやつで。


for($i = 0; $i&lt; MAX_ATTEMPT; ++$i){
if(!($i%10)){echo $i.' ';}
$mdb2 = MDB2::connect($dsn);
$mdb2-&gt;query('select * from test limi 1;');
$mdb2-&gt;disconnect();
}
//sleep(1);
echo "\n total ".(getMtime()-$start)."\n";

これで結果が

total 113.676899195


for($i = 0; $i&lt; MAX_ATTEMPT; ++$i){
if(!($i%10)){echo $i.' ';}
$mdb2 = MDB2::singleton($dsn);
$mdb2-&gt;query('select * from test limi 1;');
//$mdb2-&gt;disconnect();
}
//sleep(1);
echo "\n total ".(getMtime()-$start)."\n";

こっちは

total 6.34343004227

めっちゃはえええ!

10000回あたりでコネクションを張るコストが100秒ということで、一回のコネクションあたり10msぐらいかかるのかな?結構早いなぁ・・・。

ついでにPDOでも試してみた。


$start = getMtime();
for($i = 0; $i&lt; MAX_ATTEMPT; ++$i){
if(!($i%10)){echo $i.' ';}
try{
$pdo = new PDO("mysql:host=db.l2tp.org; dbname=test",'yousan',""\
);
}catch(PDOException $e){
var_dump($e-&gt;getMessage());
}
$stmt=$pdo-&gt;query('select * from test limit 1;');
$pdo = null;
}
echo "\n total ".(getMtime()-$start)."\n";

total 90.24330616

MDB2にくらべて75%ぐらいの時間しかかかってない。


$start = getMtime();
try{
$pdo = new PDO("mysql:host=db.l2tp.org; dbname=test",'yousan',"");
}catch(PDOException $e){
var_dump($e-&gt;getMessage());
}
for($i = 0; $i&lt; MAX_ATTEMPT; ++$i){
if(!($i%10)){echo $i.' ';}
$stmt=$pdo-&gt;query('select * from test limit 1;');
}
echo "\n total ".(getMtime()-$start)."\n";

total 3.78206896782

こちらも早い。クエリの発行が早いっぽいっすね。60%ぐらいになってる。

ちなみに現在、60クエリ/秒ぐらい吐かれているので、うーん、PDOに変えるメリットは・・・、よくわかんないです。

ファイルが多いlogのローテートについて

ファイルが多いlogのローテートについて。

syslog-ngをつかってログの集約サーバを運用している。

ホスト数が小さい、記録するログが少ない場合にはFreeBSD標準のnewsyslogでまぁそれなりにまかなえていたんだけど、ファイル数が増えてきてしまって、いちいちエントリを書くのが面倒になってきた。

特にプログラム名(syslogでいうタグ)をベースにファイルを作るようにしたので、個別のプログラムごとにファイルができてしまう。

そしてそれが追加されるホストごとにできるっていう。

こまったなぁ、なんかこういうのを解決してくれるパッケージないかなぁ、って思ってたら、@tkroroさんから「logrotateでできない?」とツッコミが。

linux標準(一部除く)のログローテートツールで、ちらちら見かけてはいたんだけどどうせできないだろうと思ってスルーしてた。できなさそうな雰囲気を感じて・・。

変な先入観を捨てて、転がってたcentくんで試してみる。confを引数に持たせることで一発だけ実行できた。

で、*をつかってディレクトリ全部とかいけたので採用。

具体的には

/var/log/hostA/messages

/var/log/hostB/messages

こんなファイルを

/var/log/*/messages

っていうエントリで処理できる。これは便利。

後はプログラムごとのやつとか、apache関連は圧縮するとか指定をいれながらぽちぽち。

ただどうもローテーション部分?(後ろに数字をつけてmvするあたり)がうまくいかなかったり(1, 1.1, 2.1, 1.2とかいっぱいできてしまった)、ファイルハンドルをsyslog-ngが握ったままで圧縮中のログは漏らしてるっぽかったり。

ひとまずは運用してみて調整しよう、と。

以上。

pearのlogパッケージを使ってみた

pearのlogパッケージを使ってみた。

pearだとLogginの中のLogっていうパッケージ名。

System_Daemonとかだと実際のファイルの位置とか、pearディレクトリ以下の、System/Daemon.phpっていうところにあるんだけど、Logパッケージはpear直下にLog.phpという名前である。

これってどういうことなのかな。よくわかんない。MDB2とかも直下にあるけどね。

元々はpearのSystem_Daemonでsyslogの出力ができないか模索してて、無さそうだったので勝手にパッチ書いて機能を追加しようとしてた。

そんな折、ソースコードを見てみるとusePEARLogInstanceなんていう文字が。


require_once('Log.php');

'usePEARLogInstance' =&gt; array(
'type' =&gt; 'boolean|object',
'default' =&gt; false,
'punch' =&gt; 'Accepts a PEAR_Log instance to handle all logging',
'detail' =&gt; 'This will replace System_Daemon\'s own logging facilit\
y',
'required' =&gt; true,
),

早速これを使ってログを吐き出させてみる。

元々System_Daemonをラッピングするようなクラスを作っているので、そこの初期化とかさせるところにLogインスタンスを作る。


require_once('Log.php');

public function start($appname, $port){
$log = Log::singleton('syslog', 'LOG_USER', $appname);
System_Daemon::setOption("appName", $appname);
System_Daemon::setOption('logFilePosition', true);
System_Daemon::setOption('usePEARLogInstance', $log);
System_Daemon::start();

引数などはマニュアル参照。このパッケージはpear形式なマニュアルがない代わりに公式ページにあるタイプ。かなり整備されてる感じ。まだほとんど読んでないけど、きっと困ったときに何とかなるであろう感じ。

System_Daemonももうちょっとマニュアルが整備されればなー、って思ってしまう。もうかなりできあがったパッケージだと思うんだけどな。

で、実際に動かしたら何のことはなくちゃんと動いてくれた。

ログファシリティはデーモンだったのでLOG_USERで送ってタグをデーモンのappnameにして受け取るようにした。