CubicLouve

Spring_MTの技術ブログ

MySQLにおける外部キー作成時の自動インデックス生成

MySQLの外部キー制約において、外部キーと参照キーにはインデックスが必要です。

下記はMySQL 8.0のドキュメントですが、5.7でも同じような内容となっています。

dev.mysql.com

参照元のテーブルには、外部キーのカラムが同じ順序で最初のカラムとしてならぶインデックスが必要です。 もし該当するindexがなければ、MySQLは自動でインデックスを作成します。 この自動で作られたインデックスは後で追加したインデックスが外部キーのインデックス要件を満たす場合、暗黙的に削除されることがあります。

これを実際に試してみようと思います

確認したMySQLのバージョンは 8.0.29 です。

まずテーブルを作ってみます

mysql> CREATE TABLE `users` (
    ->   `id` int NOT NULL,
    ->   PRIMARY KEY (`id`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected (0.01 sec)

mysql> CREATE TABLE `user_foo` (
    ->   `id` int NOT NULL AUTO_INCREMENT,
    ->   `user_id` int NOT NULL,
    ->   `foo` int  NOT NULL,
    ->   PRIMARY KEY (`id`),
    ->   CONSTRAINT FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected (0.01 sec)

これでテーブル定義を見てみます。

mysql> SHOW CREATE TABLE user_foo;
+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table    | Create Table                                                                                                                                                                                                                                                                                                         |
+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| user_foo | CREATE TABLE `user_foo` (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_id` int NOT NULL,
  `foo` int NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`), <----- CREATE文にないindexが追加されている
  CONSTRAINT `user_foo_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

CREATE文にないインデックスが自動で作られます。

外部キーのcolumnが同じ順序で最初のカラムとしてならぶインデックスが必要です。 これを確認してみましょう。

外部キーを含む複数カラムに対してセカンダリインデックスを追加してみます。

mysql> CREATE TABLE `users` (
    ->   `id` int NOT NULL,
    ->   PRIMARY KEY (`id`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected (0.01 sec)

mysql> CREATE TABLE `user_foo` (
    ->   `id` int NOT NULL AUTO_INCREMENT,
    ->   `user_id` int NOT NULL,
    ->   `foo` int  NOT NULL,
    ->   PRIMARY KEY (`id`),
    ->   KEY (`user_id`, `foo`),
    ->   CONSTRAINT FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected (0.01 sec)

mysql> SHOW CREATE TABLE user_foo;                                                                                                                                                                                     +----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table    | Create Table                                                                                                                                                                                                                                                                                                               |
+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| user_foo | CREATE TABLE `user_foo` (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_id` int NOT NULL,
  `foo` int NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`,`foo`),
  CONSTRAINT `user_foo_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

今度は自動でのインデックスが追加されていません。

user_idとfooのセカンダリインデックスで外部キー制約のインデックスの要件を満たしたためですね。

では今度はuser_idとfooを逆にしてみます。

mysql> CREATE TABLE `users` (
    ->   `id` int NOT NULL,
    ->   PRIMARY KEY (`id`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected (0.01 sec)

mysql> CREATE TABLE `user_foo` (
    ->   `id` int NOT NULL AUTO_INCREMENT,
    ->   `user_id` int NOT NULL,
    ->   `foo` int  NOT NULL,
    ->   PRIMARY KEY (`id`),
    ->   KEY (`foo`, `user_id`),
    ->   CONSTRAINT FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected (0.01 sec)

mysql> SHOW CREATE TABLE user_foo;
+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table    | Create Table                                                                                                                                                                                                                                                                                                                                        |
+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| user_foo | CREATE TABLE `user_foo` (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_id` int NOT NULL,
  `foo` int NOT NULL,
  PRIMARY KEY (`id`),
  KEY `foo` (`foo`,`user_id`),
  KEY `user_id` (`user_id`),
  CONSTRAINT `user_foo_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

今度は 外部キーのcolumnが同じ順序で最初のカラムとしてならぶインデックスにならなかったため、user_id単独のセカンダリインデックスが自動で追加されました。

今度は外部キー制約を満たすために追加したインデックスが不要になるインデックスが追加されたときの挙動を見てみます。

mysql> CREATE TABLE `users` (
    ->   `id` int NOT NULL,
    ->   PRIMARY KEY (`id`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected (0.01 sec)

mysql> CREATE TABLE `user_foo` (
    ->   `id` int NOT NULL AUTO_INCREMENT,
    ->   `user_id` int NOT NULL,
    ->   `foo` int  NOT NULL,
    ->   PRIMARY KEY (`id`),
    ->   CONSTRAINT FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected (0.01 sec)

mysql> SHOW CREATE TABLE user_foo;
+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table    | Create Table                                                                                                                                                                                                                                                                                                         |
+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| user_foo | CREATE TABLE `user_foo` (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_id` int NOT NULL,
  `foo` int NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`), <---- 自動で追加されたインデックス
  CONSTRAINT `user_foo_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

自動でインデックスが追加されています。

では、新たにインデックスを追加してみます。

mysql> ALTER TABLE user_foo ADD INDEX (`user_id`,`foo`); 
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> SHOW CREATE TABLE user_foo;
+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table    | Create Table                                                                                                                                                                                                                                                                                                               |
+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| user_foo | CREATE TABLE `user_foo` (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_id` int NOT NULL,
  `foo` int NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`,`foo`),
  CONSTRAINT `user_foo_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

user_id単体ではられていたインデックスが削除され、ALTERによって追加されたインデックスのみが残りました。

ではこのインデックスを削除してみるとどうなでしょう。

mysql> ALTER TABLE user_foo DROP INDEX `user_id`;
ERROR 1553 (HY000): Cannot drop index 'user_id': needed in a foreign key constraint

この場合は、自動でインデックスが追加されるわけではなく、外部キー制約に必要なるということでエラーになります。

ActiveRecordのcallbackの実装を調べたときのメモ

Railsのcallbackの実体

callbackはActiveSupport::Callbacksを使って定義されます。

github.com

ActiveRecordのcallback

ActiveRecord::Callbacksに定義があります。

github.com

ここでcreateなどが定義されます。

github.com

define_model_callbacksは下記で実装されています。

github.com

例 : after_create hookの呼び出され方

ActiveRecord::Baseを継承したUserクラスを考えます。

Railsのバージョンは6.1.4.6を使っています。

Userクラスにはcreateというクラスメソッドがあり、recordを登録できます。

実体は、

irb(main):002:0> User.method(:create)
=> #<Method: User(id: integer, .....).create(attributes=..., &block) /path/to/..../gems/activerecord-6.1.4.6/lib/active_record/persistence.rb:33>

なので、下記です。

github.com

hookがどのように呼び出されるかがわかりにくいので、下記でbreak pointを仕込んでおきます。

github.com

で、after_commit のcallbackをなにかしら設定しておいて、User.createを実行してみます

irb(main):001:0> User.create(....)

[1] pry(User)> 

  TRANSACTION (0.2ms)  BEGIN

From:  /path/to/..../gems/activerecord-6.1.4.6/lib/active_record/callbacks.rb:461 ActiveRecord::Callbacks#_create_record:

    460: def _create_record
 => 461:   binding.pry
    462:   _run_create_callbacks { super }
    463: end

とまりましたね。 ここでcallerを一部抜粋すると

 "/activerecord-6.1.4.6/lib/active_record/callbacks.rb:461:in `_create_record'",
 "/activerecord-6.1.4.6/lib/active_record/timestamp.rb:108:in `_create_record'",
 "/activerecord-6.1.4.6/lib/active_record/persistence.rb:903:in `create_or_update'",
 "/activerecord-6.1.4.6/lib/active_record/callbacks.rb:457:in `block in create_or_update'",

"/activerecord-6.1.4.6/lib/active_record/persistence.rb:477:in `save'"

saveから callbacksの _create_record が呼ばれて、ここでcallbackが発動することとなります。

after_createはcommit前に発動するので、このcallback内で新しくinsertした内容を別のトランザクションからは取得できないので要注意です。

Railsのrspecのrequest specについて

rspec.info

上記の記事にあるように、Rails 5以降ではrails-controller-testing gemをアプリケーションに追加することは推奨されておらず、RSpecコアチームはrequest specsを書くことを推奨しています。

その理由として、request specでは、単一のcontroller actionをテストしつつ、さらにcontroller specとは異なり、router、middleware、rackのrequestとresponseが含まれることになります。

これにより、書いているテストがより実際のリクエストに近くなり、controller testにありがちな問題を回避することができます。

Rails 5ではRails 4のrequest specやcontroller specに比べてかなりの高速化も行われています。

このように、controller specはよりrequest specを書くことがRails 5以降のデファクトとなっています。

GitHub - rspec/rspec-rails: RSpec for Rails 5+

request spec - Request specs - RSpec Rails - RSpec - Relish

request specの使用

rspec-railsを使った場合、specのtypeをtop-levelのdescribeで指定します。

RSpec.describe User, type: :request do

requestを指定することで、ActionDispatch::IntegrationTestに相当することになります。

api.rubyonrails.org

この中でHTTP requestをシュミレートする get post patch put delete head メソッドを使うことになります。

controller specでも同じようなメソッドを使うことになりますが、呼び出されたメソッドが実行するものが違います。

実装は下記になります。

github.com

この中で、 Rack::Test::Session が使われており、Rails appではなく、Rack appとしてリクエストを処理するようになっています。

github.com

request specはRackのレイヤーまでを網羅したテストとなっています。

ちょっとハマった点

rack appに対するリクエストを組み立てテストを行うのですが、Rack::Test:Session#requestの実装が少し色々やりすぎててハマりました

github.com

このなかで、specで定義したparamsをURL-encoded form dataにエンコードを自動的に行なっているので、テストもとでencodeすると二重でencodeされることになります。

こういった処理をどこまでやるかの判断は難しいですが、自動的に行われることは意識しておいていいかなと思います。

線形合同法(Linear congruential method)のスペクトル検定による可視化

線形合同法擬似乱数生成器の一つ

\displaystyle{
X_{n+1} = (A \times X_{n} + C)  \bmod M
}

ここでのA(乗数) C(増分) M(法)は定数で、AとCはMより小さい数を選ぶ。

このA C Mの選び方によって周期性は変わる。

どういう選び方すればをいいかは下記本を参照ください。

線形合同法で生成された数列がどれくらい乱雑かを判定する方法の一つにスペクトル検定がある。

今回は三次元でこれをグラフにしてみた。

Rubyのrandはメルセンヌ・ツイスタなので、それと線形合同法で生成した乱数の分布を比べてみる。

docs.ruby-lang.org

docs.ruby-lang.org

適当なスクリプトを書いて可視化した結果が下記の図となる。

可視化にはGR.rbを利用している。

github.com

  • 線形合同法(A(乗数)を137、C(増分)を187、M(法)を256にしているので規則性がわざと出やすくしている)

こう見ても今回のパラメータの線形合同法の結果は乱雑さがない感じがする。

参考資料

ja.wikipedia.org

http://www.math.sci.hiroshima-u.ac.jp/m-mat/TEACH/ichimura-sho-koen.pdf

tsujimotter.hatenablog.com

バイナリファイルを見る方法

白く塗りつぶしたpngファイルの例でやってみる。

% convert -size 128x128 xc:white white.png

vimを使う場合

vim -b white.png

でバイナリモードでvimで開いた後に下記コマンドでバイナリダンプします。

:%!xxd
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR
00000010: 0000 0080 0000 0080 0100 0000 00eb 455c  ..............E\
00000020: 6600 0000 0467 414d 4100 00b1 8f0b fc61  f....gAMA......a
00000030: 0500 0000 2063 4852 4d00 007a 2600 0080  .... cHRM..z&...
00000040: 8400 00fa 0000 0080 e800 0075 3000 00ea  ...........u0...
00000050: 6000 003a 9800 0017 709c ba51 3c00 0000  `..:....p..Q<...
00000060: 0262 4b47 4400 01dd 8a13 a400 0000 0774  .bKGD..........t
00000070: 494d 4507 e602 170e 1622 78a1 b740 0000  IME......"x..@..
00000080: 001f 4944 4154 48c7 63f8 8f06 1846 0546  ..IDATH.c....F.F
00000090: 0546 0546 0546 0546 0546 0546 0568 2b00  .F.F.F.F.F.F.h+.
000000a0: 0004 a0f8 6a22 6527 5400 0000 2574 4558  ....j"e'T...%tEX
000000b0: 7464 6174 653a 6372 6561 7465 0032 3032  tdate:create.202
000000c0: 322d 3032 2d32 3354 3134 3a32 323a 3334  2-02-23T14:22:34
000000d0: 2b30 303a 3030 d063 4c5a 0000 0025 7445  +00:00.cLZ...%tE
000000e0: 5874 6461 7465 3a6d 6f64 6966 7900 3230  Xtdate:modify.20
000000f0: 3232 2d30 322d 3233 5431 343a 3232 3a33  22-02-23T14:22:3
00000100: 342b 3030 3a30 30a1 3ef4 e600 0000 0049  4+00:00.>......I
00000110: 454e 44ae 4260 82                        END.B`.

ちなみにバイナリモードでファイルを開かないとバイナリ表示がおかしくなるので注意 下記はバイナリモードで開かなかった場合のバイナリダンプ

00000000: 3f50 4e47 0d0a 1a0a 0000 000d 4948 4452  ?PNG........IHDR
00000010: 0000 003f 0000 003f 0100 0000 003f 455c  ...?...?.....?E\
00000020: 6600 0000 0467 414d 4100 003f 3f0b 3f61  f....gAMA..??.?a
00000030: 0500 0000 2063 4852 4d00 007a 2600 003f  .... cHRM..z&..?
00000040: 3f00 003f 0000 003f 3f00 0075 3000 003f  ?..?...??..u0..?
00000050: 6000 003a 3f00 0017 703f 3f51 3c00 0000  `..:?...p??Q<...
00000060: 0262 4b47 4400 01dd 8a13 3f00 0000 0774  .bKGD.....?....t
00000070: 494d 4507 3f02 170e 1622 783f 3f40 0000  IME.?...."x??@..
00000080: 001f 4944 4154 483f 633f 3f06 1846 0546  ..IDATH?c??..F.F
00000090: 0546 0546 0546 0546 0546 0546 0568 2b00  .F.F.F.F.F.F.h+.
000000a0: 0004 3f3f 6a22 6527 5400 0000 2574 4558  ..??j"e'T...%tEX
000000b0: 7464 6174 653a 6372 6561 7465 0032 3032  tdate:create.202
000000c0: 322d 3032 2d32 3354 3134 3a32 323a 3334  2-02-23T14:22:34
000000d0: 2b30 303a 3030 3f63 4c5a 0000 0025 7445  +00:00?cLZ...%tE
000000e0: 5874 6461 7465 3a6d 6f64 6966 7900 3230  Xtdate:modify.20
000000f0: 3232 2d30 322d 3233 5431 343a 3232 3a33  22-02-23T14:22:3
00000100: 342b 3030 3a30 303f 3e3f 3f00 0000 0049  4+00:00?>??....I
00000110: 454e 443f 4260 3f0a                      END?B`?.

hexdumpコマンドを使う場合

受け取ったデータを8進数や16進数でダンプする。(デフォルト16進数) hexdumpはオプションなしでは、2バイト単位で処理し、リトルエンディアン(最下位のバイトから順番に表示)で表示します。 -C オプションをつけることで1バイトずつ処理をする。

なので、基本 -C オプションを付けて使う。

% hexdump -C white.png
00000000  89 50 4e 47 0d 0a 1a 0a  00 00 00 0d 49 48 44 52  |.PNG........IHDR|
00000010  00 00 00 80 00 00 00 80  01 00 00 00 00 eb 45 5c  |..............E\|
00000020  66 00 00 00 04 67 41 4d  41 00 00 b1 8f 0b fc 61  |f....gAMA......a|
00000030  05 00 00 00 20 63 48 52  4d 00 00 7a 26 00 00 80  |.... cHRM..z&...|
00000040  84 00 00 fa 00 00 00 80  e8 00 00 75 30 00 00 ea  |...........u0...|
00000050  60 00 00 3a 98 00 00 17  70 9c ba 51 3c 00 00 00  |`..:....p..Q<...|
00000060  02 62 4b 47 44 00 01 dd  8a 13 a4 00 00 00 07 74  |.bKGD..........t|
00000070  49 4d 45 07 e6 02 17 0e  16 22 78 a1 b7 40 00 00  |IME......"x..@..|
00000080  00 1f 49 44 41 54 48 c7  63 f8 8f 06 18 46 05 46  |..IDATH.c....F.F|
00000090  05 46 05 46 05 46 05 46  05 46 05 46 05 68 2b 00  |.F.F.F.F.F.F.h+.|
000000a0  00 04 a0 f8 6a 22 65 27  54 00 00 00 25 74 45 58  |....j"e'T...%tEX|
000000b0  74 64 61 74 65 3a 63 72  65 61 74 65 00 32 30 32  |tdate:create.202|
000000c0  32 2d 30 32 2d 32 33 54  31 34 3a 32 32 3a 33 34  |2-02-23T14:22:34|
000000d0  2b 30 30 3a 30 30 d0 63  4c 5a 00 00 00 25 74 45  |+00:00.cLZ...%tE|
000000e0  58 74 64 61 74 65 3a 6d  6f 64 69 66 79 00 32 30  |Xtdate:modify.20|
000000f0  32 32 2d 30 32 2d 32 33  54 31 34 3a 32 32 3a 33  |22-02-23T14:22:3|
00000100  34 2b 30 30 3a 30 30 a1  3e f4 e6 00 00 00 00 49  |4+00:00.>......I|
00000110  45 4e 44 ae 42 60 82                              |END.B`.|
00000117

xxdコマンドを使う場合

ファイルを16進数でダンプする。 復元もできるのがポイント。

% xxd white.png
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR
00000010: 0000 0080 0000 0080 0100 0000 00eb 455c  ..............E\
00000020: 6600 0000 0467 414d 4100 00b1 8f0b fc61  f....gAMA......a
00000030: 0500 0000 2063 4852 4d00 007a 2600 0080  .... cHRM..z&...
00000040: 8400 00fa 0000 0080 e800 0075 3000 00ea  ...........u0...
00000050: 6000 003a 9800 0017 709c ba51 3c00 0000  `..:....p..Q<...
00000060: 0262 4b47 4400 01dd 8a13 a400 0000 0774  .bKGD..........t
00000070: 494d 4507 e602 170e 1622 78a1 b740 0000  IME......"x..@..
00000080: 001f 4944 4154 48c7 63f8 8f06 1846 0546  ..IDATH.c....F.F
00000090: 0546 0546 0546 0546 0546 0546 0568 2b00  .F.F.F.F.F.F.h+.
000000a0: 0004 a0f8 6a22 6527 5400 0000 2574 4558  ....j"e'T...%tEX
000000b0: 7464 6174 653a 6372 6561 7465 0032 3032  tdate:create.202
000000c0: 322d 3032 2d32 3354 3134 3a32 323a 3334  2-02-23T14:22:34
000000d0: 2b30 303a 3030 d063 4c5a 0000 0025 7445  +00:00.cLZ...%tE
000000e0: 5874 6461 7465 3a6d 6f64 6966 7900 3230  Xtdate:modify.20
000000f0: 3232 2d30 322d 3233 5431 343a 3232 3a33  22-02-23T14:22:3
00000100: 342b 3030 3a30 30a1 3ef4 e600 0000 0049  4+00:00.>......I
00000110: 454e 44ae 4260 82                        END.B`.

xxd -r でdumpしたファイルから復元して新しいデータとして保存する

lsofの使い方まとめてみた

github.com

lsof コマンドはプロセスがオープンしているファイルの一覧を表示します。

manには下記のように記載されている。

lsof - list open files

ファイルはディスク上のファイルだけでなく、ネットワークソケット、デバイスなども含まれるため、オープンしているファイルを調べることで、ネットワークのリッスンしているポート(待受ポート)などもわかりネットワーク周りの調査でもよく使うコマンド。

表示項目

項目 説明
COMMAND プロセス名(+c num で必要な文字数取れる)
PID プロセスID
USER 実行ユーザー
FD ファイルディスクリプター(それに続く英字1文字は r read access、w write access、u read and write access を指す)
TYPE ファイルの種別、例) IPv4IPv4 socket、LINK は symbolic link fileとか
DEVICE バイス番号
SIZE/OFF ファイルサイズまたはオフセット、0t または 0xで始まる場合はオフセット
NODE ファイルのノード番号 だったりインターネットプロトコルだったりする
NAME ファイル名だったりマウントポイント名だったり
リモートアドレス(<net>:[<node>:]<port> とそれに続いて接続状態も表示される場合もある ) だったりする

オプション

lsof(8) - Linux manual page

オプション 説明
-n 名前解決をせずにIPアドレスを表示する。名前解決をしないので高速に表示されます
-P ポート番号をポート名に変換しない。 例) 443 ポートを https に変換しない
+c プロセス名の表示数(15まで、0を指定すると最大になる)
-i ネットワークファイルのみを対象とする。色々書き方があり、-iTCP だとTCPのみ、-i:ポート番号 で利用しているポートで絞り込める
形式は→ [46][protocol][@hostname|hostaddr][:service|port]
-U Unix domain socketの一覧を取得する

よく使うコマンド

UDP一覧

lsof -n -P +c 0 -iUDP 

複数の特定ポート

lsof -n -P +c 0 -i:8080,3000

create-clusterコマンドを使ってローカルでredis clusterを簡単に作成する方法

redis の中に 含まれている create-cluster コマンドを使うと、Redis Clusterを簡単にローカルで立てることができたのでそのメモ。

手順

redisをcloneしてくる

git clone git@github.com:redis/redis.git
cd redis

redisをビルドする

make

自分の環境ではmacOSでビルドは通っている。

create-clusterコマンドがあるdirectoryまで移動

cd utils/create-cluster

必要であれば config.sh を置いて環境変数を設定する

config.sh というファイルがあるとcreate-cluster は自動的にそれを読み込んでくれる。

設定可能な環境変数は下記を参照

github.com

BIN_PATHを設定すれば、任意のredis-cliコマンドの場所を指定できる。

redis clusterに必要なnodeを立てる

create-cluster start を実行してnodeを作る

 % ./create-cluster start
Starting 30001
Starting 30002
Starting 30003
Starting 30004
Starting 30005
Starting 30006

作成したnodeをclusterにする

% ./create-cluster create
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:30005 to 127.0.0.1:30001
Adding replica 127.0.0.1:30006 to 127.0.0.1:30002
Adding replica 127.0.0.1:30004 to 127.0.0.1:30003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: df85f1ae9a3ac6728d80db7e7545c036eefff225 127.0.0.1:30001
   slots:[0-5460] (5461 slots) master
M: c195571fa6c08fef6319591e71148817934f87b4 127.0.0.1:30002
   slots:[5461-10922] (5462 slots) master
M: 34a8974de69408ec6956834363392f3735cf6f96 127.0.0.1:30003
   slots:[10923-16383] (5461 slots) master
S: ca3c5200b2b57ea7242955024418df051a16609a 127.0.0.1:30004
   replicates df85f1ae9a3ac6728d80db7e7545c036eefff225
S: 15aebd8d8d14bd3df30ee5ee3c72d02b75101d12 127.0.0.1:30005
   replicates c195571fa6c08fef6319591e71148817934f87b4
S: 69f8274ef598072797ada3e2b7d458a50ebd4f62 127.0.0.1:30006
   replicates 34a8974de69408ec6956834363392f3735cf6f96
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join

>>> Performing Cluster Check (using node 127.0.0.1:30001)
M: df85f1ae9a3ac6728d80db7e7545c036eefff225 127.0.0.1:30001
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 15aebd8d8d14bd3df30ee5ee3c72d02b75101d12 127.0.0.1:30005
   slots: (0 slots) slave
   replicates c195571fa6c08fef6319591e71148817934f87b4
M: c195571fa6c08fef6319591e71148817934f87b4 127.0.0.1:30002
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 69f8274ef598072797ada3e2b7d458a50ebd4f62 127.0.0.1:30006
   slots: (0 slots) slave
   replicates 34a8974de69408ec6956834363392f3735cf6f96
S: ca3c5200b2b57ea7242955024418df051a16609a 127.0.0.1:30004
   slots: (0 slots) slave
   replicates df85f1ae9a3ac6728d80db7e7545c036eefff225
M: 34a8974de69408ec6956834363392f3735cf6f96 127.0.0.1:30003
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

[OK] All 16384 slots covered. がでれば、clusterの作成が完了となる。

その他

ログの確認

% ./create-cluster tailall 

クラスタを止める

% ./create-cluster stop