主夫ときどきプログラマ

データベース、Webエンジニアリング、コミュニティ、etc

Mysql slow queryの設定と解析方法

Mysql5.5でスロークエリに関して調べたのでまとめました。
Mysql5.1からスロークエリのパラメータが変わっているので注意しましょう。
そのためMysql5.1より前のバージョンでは内容が異なります。


設定を確認する

Mysqlのコンソールからshow variablesコマンドで、スロークエリの設定を確認できます。

mysql> show variables like 'slow%';
+---------------------+----------------+
| Variable_name       | Value          |
+---------------------+----------------+
| slow_launch_time    | 2              |
| slow_query_log      | OFF            |
| slow_query_log_file | mysql-slow.log |
+---------------------+----------------+

slow_query_log がOFFになっているとスロークエリの出力は行われません。

mysql> show variables like 'long%';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+------------+
| long_query_time | 10.000000 |
+-----------------+-----------+

slow_query_logがONのとき、long_query_timeを超えるクエリがスロークエリとして出力されます。


スロークエリを出力する(コンソールからの設定)


コンソールから設定を変更する場合はset globalコマンドで変更することができます。

mysql> set global slow_query_log_file = '/tmp/mysql-slow.log';
mysql> set global long_query_time = 5;
mysql> set global slow_query_log = ON;


show vairables コマンドで設定を確認すると、変更されていることが確認できます。
long_query_time の値が変更されていない場合がありますが、その時は一度コンソールを閉じ
最ログインすると変更されていることが確認できます。
変更には相当のユーザー権限が必要です。rootでログイン出来る場合はrootで作業しましょう。


スロークエリを出力する(my.cnfの設定)

my.cnf にパラメータを設定してスロークエリのログを出力します。
root権限で設定ファイルを編集します。

$ sudo vim /etc/my.cnf
[mysqld]
slow_query_log
slow_query_log-file = /var/log/mysql/mysql-slow.sql
long_query_time = 5

slow_query_log = ONを記述することでスロークエリの出力を有効にします。
デフォルトでは無効になっています。
mysql5.0以前ではlog_slow_queriesというオプションなので、記述してある場合はコメントアウトしてください。


log_output=FILEが指定されているとき、slow_query_log_fileオプションで指定されているファイルへログが記録されます。
long_query_timeで何秒以上かかったスロークエリをログへ記録するかを指定します。
Mysql5.1から、1秒未満の秒数(マイクロ秒まで)を小数で指定できるようになりました。


mysqldumpslowを使って集計する

mysqldumpslow コマンドを使ってスロークエリの集計を行うことができます。
-s オプションでどの項目でソートするかを指定することができます。
例では t: query timeを指定しました。その他のオプションは -h でヘルプを確認して下さい。

$  mysqldumpslow -s t /var/log/mysql/mysql-slow.sql

Count: 93  Time=4.40s (409s)  Lock=0.00s (0s)  Rows=5.0 (465), ebisu[ebisu]@2hosts
  show index from `bom`.`logs`

Count: 86  Time=3.76s (323s)  Lock=0.00s (0s)  Rows=0.4 (34), meguro[meguro]@localhost
  SELECT `hash`.`id` FROM `hash` WHERE (`key` = 'S')

スロークエリとなっているSQLが種類別に集計され、実行時間の遅いものから順番にレポートされました。
これをもとに上から順に対策を行っていけば良いというわけです。


これを応用すると、スロークエリだけでなくすべてのクエリの実行状況を集計することができます。
先に説明したパラメータをコンソールから変更してすべてのクエリをログに出力するように設定しましょう。

mysql > set global slow_query_log_file = '/tmp/slow.log';
mysql > set global long_query_time = 0;

このように設定することで、/tmp/slow.logに実行されたすべてのSQLが出力されます。
こうすると相応の負荷がサーバーにかかるので、ディスクやCPUを観察しながら数十秒〜数分間待ってから設定をもとに戻します。
出力されたログファイルを分析しましょう。
今回はソートオプションに c: count を指定します。

$ mysqldumpslow -s  c /tmp/slow.log

Count: 27153  Time=0.00s (1s)  Lock=0.00s (0s)  Rows=0.0 (0), ebisu[ebisu]@localhost
  SELECT `m`.`id`, report.ratio  AS `ratio` FROM `l` AS `l`  LEFT JOIN `report` AS `report` ON l.id = report.l_id AND report.d = 'S' WHERE (l.p_id = 'S') AND (l.kind = 'S') AND (l.status = N) AND (l.deleted = N) AND (l.id not in (N))

