MySQL "SHOW ENGINE INNODB STATUS"の読み方 その1
kouです。
何か困ったときに便利なのが各種統計情報出力コマンド。弊社では堅く作るシステムではMySQLを利用することが多く、ストレージエンジンもInnoDBを利用させていただくことが多いです。
というわけで負荷テスト時や運用時に"SHOW ENGINE INNODB STATUS"コマンドをよく使うのですが、各参考サイトを元に自分のメモ代わりに必要そうな項目をまとめてみました。
以下、各セクション毎にポイントをまとめていこうと思います。間違い等があればご指摘いただけるとうれしいです。
出力サンプルのMySQLのバージョンは5.0系です。
ステータスセクション
1 mysql> SHOW ENGINE INNODB STATUS\G 2 *************************** 1. row *************************** 3 Status: 4 ===================================== 5 100530 9:10:18 INNODB MONITOR OUTPUT 6 ===================================== 7 Per second averages calculated from the last 26 seconds
7行目のPer second ...は統計のためにデータを取得した時間です。とはいえ、全てのセクションの情報が同時に採取されるわけではないらしいので、この例では以降のセクションのカウンタ値はだいたい26秒間のもの。秒平均値は26秒間の中のどこかの期間内の平均値であることがわかります。
セマフォセクション
8 ---------- 9 SEMAPHORES 10 ---------- 11 OS WAIT ARRAY INFO: reservation count 359260, signal count 356251 12 Mutex spin waits 0, rounds 6471518, OS waits 83492 13 RW-shared spins 438760, OS waits 214212; RW-excl spins 34083, OS waits 15339
リレーショナルデータベースではそのデータの整合性を担保するために様々な処理でアトミック性を保証しなくてはいけません。MySQLで複数スレッドが動いている中で、クリティカルな処理を行う場合には、そのアトミック性を担保するために同時に1つの操作しかしていないことを保証する仕組み(同期機構)が必要です。LinuxのMySQLでは同期機構にセマフォとMutexを利用しています。
このセクションではそのセマフォ、Mutexに関する統計情報が出力されています。
11行目はスレッドがセマフォの配列に入ったOS待ち(OS wait)の回数です。reservation countが配列に入った回数、signal countが配列に入ってOSからシグナルを受け取った回数です。後述しますが、この回数は少ないほど良いです。
12行目はスレッドのMutex(相互排他ロック)による各種待ち回数を示していて、spin waitsはMutexのスピンロック待ちに入った回数、roundsはスピンロック待ちに入ったロックのスピンラウンド総数を示します。OS waitsはスピンロック待ちでもMutexを取得できなかったため、オペレーティングシステム管理下のセマフォに渡されたロック回数です。
InnoDBはスピンロック待ち→OS待ちの順にロック取得を試みます。スピンロック待ちに入ったロックはinnodb_sync_spin_loopsで設定されたスピンラウンド数(取得待ち回数)を超えるとOS待ちに引き渡されます。OS待ちはスピン待ちに比べて処理コストが大きいので一般的にはOS待ち数が小さい方が好ましいです。OS待ちが多く発生している場合はディスクI/OかInnoDB内部でこれらロックに関する競合が発生している可能性が高いです。OS待ちが毎秒10000以上であればOS待ちを減らす方向でチューニングしましょう。MySQLのスレッド数の変更(innodb_thread_concurrency設定値)が効くと思います。
とはいえ、スピン待ちもCPUリソースを消費するため、あまりspin rounds数が大きいようだとスピン待ちとOS待ちのバランスを取るためにチューニングが必要です(前述のinnodb_sync_spin_loops変数で設定できます。まだ試したことないですが、spin roundが毎秒1000以上であればinnodb_sync_spin_loopsを変更すると良いそうです。もしくはinnodb_thread_concurrency設定値を減らす)。
13行目は読み書きに関する共有ロック(RW-shared)、排他ロック(RW-excl)の各待ち種別カウンタを示していて、それぞれスピン待ち(spins)、OS待ち(OS waits)の回数を示しています。
まとめると、データベースへの並列アクセスが多い場合は同期機構に密接に関わるこのセクションの数字が特に重要で、ほとんどの状況ではOS待ちが多く発生してパフォーマンスが劣化していると思います。その場合はディスクI/OやInnoDB内部の競合が発生している可能性が多いので、MySQLのスレッド数を減らす(innodb_thread_concurrency)と症状が改善されることが多いです。パフォーマンスが出ないからとスレッド数をむやみに増やすと逆に競合が発生しやすくなるため逆効果です。
(もちろん、ボトルネックがどこにあるかによって増やした方がいい場合もありますので、innodb_thread_concurrencyを変更しながらベンチマークをとってチューニングすることが一番望ましいです。)
「その2」へ続く。
参考サイト/書籍:
MySQL Performance Blog - SHOW INNODB STATUS walk through
MySQL 5.0 Reference Manual - 13.2.13.2. SHOW ENGINE INNODB STATUS and the InnoDB Monitors
実践ハイパフォーマンスMySQL 第2版
PhotoShop・Illustrator CS5 からWeb用に保存したファイルが一部携帯端末で見れない場合
こんにちは。Nairoです。
CS5が発売されましたね。
使っている方も多いかと思いますが、先日CS5で書き出したファイルが初期設定のままでは、一部携帯端末(例えばSoftbank 905SH)で表示しないということに気づきました。
初期設定ではWeb書き出し機能(Web およびデバイス用に保存)にメタ情報を付与するようになっています。
これです。
このまま初期設定で保存してしまうとメタ情報などが付与されてしまい、一部端末で表示しないことがあります。
ということでメタ情報をなしに。
情報を付けなければファイル容量も小さくなりますし忘れずに!
特にインストールした直後などは注意しましょう。
メモ書きでした。
コンパイル言語 C++ とスクリプト言語 Lua を、Luabind を使って組み合わせてみる
みなさん、こんにちは。
kaoruです。
今回は、コンパイル言語 C++ とスクリプト言語 Lua を、Luabind というライブラリーで使って組み合わせて利用する方法を紹介します。
はじめに
まず、C++とLuaを組み合わせようという背景を説明したいと思います。
両者を比較するポイントとして、コンパイル言語かスクリプト言語かという点が重要な要素になります。
コンパイル言語とスクリプト言語の特徴を挙げてみました。
コンパイル言語(C++, JAVAなど)- コンパイル時に全ての変数の型の整合性がチェックされるので堅牢
- 大きなデータを効率的に扱える
- 実行速度が速い
- 一度作ってしまえば再利用が容易
- 型が動的で柔軟
- コンパイルやインストール、システム再起動などの前準備せずに動作確認が可能
- コーディング量が少なく、試行錯誤が簡単
- 再利用できるように作るには工夫が必要
コンパイル言語とスクリプト言語では、得意分野が大きく違うのです。
そこで、それぞれの得意分野を生かしながら両方を組み合わせて開発をすることでよりよいものを作ることができます。
今回取り上げるLuabindはC++とLuaの間の橋渡しをするためのライブラリーです。
Luaを使ってみよう
読者の中には、Luaを使ったことがない方もいらっしゃると思いますので、雰囲気をご紹介したいと思います。
Hello worldを作ってみよう
Luaはほかのアプリケーションの中に組み込んで使うことが多いのですが、単体でも動作します。
画面にテキストを表示するだけなら1行から書けます。
print "Hello world"
簡単ですね。Luaは行末にセミコロンを書かなくてもOKです。
文とブロック
PHPなどでは、{ と } でブロックを囲みますが、Lua では do と end などで囲みます。
do
local a = 123
print a
end
四則演算と式
PHPなどほかの言語に比べると、!= がなくて、かわりに ~= と書かないといけないなど、微妙な違いはありますが、慣れるとあまり大きな違いはありません。
do
local a, b = 1, 2
if a ~= b then
print "Mismatch"
end
end
さらにメタテーブルという機能を使うと、オブジェクト同士の演算などをカスタマイズできるので、C++のoperatorと感覚が似ていて面白そうです。
関数の定義
関数の定義はJavascriptとよく似ています。ラムダ式も使えます。
function make_less_than(b)
return function less_than(a)
return a < b
end
end
Luabindを使ってみる
簡単に使い方を紹介します。
Luabind / Lua のインクルードファイルをロードします。
#include <luabind/lua_include.hpp> #include <luabind/luabind.hpp>
まず、Luaステートオブジェクトを作成します。
lua_State* L = lua_open();
lua.hをみると、lua_open()関数はマクロでluaL_newstate()関数に置換されるため、luaL_newstate()関数を直接呼んでもよさそうです。
ここで作成したオブジェクトは、使い終わったら、lua_close(L)関数でクローズしなければいけません。C++では、自分で閉じるのではなく、RAIIクラスやスマートポインターを使って自動的にリソース管理するほうがよいでしょう。
次に標準ライブラリーをロードします。
luaL_openlibs(L);
この関数をロードするためには追加のインクルードファイルが必要かもしれません。
extern "C" {
#include "lualib.h"
}
Luabindを使う場合は、Luabindの初期化も忘れないように行わなければいけません。忘れるとアプリケーションがクラッシュしてしまいます。
luabind::open(L);
Luabindによる疑似クラス
Luaにはクラスの言語サポートはありませんが、メタテーブルなどの柔軟な言語機能を用いて、クラス相当のものをライブラリーで作ることができます。 今回はLuabindの機能を使ってクラスを作ってみました(yumewaza.cpp, sample.lua)。luabind::open(L)を呼び出すと、class, propertyやsuperなどのキーワードが登録され利用できるようになります。
yumewaza.cpp#include <string>
#include <sstream>
#include <iostream>
#include <luabind/lua_include.hpp>
#include <luabind/luabind.hpp>
extern "C" {
#include "lualib.h"
}
int main(int argc, const char *argv[])
{
if (argc != 2) {
std::cerr << argv[0] << " <script file.lua>" << std::endl;
return 1;
}
lua_State* L = lua_open();
luaL_openlibs(L);
luabind::open(L);
if (luaL_dofile(L, argv[1])) {
std::cerr << "ERROR : " << lua_tostring(L, -1) << std::endl;
lua_close(L);
return 1;
}
lua_close(L);
return 0;
}
sample.lua
-- 継承元がない独立したクラス MyClass
class 'MyClass'
-- コンストラクター
function MyClass:__init()
self.var_a = 1
end
-- 関数1
function MyClass:func01()
print("MyClass:func01()")
end
-- 関数2
function MyClass:func02()
print("MyClass:func02()")
self.var_a = self.var_a + 1
end
-- MyClass を継承したクラス MyClass2
class 'MyClass2' (MyClass)
-- コンストラクター
function MyClass2:__init()
MyClass.__init(self)
end
-- オーラーライドされた関数2
function MyClass2:func02()
MyClass.func02(self)
print("MyClass2:func02()")
self.var_a = -self.var_a
end
do
print "---------- test01 ---------"
local obj01 = MyClass()
print(string.format("self.var_a = %d", obj01.var_a))
obj01:func01()
print(string.format("self.var_a = %d", obj01.var_a))
obj01:func02()
print(string.format("self.var_a = %d", obj01.var_a))
print "---------- test02 ---------"
local obj02 = MyClass2()
print(string.format("self.var_a = %d", obj02.var_a))
obj02:func01()
print(string.format("self.var_a = %d", obj02.var_a))
obj02:func02()
print(string.format("self.var_a = %d", obj02.var_a))
end
sample.luaの実行例
---------- test01 ---------
self.var_a = 1
MyClass:func01()
self.var_a = 1
MyClass:func02()
self.var_a = 2
---------- test02 ---------
self.var_a = 1
MyClass:func01()
self.var_a = 1
MyClass:func02()
MyClass2:func02()
self.var_a = -2
サンプルに示すように、classキーワードを使うことで、本当のクラスのように定義が書けるほか、継承や、メソッドのオーバーライドも可能になります。 自分で独自にクラス機構を実装することもできますが、Luabindを使っている場合は、ぜひ利用してみてはどうでしょうか。
C++のクラスや構造体をLuaで使う
C++のクラスや構造体をLua側で使えるように登録するには次のようにします。
例として次の構造体がC++側であるとします。
struct my_struct_t
{
int var_a;
std::string var_b;
boost::optional<std::string> var_c;
void method_a();
int method_b();
std::string method_c();
};
これをLua側で使えるようにLuabindを使って登録してみます。
luabind::module(L)
[
luabind::class_<my_struct_t>("my_struct_t")
.def_readwrite("var_a", &my_struct_t::var_a)
.def_readwrite("var_b", &my_struct_t::var_b)
.def_readwrite("var_c", &my_struct_t::var_c)
.def("method_a", &my_struct_t::method_a)
.def("method_b", &my_struct_t::method_b)
.def("method_c", &my_struct_t::method_c)
];
C++のフリー関数をLuaで使う
あるいは、C++のフリー関数をLua側で使えるように登録するには次のようにします。
C++側で、次のようなフリー関数があったとすると
my_struct_t create_my_object()
{
return my_struct_t();
}
Lua側で使えるようにするには、次のようにします。
luabind::module(L)
[
luabind::def("create_my_object", &create_my_object)
];
C++からLuaスクリプトを実行
このような準備をした後、luaL_dostring関数を使えば、Luaのスクリプトを文字列で渡して実行できます。
luaL_dostring(lua_state.get(),
"function set_a(obj, value) \n"
" obj.var_a = value \n"
"end \n"
);
C++からLuaスクリプトファイルをロードして実行する
あるいは、Luaスクリプトをファイルからロードするには次のようにします。
if (luaL_dofile(L, "script.lua")) {
std::cerr << "ERROR : " << lua_tostring(L, -1) << std::endl;
BOOST_ASSERT( false );
}
Luaの関数をC++から呼び出す
また、Luaスクリプトの中で定義されている関数をC++側から直接呼び出すこともできます。
my_struct_t obj;
luabind::call_function<void>(
L,
"set_a",
boost::ref(obj),
123
);
呼び出されるLua側のソースコードは例えば次のようになります。
function set_a(obj, value)
obj.var_a = value
end
Lua側でC++側の変数の値を変更するには
ここで、objをそのまま渡さずに、boost::ref(obj)としているのは、objをそのまま渡してしまうと、objのコピーが作成されて、それがLua側に渡されるために、Lua側でオブジェクトのプロパティーを書き換えても、呼び出し元のobj変数は変更されないからです。
boost::refを利用すると、参照としてLua側にオブジェクトを引き渡せるので、Lua側での変更が、きちんと、C++側に反映されます。
Luaの文字列/nilをboost::optionalを使って扱う
また、数値や文字列だけではなく、nilをC++とLuaでやりとりしたい場合もあると思います。
今回は、boost::optionalを使ってC++側でnilを表現することにしました。それには、LuaとC++の双方で型をコンバートする方法をLuabindに教える必要があります。
#include <string>
#include <boost/optional/optional.hpp>
#include <luabind/lua_include.hpp>
#include <luabind/luabind.hpp>
namespace luabind
{
template <>
struct default_converter<boost::optional<std::string>>
: native_converter_base<boost::optional<std::string>>
{
static int compute_score(lua_State* L, int index)
{
using namespace std;
switch (lua_type(L, index))
{
case LUA_TNIL:
case LUA_TSTRING:
return 0;
default:
return -1;
}
}
boost::optional<std::string> from(lua_State* L, int index)
{
using namespace std;
switch (lua_type(L, index))
{
case LUA_TNIL:
return boost::optional<std::string>();
case LUA_TSTRING:
default:
return string(lua_tostring(L, index));
}
}
void to(lua_State* L, boost::optional<std::string> const& x)
{
using namespace std;
if (x)
lua_pushstring(L, x->c_str());
else
lua_pushnil(L);
}
};
template <>
struct default_converter<boost::optional<std::string> const&>
: default_converter<boost::optional<std::string>>
{};
}
今回は、boost::optional
Luaスクリプト中で発生したエラー情報の取得
デフォルトではLuaスクリプトにエラーがある場合、あまり情報を取得することができませんが、luabind::set_pcall_callback というメソッドでエラーハンドラーを指定することができます。
今回、サンプルを作ってみました。
int lua_error_handler(lua_State* L)
{
lua_Debug d = {};
std::stringstream msg;
// スタックからエラーメッセージを取得する
std::string err = lua_tostring(L, -1);
msg << "ERROR: " << err << "\n\nBacktrace:\n";
for (int stack_depth = 1; lua_getstack(L, stack_depth, &d); ++stack_depth) {
lua_getinfo(L, "Sln", &d);
msg << "#" << stack_depth << " ";
if (d.name)
msg << "<" << d.namewhat << "> \"" << d.name << "\"";
else
msg << "--";
msg << " (called";
if (d.currentline > 0)
msg << " at line " << d.currentline;
msg << " in ";
if (d.linedefined > 0)
msg << "function block between line " << d.linedefined << ".." << d.lastlinedefined << " of ";
msg << d.short_src;
msg << ")\n";
}
// スタックに積まれているエラーメッセージを、新しい文字列に置換する。
lua_pop(L, 1);
lua_pushstring(L, msg.str().c_str());
std::cout << msg.str() << std::endl;
return 1;
}
luabind::set_pcall_callback(lua_error_handler);
実行例を、以下に示します。
ERROR: [string "function _set_a(obj, value) ..."]:2: attempt to index field 'var_a' (a number value)
Backtrace:
#1 <global> "_set_a" (called at line 2 in function block between line 1..3 of [string "function _set_a(obj, value) ..."])
#2 -- (called at line 5 in function block between line 4..6 of [string "function _set_a(obj, value) ..."])
1行目にエラーの内容(この例では、var_aは数値であるのに、インデックスフィールドが参照された旨が示されている。)が表示され、 Backtrace:の次の行から、呼び出し履歴が表示されています。#1によると、エラーの個所はグローバル関数の_set_aの中の2行目(それはLuaスクリプトとして渡された文字列の1?3行目で定義されている。)から呼び出されていることが分かり、#2によると、それは、5行目から呼び出されていることがわかります。
このようにエラーハンドラーを設定することで、エラーが発生した場所や、スクリプトのファイル名、関数の呼び出し履歴などを取得し、表示することができます。
Luabind がVS2010でコンパイルエラーになる場合には・・・
余談ですが、筆者の環境ではVisual Studio 2010でLuabindがコンパイルエラーになったため、Luabindのソースコードを若干変更しました。
具体的には、std::pairのコンストラクターを呼び出している個所で、C++0xに関連して、0の扱いがあいまいになってしまいコンパイルエラーになっていたので、明示的なキャストをして問題を解決しました。
diff --git a/luabind/lua_include.hpp b/luabind/lua_include.hpp
index 899df14..368b61a 100755
--- a/luabind/lua_include.hpp
+++ b/luabind/lua_include.hpp
@@ -25,8 +25,8 @@
extern "C"
{
- #include "lua.h"
- #include "lauxlib.h"
+ #include <lua.h>
+ #include <lauxlib.h>
}
#endif
diff --git a/src/inheritance.cpp b/src/inheritance.cpp
index 45daeb8..4c7293b 100644
--- a/src/inheritance.cpp
+++ b/src/inheritance.cpp
@@ -154,7 +154,7 @@ std::pair<void*, int> cast_graph::impl::cast(
if (cached.first != cache::unknown)
{
if (cached.first == cache::invalid)
- return std::pair<void*, int>(0, -1);
+ return std::pair<void*, int>(static_cast<void *>(0), -1);
return std::make_pair((char*)p + cached.first, cached.second);
}
@@ -192,7 +192,7 @@ std::pair<void*, int> cast_graph::impl::cast(
m_cache.put(src, target, dynamic_id, object_offset, cache::invalid, -1);
- return std::pair<void*, int>(0, -1);
+ return std::pair<void*, int>(static_cast<void *>(0), -1);
}
void cast_graph::impl::insert(
おわりに
駆け足で、紹介してきましたが、C++とLua、Luabindを利用した開発のイメージはつかめましたでしょうか。コンパイル言語での開発の中にスクリプト言語を取り入れると、自由度が上がり、開発の幅が広がりますので、ご興味のある方は一度試してみられるといいと思います。
LuaのAPIをC言語から呼び出す際には、Luaスタックを介してやりとりをしなければいけないので、 少々面倒くさい面があるのですが、C++からLuabindを利用すると、Luaスタックの操作はほとんど ライブラリー側で面倒をみてくれるので、本当にやりたいことだけを書けばよくなっています。
まだまだ、書ききれなかったことはたくさんありますが、LuaやLuabindのオフィシャルサイトの他、最近は、ユーザーのブログや書籍もたくさんありますので、困った時にはいろいろと探してみるといいかもしれません。
最後に、オフィシャルサイトのリンクを掲載しておきます。
http://www.lua.org/
Luaのサイト 最新のLuaがダウンロードできます
http://www.rasterbar.com/products/luabind.html
Luabindのサイト 最新のLuabindがダウンロードできるほか、ドキュメントがとても参考になります。






