サーバーの状態やMySQLの状態の指標のまとめ
指標に関していつもググってばっかりいたので、まとめてみました。
ツッコミ大歓迎。
CPU usage
name | detail |
---|---|
User | ユーザ空間(アプリケーション)でCPUが使われた時間の割合 |
Nice | 優先度を変更された(nice値が変更された)プロセスにより、ユーザ空間でCPUが使われた時間の割合 |
System | カーネル空間でCPUが使われた時間の割合 |
Idle | CPUが何も処理をせずに待機していたCPUの時間の割合(ディスクI/O待ち以外) |
Wait(iowait) | CPUがディスクI/O、またはネットワークI/Oの結果を待っていた時間の割合(I/O処理中で、その終了を待機している時間) |
Intr | 割り込み |
SoftIRQ | ソフト割り込み |
Steal | 仮想サーバがCPUを使って待たされていた時間の割合 |
http://blog.suusuke.info/2011/10/24/365/
hiboma/Linuxカーネル解読室-3-1.md at master · hiboma/hiboma · GitHub
O'Reilly Japan - 詳解 システム・パフォーマンス
の 6.3.6
6.6.3
も合わせて参照
Memory Usage
name | detail |
---|---|
used | 使用している物理メモリ量 |
buffer | ファイルなどのメタデータをキャッシュしている物理メモリ量 |
cached | ページキャッシュに使用されているメモリ量 |
avail real | 利用可能な物理メモリの量 |
total real | 総物理メモリの量 |
used swap | Swap領域で使用している量 |
cached も buffers も空きメモリの一部 状況に応じてflushされる
Nginx
name | detail |
---|---|
Reading | nginxはリクエストヘッダーを読み込んでいる数 |
Writing | nginxはリクエストボディーを読み込んでいる、リクエストを処理中、またはクライアントへ返信している数。upstreamから結果を受け取ってクライアントに返し中なのか、upstreamからの回答待ちであるかが区別されてないっぽいのが困る。 |
Waiting | リクエスト処理を待っているクライアントのコネクション数 (idle client connections waiting for a request.) keep-aliveの接続数 |
http://wiki.nginx.org/HttpStubStatusModule#stub_status
MySQL
変数の参照はここ
MySQL :: MySQL 5.6 リファレンスマニュアル :: 5.1.6 サーバーステータス変数
MySQL Threads
name | detail |
---|---|
Cached | キャッシュされているスレッド数(スレッドは使いまわされる) |
Connected | 現在の接続数 |
Running | スリープ状態になっていないスレッドの数 |
Threads_created 接続を処理するために生成されたスレッド数(この値が増えまくるなら、cached が足りていない)
Threads_cached + Threads_connected が thread_cache_sizeの値より小さければ想定内。
MySQLのスレッドとか接続数とか - @bayashi Wiki
MySQL Processlist
name | detail |
---|---|
State Closing Tables | 変更されたテーブルデータをディスクにフラッシュし、使用されたテーブルを閉じているスレッド数 |
State Copying To Tmp Tables | メモリー内の一時テーブルにコピーしているスレッド数 |
State End | ALTER TABLE、CREATE VIEW、DELETE、INSERT、SELECT、または UPDATE ステートメントの最後、ただしクリーンアップの前に発生しまる状態のスレッド数 |
State Freeing Items | コマンドの実行を完了したスレッド数、通常、この状態のあとは cleaning up になります |
State Init | ALTER TABLE、DELETE、INSERT、SELECT、または UPDATE ステートメントの初期化の前に発生する状態のスレッド数 |
State Locked | 別のクエリーによってロックされているスレッド数 |
State Login | クライアントが正常に認証されるまでの初期状態のスレッド数 |
State Reading From Net | ネットワークからパケットを読み込んでるスレッド数 |
State Sending Data | SELECT ステートメントのために行を作成し、また、クライアントにデータを送っているスレッド数 |
State Sorting Result | |
State Statistics | クエリー実行計画を開発する統計を計算しているスレッド数 |
State Updating | 更新する行を探していて、それらを更新しているスレッド数 |
State Writing To Net | ネットワークにパケットを書き込んでいるスレッド数 |
State None | Stateのないスレッド数 例えば SLEEP中のスレッド |
State Other |
http://mysql.stu.edu.tw/doc/refman/5.1-olh/ja/general-thread-states.html
MyISM Indexes
name | detail |
---|---|
Key Read Requests | |
Key Reads | |
Key Writes Requests | |
Key Writes |
MySQL Handlers
クエリの I/O 動作
name | detail |
---|---|
Handler Write | INSERTの回数 |
Handler Update | UPDATEの回数 |
Handler Delete | DELETEの回数 |
Handler Read First | テーブルやインデックスの全件検索(インデックスフルスキャン)の際にまず最初に先頭レコードの取得するが、その回数。フルスキャンが多いとこの回数が増える。(要チューニング) |
Handler Read Key | インデックスに基づく読み込み回数。これが多い場合は適切にインデックスが貼られている。 |
Handler Read Next | インデックスに基づいて行を特定した後、後続の行を読んだ回数。範囲指定のインデックススキャンの場合に増えます。 |
Handler Read Prev | インデックスに基づいて行を特定した後、その前の行を読んだ回数。範囲指定のインデックススキャンの場合に増えます。 |
Handler Read Rnd | 固定位置に基づくレコード読んだの回数(handler::rnd_pos()が呼ばれた回数)。固定位置に基づくレコードの読み込みとは、Random Readのこと。結果のソートを必要とするクエリを多く実行すると、この値が大きくなる。(要チューニング) |
Handler Read Rnd Next | データファイルでの次のレコードを読み取った回数。 テーブルスキャンが多く実行されると、この値が大きくなる。(要チューニング) |
MySQL Select Types
name | detail |
---|---|
Select Full Join | インデックスのないカラムで JOINした回数 |
Select Full Range Join | 範囲指定の効果はあるがインデックスは使わず JOINした回数 |
Select Range | WHERE などの指定によって範囲が限定された探索を行った回数 |
Select Range Check | インデクッスなしのJOIN数 |
Select Scan | テーブル(またはインデックスでも)の先頭行から全件検索(スキャン)をした回数 |
JOINに関してはEXPLAINしてtypeがeq_refになるようにする。
MySQL Sorts
name | detail |
---|---|
Sort Rows | ソートしたレコード数 |
Sort Range | 範囲検索ソートの回数 |
Sort Merge Passes | ソートで必要としたマージパスの回数 |
Sort Scan | テーブルスキャンでソートした回数 |
MySQL Temporary Objects
name | detail |
---|---|
Created Tmp Tables | 作成した一時テーブルの数 |
Created Tmp Disk Tables | ディスク上に作成した一時テーブルの数 |
Created Tmp Files | 作成した一時ファイルの数 |
sort_buffer_sizeを超える大きなORDER BYなどで作成される。
漢(オトコ)のコンピュータ道: Using filesort
MySQL Transaction Handle
name | detail |
---|---|
Handler Commit | コミットの要求数 |
Handler Rollback | ロールバックの要求数 |
Handler Savepoint | セーブポイントの要求数 |
Handler Savepoint Rollback | セーブポイントロールバックの要求数 |
Cache hit rate
name | detail |
---|---|
key cache | |
query cache | |
table lock immediate | |
thread cache | |
tmp table on memory |
Dirty page rate
buffer pool の page のうち disk に flush されてない page の比率
ここが増加していると、diskへの書込みが追いついていない
https://dev.mysql.com/doc/refman/5.6/ja/glossary.html#glos_dirty_page
Buffer Pool Activity
name | detail |
---|---|
Page Created | 作成されたページの数 |
Page Read | 読み込まれたページの数 |
Page Written | 書き込まれたページの数 |
Checkpoint Age
Current Lock Wait
トランザクションのロック開放待ち時間の合計秒数
InnoDB I/O
InnoDB I/O Pending
Lock Structures
開放まちLock structの数
InnoDB Log
name | detail |
---|---|
Innodb Log Buffer Size | ログバッファのサイズ |
Log Byte Written | ログに書き込まれたデータ量 |
Log Byte Flushed | ロクから書き出されたデータ量 |
Unflushed Log | ログから書き出されていないデータ量 |
Row Lock Time
行ロック獲得のための所用総時間(msec)
Row Lock Waits
行ロック獲得待機回数
InnoDB Tables In Use
name | detail |
---|---|
InnnoDB Tables In Use | 実行中のトランザクションが利用しているテーブル数の合計数 |
InnnoDB Locked Tables | 実行中のトランザクションがロックしているテーブル数の合計数 |
InnoDB Transactions
name | detail |
---|---|
InnnoDB Transactions | 生成されたトランザクション数 |
History List | undo領域にある未破棄のトランザクション数 |
参照
mpstat
CPUごとの使用状況
-P ALL
ですべてのコアの情報を表示する
vmstat
unix domain socketのstatus
cat /proc/net/unixの中身
項目 | 説明 | 例 |
---|---|---|
Num | カーネルのテーブルスロット | ffff8800798ec0c0 |
RefCount | ソケットを使用しているユーザー数 | 00000002 |
Protocol | いまのところいつも 0 | |
Flags | ソケット の状態を保持しているカーネル内部のフラグ | |
Type | always '1' とかいてあるが2もある。。。 | 0001 |
St | ソケットの内部状態 | 01 02 03 https://github.com/torvalds/linux/blob/master/include/uapi/linux/net.h#L47 |
Inode | ||
Path | (もしあれば) ソケットのパス名 |
sinatraで設定したパス一覧を取得する
メモ
こんな感じでスクリプトを書いた。
require 'rack' require 'sinatra' class Sinatra::Base private class << self attr_reader :original_routes # なぜかsuperが呼べない... def route(verb, path, options = {}, &block) @original_routes ||= {} (@original_routes[verb] ||= []) << path host_name(options.delete(:host)) if options.key?(:host) enable :empty_path_info if path == "" and empty_path_info.nil? signature = compile!(verb, path, block, options) (@routes[verb] ||= []) << signature invoke_hook(:route_added, verb, path, block) signature end end end def run(*args) args.first.instance_variable_get(:@mapping).each do |mapping| # 二番目の要素がmappingのbase path base_path = mapping[1] # 三番目の要素がcontroller class mapping[3].original_routes.each do |method, paths| paths.each { |path| puts "#{method}\t#{base_path}#{path}" } end end end Sinatra::Base.set :running_server, true load './config.ru'
railsで特定のモデルに対してbelongs_toで参照先を設定しているモデルを取り出す
まあ、いろいろ調べる必要がありまして。。。
ざっくりとこんな感じでとれた。
# production環境では要らないが、development環境では必要かと Dir.glob(File.expand_path('./app/models/*.rb', Rails.root)).each do |f| require f end # 定数を全部取得 constants = Object.constants.map do |name| Object.const_get(name) end # 定数一覧からモデルを絞り込む all_models = constants.select do |c| c.class == Class && c < ActiveRecord::Base && !c.abstract_class? end target = {} all_models.each do |model| model.reflect_on_all_associations(:belongs_to).each do |association| if association.name == :hoge || %w(Hoge).include?(association.options[:class_name]) target[association.active_record.name] = { model: association.active_record, foreign_key: association.foreign_key } end end end target
これで対象モデルが取得できたのであとはよしなに使う。 例えば、一つも参照がされていないデータを取ってくるとか。
target_ids = Hoge.pluck(:id) target.each do |name, modle| used_ids = model[:model].where(model[:foreign_key] => target_ids).pluck(:"#{model[:foreign_key]}") target_ids -= used_ids end target_ids
とか
rubyで局所的にパッチを当てる方法を模索中
下記のようなコードで、Cクラスの中だけで何とかAクラスに対するパッチを当てたい。
class A def self.foo p 'foo' end def bar p 'bar' end end module ExtendFoo refine A.singleton_class do def foo p 'extend foo' end end refine A do def bar p 'extend bar' end end end class B def initialize A.foo A.new.bar end end class C using ExtendFoo def initialize B.new end end C.new
これを実行すると、
"foo" "bar"
になるのだが、
"extend foo" "extend bar"
にする方法がないものか。。。
railsアプリでstackprofを使ってボトルネックを探す + JSON::Schema(2.2.1)の高速化
railsアプリが遅いって言われたので、久しぶりにrubyでisuconしてみました。
railsアプリでstackprofを使ったプロファイリング
まず、自分がいつもやってる方法なのですが、config.ru
にstackprofの設定を仕込みます。
stackprofはrackミドルウェアとして差し込めるようになっています。
下記設定はrailsだけでなく、sinatraでももちろん動きます。(これをいつも仕込んでおいてあります。)
Gemfileにgem 'stackprof'
を書いてconfig.ruに下記のように仕込んでいます。
is_stackprof = ENV['ENABLE_STACKPROF'].to_i.nonzero? stackprof_mode = (ENV['STACKPROF_MODE'] || :cpu).to_sym stackprof_interval = (ENV['STACKPROF_INTERVAL'] || 1000).to_i stackprof_save_every = (ENV['STACKPROF_SAVE_EVERY'] || 100 ).to_i stackprof_path = ENV['STACKPROF_PATH'] || 'tmp' use StackProf::Middleware, enabled: is_stackprof, mode: stackprof_mode, raw: true, interval: stackprof_interval, save_every: stackprof_save_every, path: stackprof_path run Rails.application
環境変数でstackprofの設定を渡しています。 stackprofを仕込んでrailsを起動するときはこんな感じです。
ENABLE_STACKPROF=1 bundle exec rails s
で、今回とったプロファイリング結果がこちら。 (まあ、色々削った結果を載せています)
================================== Mode: cpu(1000) Samples: 7758 (7.75% miss rate) GC: 1720 (22.17%) ================================== TOTAL (pct) SAMPLES (pct) FRAME 5799 (74.7%) 2987 (38.5%) JSON::Schema.add_indifferent_access 858 (11.1%) 638 (8.2%) JSON#generate 439 (5.7%) 439 (5.7%) block in JSON::Schema::TypeV4Attribute.data_valid_for_type? 866 (11.2%) 359 (4.6%) ActiveSupport::JSON::Encoding::JSONGemEncoder#jsonify 279 (3.6%) 279 (3.6%) block in JSON::Schema.add_indifferent_access 174 (2.2%) 174 (2.2%) String#to_json_with_active_support_encoder 296 (3.8%) 118 (1.5%) block in Array#as_json 111 (1.4%) 98 (1.3%) block in Hash#as_json 2205 (28.4%) 96 (1.2%) block in JSON::Schema::PropertiesV4Attribute.validate ・ ・ 7150 (92.2%) 32 (0.4%) JSON::Schema::Validator#validate ・ ・ 3361 (43.3%) 11 (0.1%) JSON::Schema#initialize
JSON::Schema.add_indifferent_access
が悪さしているぽい。
ボトルネックの洗い出しと解消
で、ここからは、最近のコード変更点とJSON::Schemaに関わる部分を洗い出して、悪さしているコードを洗い出して、最小の再現コード書いてみたのがこちら。
require 'json' require 'json-schema' require 'benchmark' class BenchmarkJSONSchmea def initialize @json_data = [] 70_000.times do |i| @json_data << {id: i, name: "てすとてすと", point: 12345, description: "テストてすと"} end @json_schema = <<-JSON { "type": "array", "items": { "type": "object", "required": ["id"], "properties": { "id": {"type": "integer"}, "name": {"type": "string", "minLength": 2, "maxLength": 15}, "point": {"type": "integer"}, "description": {"type": "string", "minLength": 2, "maxLength": 15} } } } JSON end def run # 試行回数は1回 Benchmark.bm(7, ">total:", ">ave:") do |x| x.report("raw: ") { JSON::Validator.validate!(JSON.parse(@json_schema), JSON.parse(@json_data.to_json)) } end end end BenchmarkJSONSchmea.new.run
で、結果がこちら
% bundle exec ruby bench.rb user system total real raw: 6.310000 0.110000 6.420000 ( 7.567306)
遅い。。。
stackprofの結果を鑑みて
stackprofではJSON::Schema.add_indifferent_access
が遅いって言われているので、ここを見てみてパッチを当ててみた。
require 'json' require 'json-schema' require 'benchmark' class JSON::Schema def self.add_indifferent_access(schema) if schema.is_a?(Hash) schema.default_proc = proc do |hash,key| if hash.has_key?(key) hash[key] else hash.has_key?(key) ? hash[key] : nil end end schema.keys.each do |key| add_indifferent_access(schema[key]) end end end end class BenchmarkJSONSchmea def initialize @json_data = [] 10_000.times do |i| @json_data << {"id" => i, "name" => "てすとてすと", "point" => 12345, "description" => "テストてすと"} end @json_schema = <<-JSON { "type": "array", "items": { "type": "object", "required": ["id"], "properties": { "id": {"type": "integer"}, "name": {"type": "string", "minLength": 2, "maxLength": 15}, "point": {"type": "integer"}, "description": {"type": "string", "minLength": 2, "maxLength": 15} } } } JSON end def run # 試行回数は1回 Benchmark.bm(7, ">total:", ">ave:") do |x| x.report("patch: ") do JSON::Validator.validate!(JSON.parse(@json_schema), JSON.parse(@json_data.to_json)) end end end end BenchmarkJSONSchmea.new.run
結果がこちら
% bundle exec ruby patch_bench.rb user system total real patch: 0.890000 0.010000 0.900000 ( 0.993027)
これで5倍くらいは速くなってます。
あとはrefimentsでパッチ当てて、こんな感じで局所化したかったけどなぜかうまく動作しない。。。
module JSONSchemaExtension refine JSON::Schema.singleton_class do def add_indifferent_access(schema) if schema.is_a?(Hash) schema.default_proc = proc do |hash,key| if hash.has_key?(key) hash[key] else hash.has_key?(key) ? hash[key] : nil end end schema.keys.each do |key| add_indifferent_access(schema[key]) end end end end end class BenchmarkJSONSchmea using JSONSchemaExtension end
Webサイトに必要なfaviconが21個になっていたらしいのでrack-favicon_allっての書いた
Webサイトに必要なfaviconが21個になっていた - IT探検記
どうもWebサイトに必要なfaviconが21個もあるらしい。
で@bayashiが
増殖中の favicon を Plack::Middleware ひとつで配信する - @bayashi Diary
のような記事を書いていたから、rackミドルウェア版作ってみた。
SpringMT/rack-favicon_all · GitHub
rack-favicon_all | RubyGems.org | your community gem host
誰か作ってるかもしれないけれど、後悔はしていない。