Count: 18762  Time=0.00s (1s)  Lock=0.00s (0s)  Rows=0.0 (0), sugamo[sugamo]@localhost
  SELECT `settings`.`value` FROM `settings` WHERE (`key` = 'S')

Count: 4032  Time=0.00s (0s)  Lock=0.00s (0s)  Rows=0.0 (0), 2users@2hosts
  #

Count: 4031  Time=0.00s (0s)  Lock=0.00s (0s)  Rows=0.0 (0), tamachi[tamachi]@localhost
  SELECT `l`.* FROM `l` WHERE (id = N) AND (deleted = N)

Count: 4026  Time=0.00s (0s)  Lock=0.00s (0s)  Rows=0.0 (0), 0users@0hosts
  administrator command: Quit

Count: 4012  Time=0.00s (0s)  Lock=0.00s (0s)  Rows=0.0 (0), mejiro[mejiro]@localhost
  SELECT `filter`.`l_id` FROM `filter` WHERE (c_id = 'S') ORDER BY `l_id` asc

Count: 4012  Time=0.00s (3s)  Lock=0.00s (0s)  Rows=0.1 (563), yoyogi[yoyogi]@localhost
  SELECT `hash`.`id` FROM `hash` WHERE (`key` = 'S')

Count: 4012  Time=0.00s (0s)  Lock=0.00s (0s)  Rows=0.0 (0), osaki[osaki]@localhost
  set names 'S'


この方法をとれば、1回の実行には0.1秒しかかからないけれど毎分1万回実行されるため負荷の大半を占めている、
といったSQLを簡単に特定することができます。


スロークエリをDBに出力する

Mysql5.1からはスロークエリをログファイルではなくDBに保存することができるようになりました。
先にも少し出てきましたが、log_output オプションに TABLE を指定します。
my.cnf の場合は

[mysqld]
log_output = TABLE

コンソールからの場合は

mysql> set global log_output = 'TABLE';

そうするとスロークエリログが mysql.slow_log というテーブルに出力されるようになります。
また、あわせて一般ログも mysql.general_log というテーブルに出用されるのでこちらも意識しておいてください。

これらのテーブルはCSVストレージエンジンを用いて作成され、以下のような構成になります。

mysql> show create table mysql.slow_log \G
*************************** 1. row ***************************
       Table: slow_log
