Bash
MySQL
shell

MySQLのデータの結果を監視してメールを送信するバッチ(cron設定する)

More than 1 year has passed since last update.

作ろうと思ったきっかけ

データベース上でデータ不整合が発生する現象が発生した。
AというテーブルのBカラムの集計値はCというテーブルのDカラムと一致しなければならないのに、何故か一致しない。
どこかで不整合が発生する仕組みになってしまったのだろうが、原因が特定できない。
発生した日のログを見ようと思ったが、発生した日すらわからない。
このシステム稼働してもう何年だっけ?という手詰まりなので、
「今までの事はさておき、これから発生したとして、発生した日がわかればログも追いやすいだろう。チェックバッチを作って毎日チェックしよう」という悲しいモチベーションで作る事になった。

check.shをとりあえず作ってみる

ネーミングセンスの悪さは否めない。

先に言っておくと、下の記述は良くない。

check_ng.sh
#!/bin/bash

# 集計値と異なる場合にメールを送信するsh

# メール送信時に送信時刻を記載するためだけのもの
NOW_TIME=`date +"%Y-%m-%d %H:%M:%S"`
# 送信先メールアドレス
ADMIN="hoge@hogehoge.com"

# SQLファイルの指定
# 簡単ならば直接記述しても良いけれども、ボチボチ修正も入りそうなので
# 外からファイルを読み込む事にする。
SQL_FILE_NAME=diff_check.sql
# このSQLは「count (*)」とかにしておいて、差分が発生した時だけ0以外の
# 値が取得できるものとする。

# MYSQLの設定
DB_USER=hoge
DB_PASSWORD=fuga
DB_NAME=example

COUNT=`cat "$SQL_FILE_NAME" | mysql -u "$DB_USER" -p"$DB_PASSWORD" "$DB_NAME" -N 2>&1`
# ほとんどの場合は何かしらワーニングがついてくると思うのですが…
echo $COUNT

if [ $COUNT -ne 0 ] ; then
echo "$NOW_TIME error" |mail -s "error" $ADMIN
echo "NG"
else
echo "OK"
fi

check_ng.sh: 25 行: [: 引数が多すぎますとか言われます。
echo $COUNT の中身が「mysql: [Warning] Using a password on the command line interface can be insecure. 0」とかなってるからしょうがないです。

最初は「mysql: [Warning] Using a password on the command line interface can be insecure. 」を削れば良いかと思いました。
COUNT=${COUNT#"mysql: [Warning] Using a password on the command line interface can be insecure. "}とか。
でもこのワーニング文言、実は環境ごとに微妙に違うようで、この実装だと他の案件で流用できない。それもどうかと思うけれど、何もかもがあまりよくない。

そもそもの話なのだけれども、DB_PASSWORDという変数がもはやおかしい。
https://dev.mysql.com/doc/refman/5.6/ja/environment-variables.html
を見ると
MYSQL_PWDという環境変数がある。これを使えばいい。

※やっぱりセキュアではないんだけれども。
「mysqld に接続する際のデフォルトのパスワード。これを使用することはセキュアではありません。」と書いてある。
本番稼働しているMYSQLに対して、クリティカルな異常でも何でもない監視用バッチの為にcnfを弄って対応する事も難しい。許可が降りない。

どうせ上の例でもパスワードをハードコーディングしていてセキュアでもなんでもないので、今更セキュアを気にしてもしょうがないと割り切る。

check.sh
#!/bin/bash

# 集計値と異なる場合にメールを送信するsh

# メール送信時に送信時刻を記載するためだけのもの
NOW_TIME=`date +"%Y-%m-%d %H:%M:%S"`
# 送信先メールアドレス
ADMIN="hoge@hogehoge.com"

# SQLファイルの指定
# 簡単ならば直接記述しても良いけれども、ボチボチ修正も入りそうなので
# 外からファイルを読み込む事にする。
SQL_FILE_NAME=diff_check.sql

# MYSQLの設定
DB_USER=hoge
DB_NAME=example

# -pの部分はごっそり消していい。
COUNT=`cat "$SQL_FILE_NAME" | MYSQL_PWD=fuga mysql -u "$DB_USER" "$DB_NAME" -N 2>&1`
# これで件数だけ取得できる
echo $COUNT

if [ $COUNT -ne 0 ] ; then
echo "$NOW_TIME error" |mail -s "error" $ADMIN
echo "NG"
else
echo "OK"
fi

ちなみに、

MYSQL_PWD=fuga
COUNT=`cat "$SQL_FILE_NAME" | mysql -u "$DB_USER" "$DB_NAME" -N 2>&1`

ではダメです。

COUNT=`cat "$SQL_FILE_NAME" | MYSQL_PWD=fuga mysql -u "$DB_USER" "$DB_NAME" -N 2>&1`

こうでないと、うごかない。

cronの設定

あとは、cronで設定するだけ。

00 6 * * * sh /home/hoge/batch/check.sh > /home/hoge/batchlog/check_`date +\%Y-\%m-\%d`.log