ふり返る暇なんて無いね

日々のメモ書きをつらつらと。メインブログに書くほどでもないことを流してます

MySQLのLINEAR KEY パーティションでPKで検索しても遅い場合

プライマリーキー(id)でSELECTしてるのにSlowlogに出てくる不思議なクエリがあるからなんでだろうなと調べてみると、なんと全パーティションを検索していて遅かったという現象がありました。

ちょっとテスト環境で再現してみます。

環境

Ubuntu 12.04のdebで入るmysql-server-5.5で見てます。

> show variables like 'version';
+---------------+-----------------------------+
| Variable_name | Value                       |
+---------------+-----------------------------+
| version       | 5.5.43-0ubuntu0.12.04.1-log |
+---------------+-----------------------------+
1 row in set (0.04 sec)

下準備

実際のテーブル定義からいろいろ改変してます。なので、適当です。

CREATE TABLE user_item (
  id         int(10) unsigned NOT NULL AUTO_INCREMENT,
  user_id    int(10) unsigned NOT NULL,
  item_id    int(10) unsigned NOT NULL,
  count      int(10) unsigned NOT NULL DEFAULT 0,
  type       enum('type1','type2','typ3','type4') NOT NULL DEFAULT 'type1',
  created_at datetime NOT NULL,
  updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY  (id,user_id),
  UNIQUE KEY   user_item_type (user_id, item_id, type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8
PARTITION BY LINEAR KEY (user_id) PARTITIONS 128;

-- 適当にデータ入れる
INSERT INTO user_item (user_id, item_id, count, created_at) VALUES
    (1,1,1, NOW()),
    (1,2,1, NOW()),
    (2,2,1, NOW()),
    (3,3,1, NOW()),
    (3,4,1, NOW()),
    (4,1,1, NOW()),
    (4,4,3, NOW())
;

実際に試す

> explain partitions select * from user_item where id=1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: user_item
   partitions: p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20,p21,p22,p23,p24,p25,p26,p27,p28,p29,p30,p31,p32,p33,p34,p35,p36,p37,p38,p39,p40,p41,p42,p43,p44,p45,p46,p47,p48,p49,p50,p51,p52,p53,p54,p55,p56,p57,p58,p59,p60,p61,p62,p63,p64,p65,p66,p67,p68,p69,p70,p71,p72,p73,p74,p75,p76,p77,p78,p79,p80,p81,p82,p83,p84,p85,p86,p87,p88,p89,p90,p91,p92,p93,p94,p95,p96,p97,p98,p99,p100,p101,p102,p103,p104,p105,p106,p107,p108,p109,p110,p111,p112,p113,p114,p115,p116,p117,p118,p119,p120,p121,p122,p123,p124,p125,p126,p127
         type: ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: const
         rows: 131
        Extra:
1 row in set (0.00 sec)

プライマリーキーで引いてるのは確かです。ですが、partitionsを見ると全パーティションを見てることが分かります。
実際の現場では512分割してるのでいくらプライマリーキー使ってるとは言え、でかいテーブルに対してそれだけ検索すれば遅くはなります。

よく考えて見ると当たり前の話で、(id, user_id)でプライマリーキー張ってますが、実際のパーティションキーは(user_id)になってます。
キーとなってるuser_idをハッシュ関数に通さない限りどのパーティションにデータがあるかは分からないので、全パーティションなめるのは当然の話でした。


下記はuser_idで引いてる場合、パーティションをちゃんと刈り込んでるのが分かります。

> explain partitions select * from user_item where user_id=1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: user_item
   partitions: p4
         type: ref
possible_keys: user_item_type
          key: user_item_type
      key_len: 4
          ref: const
         rows: 2
        Extra:
1 row in set (0.00 sec)


何も考えずにプライマリーキー使っていたら少しはまったというお話でした。

判断と判断の変更

現状取り得る限り最善(と思われる)の技術的判断を常にしているとは思う。もちろんいろいろな技術的要因で次善の策になることもあるだろう。
ここで取った判断が後々問題になるのはよくあるとは思う。実際に運用してみないといろいろ分からない部分が多いから仕方ない。

そこで気を付けたいことは、既に決めた判断に拘泥するのではなく、現状を改善するために方向転換する勇気を持つことである。
過去の判断を変えると言うことは、ある意味そのときの自分(や判断した人)を否定するという意味にとってしまう人もいるかも知れない。判断した時点でその判断は人から切り離されたものであり、人そのものではない。というのを頭に置きたい。

また、判断を変えるにあたって、いろいろなものを変更しないといけない場合がある。そもそもの話として、方向転換しやすいように規模を小さく、依存性を小さく、変更を容易にできるようにしておくのが肝要である。

なんか、まとまりがなくなったので、中途半端だけどここに捨てておく。

Elasticsearchを1.4以上に上げたらkibana3がElasticsearchにConnection Failedする際の対処

Elasticsearchを1.4以降からセキュリティの関係でクロスドメイン通信が無効にされています。


この関係で、Elasticsearch1.3系から1.5系に上げたらkibana3がElasticsearchに接続できなくなったので、対処をメモ。

というか、エラーメッセージに書いてある通りのことすればいいんですけどね。

Connection Failed

Possibility #1: Your elasticsearch server is down or unreachable

This can be caused by a network outage, or a failure of the Elasticsearch process. If you have recently run a query that required a terms facet to be executed it is possible the process has run out of memory and stopped. Be sure to check your Elasticsearch logs for any sign of memory pressure.

Possibility #2: You are running Elasticsearch 1.4 or higher

Elasticsearch 1.4 ships with a security setting that prevents Kibana from connecting. You will need to set the following in your elasticsearch.yml:

  1. http.cors.enabled: true
  2. http.cors.allow-origin to the correct protocol, hostname, and port (if not 80) that your access Kibana from. Note that if you are running Kibana in a sub-url, you should exclude the sub-url path and only include the protocol, hostname and port. For example, http://mycompany.com:8080, not http://mycompany.com:8080/kibana.

Click back, or the home button, when you have resolved the connection issue

前提

  • Elasticsearch 1.5.2
  • Kibana 3.1.2
  • Elasticsearchとkibanaはそれぞれ別のhostで動いている
    • elasticsearch.masasuzu.local
    • kibana3.masasuzu.local

対処

elasticsearch.ymlに下記の設定を追記ししてElasticsearchを再起動します。

http.cors.enabled: true
http.cors.allow-origin: http://kibana3.masasuzu.local

http.cors.allow-originはデフォルトだと*になっていて良くないので、変えておきます。この設定は構成によって変わりうります。

サーバ起動時に/etc/init.d/ に設定があるデーモンを自動起動したい

最近のUbuntuだとupstartがちゃんと設定されてれば、そっちで制御できるんだけど、サードパーティーのパッケージとかで、入れるとたまにサーバ起動時に自動で立ち上がらないようになっていたりするやつもあったりします。
そういうときに自動起動するように設定するコマンドがupdate-rc.dだったりします。

コマンドをいっつも忘れるので、自分メモです。

自動起動設定

例はelasticsearch。
下記のコマンドを叩くと/etc/rc{0..6}.d/以下に/etc/init.d/elasticsearchのシムリンクを張ってくれます。
Kが先頭についている場合はそのrunlevelになったときに停止することを意味します。Sならスタート。
数字が小さいほど優先的に実行されるようですね。

update-rc.d elasticsearch defaults

自動起動解除

/etc/rc{0..6}.d/からシムリンクを削除してくれます。
/etc/init.d/に起動スクリプトが有る場合は-fオプションが必要です。

update-rc.d elasticsearch remove

ポートが空いてるか調べたいとき

サーバを立ち上げたけど、サービスになぜか繋がらないというのはよくある話。
プロセスが立ち上がってるのは確かだけど、繋がらない。そんなとき次見る場所としてはポートが空いてるかどうかです。

いくつかポートが空いてるかどうか調べる方法のメモを残しておきます。

ローカルポートが空いてるか見る

root権限じゃないとプロセス名が確認できないのでsudoしてます。

sudo netstat -tnp

netstatじゃなくて時代はssだという方は次の方法。
パイプ噛ましてる理由は、ssはデフォルトだとターミナルめいっぱいに表示が広がって見づらいので、こうしてます。他に良い方法知りたいです。

sudo ss -tnlp | cat

外からポートが空いてるか見る

プロトコルを話せるならtelnetで会話してみると良いでしょう。
会話しなくてもrefuseされなければ、ポートが空いてることが確認できます

telnet ${ip_address} ${port}

nmapでポート空いてるかみることができます。STATEがopenなら空いてる。closeなら閉じてる、filteredだとパケットが目的のサーバに到達できずに判定できない感じです。filteredになってるときは、ファイヤーウォールの設定やネットワーク経路を調べて見ると良いかもです。

nmap -sT -p${port} ${ip_address}

余談

よくあるミスとして、バインドアドレスが127.0.0.1だったりlocalhostになっていたりする場合があります。
その場合、立ち上げてるサーバからしかアクセス出来ないです。
デフォルトだとそうなってるのが多いので、そこ見てあげると良いでしょう。

あなたは嫌いですか。でも僕は好きです。

人は誰しも嫌いなものはあるので、別に嫌いなものは嫌いで構わないのですが、嫌いなことをことさら強調されると聞かされてる人は困ってしまいます。特に侮蔑的な言葉を使われると誰もいい気がしないです。

自分が嫌いなものでも、誰かに取っては好きなものであるかもしれないというのを心にとめておいて欲しいです。自分が好きなものに対して汚い言葉を浴びせられていい気分しないですよね?


僕は東京が好きです。生まれ育った土地です。でも、後から東京に来た人の中にはそうでもない人がいるようです。
別にそれはそれでかまわないのですが、もう少し考えて欲しいところです。

縮退運用という考え方

※ 社内の日記に書いたのの転載
※ アプリっていってるのはwebアプリ/webサービスのことをいってる


アクセス集中などで異常な高負荷になると1アプリ全体が使用不可能になってしまうことが多いです
こういうときは、だいたいにおいて、負荷が下がるのを天に祈りながら待つか、全体メンテナンスに入れてやり過ごすくらいしかできないです。
(もちろんその間に、負荷の原因を探ったり、コード直したり、サーバ追加できるならしたりします)
アプリ全体に障碍が起きているということは言うまでも無く、売り上げの減少、サービス自体の信用低下に繋がります。

全体停止はなるべく避けたい!せめて主要機能だけは動き続けて欲しい! そういう人のために縮退運用という考え方があります。

アプリの機能を見ていくと、この機能はアプリの根幹部分だから絶対に無いとダメなものと、この機能は枝葉なので必要無い、もしくは後でリカバリすれば良い機能があると思います。
高負荷時や障碍時に後者の部分の機能停止または機能制限することにより負荷を下げ、前者の機能を提供し続けてサービスを止めないことを縮退運用(フォールバックといったりもする)言います。
全体メンテナンスじゃ無くて、機能ごとにメンテナンスに入れるみたいなイメージだとわかりやすいかな。


縮退運用は大きな売り上げを上げており、少しの障碍が大幅な損失を生むようなサービスには合った方が望ましいですが、
機能ごとに依存関係があったり、大規模であったりすれば、その分実装コストが膨らむのでその辺をちゃんと考えると良いでしょう。


縮退運用というものもあるよ。という話でした。

$PATHを見やすく表示したい

某社の某所のSlackで$PATHを見やすく表示するワンライナーのいろんな方法を紹介してたので転載。
個人的な 備忘録。

zshで動作確認してます。

% zsh --version
zsh 5.0.7 (x86_64-apple-darwin14.0.0)

置換処理をperlに任せる

echo $PATH | perl -pe 's/:/\n/g'

echoプロセスを省略して、省エネする

perl -E 'say for split /:/, $ENV{PATH}’

perlの%ENVではなく元々のシェルの$PATHを参照するパターン

perl -E "say qq{$PATH} =~ s/:/\n/gr"

変数展開するパターン。自分が紹介したパターン。(zsh)

echo ${PATH//:/
}

クオートすれば¥nが使えるの(zsh)

echo ${PATH//:/"\n"}


変数展開覚えとくと便利。

ここから追記

twitterでツッコミをいただいたんですが、これzsh前提でbashだとうまく動かない様子。


$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin14)
Copyright (C) 2007 Free Software Foundation, Inc.
echo -e ${PATH//:/\\n}
# or
echo "${PATH//:$'/\n'}"

他の方法としては、sed使うパターン。実は私普段はPerlワンライナーよりsedを使う方が自分は慣れてます。


echo ${PATH} | sed -e "s/:/\n/g"

trを使うパターンがあります。trは普段全く使わないのでなるほどと言う感じでした。


echo "$PATH" | tr : \\n


思いがけないところで、シェルの知見が深まって感謝です。