Create Table: CREATE TABLE `slow_log` (
  `start_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `user_host` mediumtext NOT NULL,
  `query_time` time NOT NULL,
  `lock_time` time NOT NULL,
  `rows_sent` int(11) NOT NULL,
  `rows_examined` int(11) NOT NULL,
  `db` varchar(512) NOT NULL,
  `last_insert_id` int(11) NOT NULL,
  `insert_id` int(11) NOT NULL,
  `server_id` int(10) unsigned NOT NULL,
  `sql_text` mediumtext NOT NULL
) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log'
1 row in set (0.00 sec)


テーブルに出力することで、SQLによる集計作業が行えるため柔軟にスロークエリを解析することができます。



参考
http://nippondanji.blogspot.jp/2009/01/mysql-51.html
http://www.kakashi.biz/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0/mysql5-5-%E3%81%A7-slow-query-log-%E3%82%B9%E3%83%AD%E3%83%BC%E3%82%AF%E3%82%A8%E3%83%AA%E3%83%BC%E3%83%AD%E3%82%B0-%E3%82%92%E6%8E%A1%E3%82%8B/
http://d.hatena.ne.jp/sh2/20090414



―知って得するクラウドの動向―というセミナーに参加したけどあんまり得しなかったはなし

近畿情報通信講座① ―知って得するクラウドの動向―
http://telecon.or.jp/240627/seminar/syousai.html
というセミナーに行ってきたので、その内容をピックアップしてまとめた。

総務省クラウド政策/オープンデータ政策の現状と課題

日本のICT(Information and Communication Technology 情報通信技術)について
インフラの整備状況や普及率は世界のトップクラスであるにもかかわらず、
国際競争力の評価は低い。ICTビジネスにおける仕組みが悪く、ビジネスが育たない環境である。

中小企業でのクラウド利用状況が低く、まだまだ認知度も低い。
これからは中小企業こそクラウド活用が望ましい。

総務省では「スマートクラウド戦略」を進めている。

中小企業のクラウド導入7つの壁

  • 規制
  • 費用
  • 効果(可視化されていない)
  • 人材
  • 標準化
  • セキュリティ
  • 知的財産
感想
国のやっていることは規模が大きすぎて、身近に感じることが難しいです。
「フューチャースクール」という原口元大臣の始めた事業事業仕分けで3回廃止が決定したけど、政治判断で現状も続いている、とのこと。
事業仕分けって意味無いじゃん。政治家も分かっててやってんだろうなぁ。

京都におけるクラウドの挑戦

京都クラウド・ビジネス研究会のチームKの活動について発表


京都機械金属中小企業青年連絡会(機青連)との勉強会を経て
中小製造業がつかえるクラウドシステムを開発
「京都クラウドGOZAN」を開発->デモ

感想
これから中小企業向け(IT業界以外への)が増えていくのだろうけど、
オープン化と標準化がうまくできれば成功しそう。
ひとつのビジネスチャンスではあるのだろう。

Amazon Cloud で可能になる新しいITの世界

堀内さん AWSテクニカルエバンジェリスト

JAWS-UG #jawsug
アマゾンのユーザーグループ 九州がさかん(全県ある)


1次元蟻、2次元あり、3次元蟻
新たな道をきりはらくはなし。(by宇宙兄弟


Amazonの3つのビジネス


クラウドコンピューティングとは?
「いつでも、必要なだけ、安価に」
電気インフラのイメージに近い。


AWSにおけるクラウドコンピューティングの定義

  • 初期投資不要
  • 自由自在なスケールアップ、ダウン
  • 定額な従量課金
  • 市場投入時間の短縮
  • インフラ管理からの解放 -> 本来の業務への集中。ビジネスの差別化への集中をはかれる。


AWSリージョンとアベイラビリティゾーン
availability-zone は物理的に離れている。専用線で繋がっている。<事例>
東大空間情報科学センター

  • 数十万規模のデータセット作成に利用
  • データ処理の発生に応じて必要分のEC2インスタンスの作成
  • データ受け渡しにS3

トーカ堂
既存システムを「えびすマート」を使ってコスト削減
http://www.ebisumart.com/
http://www.tokado.jp/


東京リージョンの利用が急拡大中
世界一の初年度成長速度。
IaaSの世界シェア60%
Hadoop利用の世界リーダー
S3に1兆オブジェクトの利用 65万/day


・分散アーキテクチャをカンタンに構築できる
疎結合なプロセスフロー


RDS:DBインストールされている
最適化不要、自動バックアップ、
マルチAZを使って自動レプリケーション、自動フェイルオーバー


S3データ保存の基板
リージョン選択、バケット作成 そこにデータを放り込む
保存データの暗号化されてる。
裏側では自動で3箇所にコピーされているので、耐久性が99.999999999%
1GB/month 10円
Dropbox -> S3 に保存されている。

感想
詳しいことが知りたければユーザーグループのイベントに参加する方が良いでしょう。
あとちゃんと英語のドキュメントを読めるようになりましょう。

KDDIクラウド構想と取り組み

スマートホンのセキュリティ対策に力をいれている。
ビジネスに使えるアプリケーションを提供中。

感想
疲労と集中力低下のためあんまり覚えていません。

総評

自分はおそらくこのセミナーのターゲットとずれていたと思われるのであまり興味を持てる内容ではなかった。
技術情報を得るにはもっとジャンルが絞られたカンファレンスや勉強会などに参加したほうが良い情報を得られるだろう。
Amazonエバンジェリストの方とFacebookでつながれたのがよかった。

「自分は仕事が好きじゃない」ということに気づいてしまった。

学校を卒業してからIT関係の業界で9年間働いてきました。
大企業で3年間、ベンチャーの小企業で4年間、フリーランスになって2年間という
よく分からないキャリアです。
これまでの自分は
「仕事好き?楽しい?」
と聞かれれば
「けっこう好きだよ。やってて楽しいし。」
と答える、どちらかと言えば『仕事好きな俺かっこいい』とか思っちゃってる痛いヤツだったかもしれません。
でもやっぱりちゃんと考えると「仕事は好きじゃない」ということに気づいてしまいました。

何を好きだと思っていたのか。

では今までは何を好きだと思っていたのでしょうか。
これまで「仕事は好きだし、楽しい」と思っていたのには理由があるはずです。
実際に好きなことといえば
 ・プログラムを書くこと
 ・新しい仕組みやものを考える事(計画すること)
 ・問題解決
が主に挙げられると思います。

つまり仕事の中にちらほらと出てくる「プログラミング」や
新しい「サーバー構成、プログラム構成を考えること」や
パフォーマンスを調査しての「サーバーチューニング」などを
好きだ、楽しい、と思っていたことになります。

それってほんとに仕事だろうか

ではもう一つ掘り下げてみます。
前述の「好きなこと」それってほんとに仕事なんでしょうか。

(ちょっと面倒な表現をすると)
「雇用主に拘束されている時間内で行っている行為」のあいだ
「自分のやっていることはすべて仕事だ」と思っているなんて事はありません。
ディスカッションから新しいアイディアが思いついたり、
自分に今必要な情報を探していたり、新しいものを作っていたり、
わくわくするような事をしている時は「仕事をしてる」なんて意識はありません。

ただ「興味の対象にまっすぐ向かっているだけ」です。

それは傍から見たら「仕事をしている」ように見えるかもしれませんが、
自分の心のなかでは「楽しいんでいる」だけなんです。
たったそれだけ。

つまり、ゲームしたり漫画読んだりと
好きなことやってる時と同じような「心の状態」なので、
けっして「仕事を楽しんでいる」訳ではなかったのです。

「仕事のカラを被った楽しいこと」をやっているだけでした。
だから仕事なんてしてなかったんです。

もう仕事なんてしない

これに気づくのに9年もかかってしまいました。
だから僕は仕事なんて嫌いです。
仕事は好きじゃないし、楽しくないし、ずっと遊んでいたいです。

しかし収入を得る必要はあるので「収入を得る行為」は行いますが、
「仕事」をしないで人生を送る方法を考えていきます。
そして「楽しいこと・好きなこと」をして収入を得ていきます。

まずは「働くこと」について真剣に考えてみようと思います。

補足

「仕事」というと人によっていろんな意味や解釈が含まれるので
今回のエントリーについては変に想う部分もあるかと思います。
私の中では「仕事」とはおもしろくもない、つまらない、ただやらされているだけのこと
という感じの位置づけです。そこに自分の意志がない行為、とでも言えばいいでしょうか。
そういうことを本文で読み取っていただけると幸いです。

GoogleAppEngineで構築する簡単RestfulServer


WebServiceに保存されているデータを外部に公開したい場合、WebAPIという形で公開するケースがあります。
しかし、実際にWebAPIを作成しようと思うと、リクエストパラメータを解析して処理を決めたり、XMLJsonフォーマットに変換するなど手間がかかります。


今回はそんなWebAPIをGoogleAppEngineを使って公開する方法を紹介します。
GoogleAppEngineのDataStoreに保存されているデータをModelの構造のまま取得したり、
更新したりすることが簡単にできます。

appengine-rest-server を使う

GoogleAppEngineには「appengine-rest-server」というフレームワークがあります。
GoogleCodeで開発されていて、これを使うことで簡単にRestfulなWebAPIを作成することができます。

ではダウンロードして設置してみましょう。
本記事の執筆時には1.0.5が最新のようですが、以下のコードは1.0.4で作成されています。

appengine-rest-server を設置する

ダウンロードしたファイルを解凍すると、READMEやLICENSEなどのテキストとsrcフォルダが展開されます。
srcフォルダの階層下にrestというフォルダがあるのでそれをGoogleAppEngineのアプリケーションフォルダにコピーします。

$ unzip appengine-rest-server-1.0.4.zip
$ cd appengine-rest-server-1.0.4/src/main/python/
$ cp -rf rest /path/to/appengine/app

これで設置は完了です。
SDKコンソールのInteractive Console で

import rest

が実行できたら問題なくインストール出来ています。

動かしてみよう

今回はアプリケーションのルートにapiというフォルダを作成し、その中のsample.pyにappengine-rest-serverを実装します。
まずは、app.yamlにURLを登録しましょう。以下のようにURLを登録してください。

-url: /api/.*
  script: /api/sample.py

つぎはsample.pyです。rest.Dispatcherというオブジェクトに公開したいModelを登録するだけです。

from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
import rest

# 公開したいModel
class Task(db.Model):
    title = db.StringProperty()
    author = db.StringProperty()
    achieved = db.BooleanProperty(default=False)
    created = db.DateTimeProperty(auto_now_add=True)

# サンプルデータの登録
Task.get_or_insert('task1', title='Foo', author='Tom')
Task.get_or_insert('task2', title='Bar', author='Jhon', acheved=True)

# ベースアドレスを登録し、公開するモデルを登録します
rest.Dispatcher.base_url = '/api'
rest.Dispatcher.add_models({
    'task'   : (Task, ['META_DATA', 'POST', 'GET']),
    })  

application = webapp.WSGIApplication([
    ('/api/.*', rest.Dispatcher)
    ], debug=True)

def main():
    run_wsgi_app(application)

if __name__ == '__main__':
    main()

rest.Dispatcher.add_modelsに公開したいModelを登録します。複数登録できますが、今回は1つだけ。
また、対応するHttpMethodも指定することができます。

ブラウザからアクセスしてみよう

GoogleAppEngineLauncherを起動してアクセスしたのが以下の画面です。
一覧表示 http://localhost:8080/api/task


Key指定表示 http://localhost:8080/api/task/agxrYXdhaWljcnVpc2VyDwsSBFRhc2siBXRhc2sxDA


その他metadataなどを表示することができます。
また、FirefoxのRESTClientアドオンを使うことで、データ登録の動作確認ができます。
POSTメソッドでデータをXMLで送信することで登録できます。


GoogleAppEngine Python でIPアドレスを操作する方法

GoogleAppEngineでアクセス元のIPアドレスによって処理を分けたい場合があります。(主にガラケー対応)
その場合IPアドレスを表現するオブジェクトを使いたいところですが、その用途にあったPythonモジュールなかなか見つかりません。
そこで今回は

  • IP Address をオブジェクトとして扱う
  • IP Address をCIDR表記で範囲として扱う

という2点をPythonでクリアするための方法を紹介します。

IPy モジュールを使う

Freeで公開されているIPyというモジュールを使います。

IPy - A python Module for handling IP-Addresses and Networks
http://c0re.23.nu/c0de/IPy/

こちらで紹介されているモジュールはPerlのNet::IPに似たモジュールで、IPv6にも対応しています。
最新版をダウンロードしましょう
IPy-0.42.tar.gz

まずはPythonインタプリタで動作確認

解凍すると IPy-0.42/build/lib/IPy.py が得られるので lib ディレクトリの階層でPythonインタプリタを起動します。

$ tar xzvf IPy-0.42.tar.gz
$ IPy-0.42/build/lib
$ python

IPy.pyはそのままではunicode errorが起こるので、762, 763の特殊文字を消します。
(コピーライトの文字が入っていました。)

では試しにOverviewにあるようにIPyを使ってみましょう。

>>> import IPy
>>> ip = IPy.IP('120.3.4.5/1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "IPy.py", line 294, in __init__
    raise ValueError, "%s goes not well with prefixlen %d" % (hex(self.ip), self._prefixlen)
ValueError: 0x78030405L goes not well with prefixlen 1

するとこのようにエラーが起こってしまいます。
すこしソースコードを編集してみましょう。1220行目をコメントアウトし、1221行目のようにします。

1218 def _checkNetaddrWorksWithPrefixlen(net, prefixlen, version):
1219     """Check if a base addess of e network is compatible with a prefixlen"""
1220     #if net & _prefixlenToNetmask(prefixlen, version) == net:
1221     if net:
1222         return 1
1223     else:
1224         return 0
1225 

これで準備完了!
Pythonインタプリタで動作確認をしましょう。

GoogleAppEngine での利用

IPy モジュールを GoogleAppEngine で使うためにソースファイルのIPy.py をGoogleAppEngieのルートディレクトリに配置します。
(GoogleAppEngineLaunchdr の Path に表示されているところにコピーします。)

$ mv IPy.py /path/to/gae/root

では、SDK Console の Interactive Console で動作確認をしよう。
Textareaに

import IPy
ip = IPy.IP('192.168.1.100')

と打って [Run Program] ボタンを押し、右側にエラーが表示されなければ成功。
この状態になれば、下層の*.pyでもimportすることができます。


では実際のコードで使ってみましょう。
サンプルとして、携帯キャリアのIP帯域からのアクセスかを判定するメソッドを作ります。

import IPy
from google.appengine.ext import webapp

class Main(webapp.RequestHandler):
'''
  中略
'''

    def include(self):
        # 簡素化するためにRequestParameterでキャリアとIPが送られてくる前提です^_^;
        carrier = self.request.get('carrier')
        remote_ip = IPy.IP(self.request.get('ip'))
        elif carrier == 'softbank':
            soft_ip = [
                IPy.IP('123.108.237.0/27'),   IPy.IP('202.253.96.224/27'),  IPy.IP('210.146.7.192/26'),  IPy.IP('123.108.237.224/27'),
                IPy.IP('202.253.96.0/27')]
            # CIDRで範囲指定されたIP群の中に含まれているかを判断します
            for ip in soft_ip:
                if remote_ip in ip:
                    return True
            return false
        else:
            return False

こんな感じでIPyを使う事でIPアドレスにより制御をGoogleAppEngineで実現できます。