地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

【Nginx】proxy_ignore_client_abortの効果を考えてみる

nginx.org

proxy_ignore_client_abortはデフォルト値がOFFの設定でONにすると以下のような動きになる。

クライアントが応答を待たずに接続を閉じたときに、プロキシされたサーバとの接続を閉じるかどうかを決定します。

これは何が嬉しいかというとリバースプロキシ先のコネクションが同時接続数=プロセス数みたいな大量アクセスをさばくのが苦手なアプリケーションなんかだと思われる。バックエンドのプロセスが50だとすると同時にさばけるのは50リクエストしか無い。50リクエストのうち10クライアントが消失(スマホなら高速移動したとか)した場合にNginxはproxy_ignore_client_abortがオフだとアプリケーションの応答を待ち続けるような動きとなる。アプリケーションの処理が例えば思いみたいなケースだとNginxは結構な時間無駄なコネクションを持ち続ける(つまりクライアント+プロキシ先とのコネクションを維持してしまう)。

proxy_ignore_client_abortをオンにすることでクライアントが接続を閉じたらプロキシサーバとの接続を閉じてくれる。アプリケーションとのコネクションはクローズされるだけでアプリケーションの処理は(アプリケーションの実装次第ではあるが多くは)継続される。そこも止めたいのなら違う手法が必要となる。

【MySQL】非ユニークなカラムのロックは広いかもしれない

非ユニークインデックスの場合は、等価条件であっても範囲条件に近いロックを取るという話。空振ってなくても思ったよりロック広い??ってなるケースとかはこれの可能性がありそう。ユニーク制約の付いていないカラムには同値を入れることができるため、現在存在しているnot_uniquのレコードだけロックしてもレコードの挿入を防ぐことはできない。

mysql> desc t1;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| ID    | int(11)     | NO   | PRI | NULL    |       |
| name  | varchar(30) | YES  | MUL | NULL    |       |
+-------+-------------+------+-----+---------+-------+

mysql> show index from t1\G
*************************** 1. row ***************************
        Table: t1
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: ID
    Collation: A
  Cardinality: 3
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:
*************************** 2. row ***************************
        Table: t1
   Non_unique: 1
     Key_name: id_index
 Seq_in_index: 1
  Column_name: name
    Collation: A
  Cardinality: 3
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment:
Index_comment:

mysql> select * from t1;
+----+------+
| ID | name |
+----+------+
|  3 | aaa  |
|  6 | bbb  |
|  9 | ddd  |
| 21 | ggg  |
+----+------+

例えばこんなテーブルがあるとしてnameは非ユニーク。ここでdddで条件検索で排他ロックをとってみる

mysql> begin;
mysql> select * from t1 where name = 'ddd' for update;

この時のinnodbの値はこんな感じ。PRIMARYはレコードロックをとっているがセカンダリインデックスの方はlock_mode X locks gap before recとなっておりGAPロック持ちとなっている

---TRANSACTION 1986, ACTIVE 17 sec
4 lock struct(s), heap size 1136, 3 row lock(s)
MySQL thread id 2, OS thread handle 139829509609216, query id 396 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`t1` trx id 1986 lock mode IX
RECORD LOCKS space id 24 page no 4 n bits 72 index id_index of table `test`.`t1` trx id 1986 lock_mode X
Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 3; hex 646464; asc ddd;;
 1: len 4; hex 80000009; asc     ;;

RECORD LOCKS space id 24 page no 3 n bits 80 index PRIMARY of table `test`.`t1` trx id 1986 lock_mode X locks rec but not gap
Record lock, heap no 4 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
 0: len 4; hex 80000009; asc     ;;
 1: len 6; hex 000000000707; asc       ;;
 2: len 7; hex a70000011b0128; asc       (;;
 3: len 3; hex 646464; asc ddd;;

RECORD LOCKS space id 24 page no 4 n bits 72 index id_index of table `test`.`t1` trx id 1986 lock_mode X locks gap before rec
Record lock, heap no 5 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 3; hex 676767; asc ggg;;
 1: len 4; hex 80000015; asc     ;;

別のトランザクションを起動してinsertを試してみる。bbb ~ gggまでのGAPに対してロックが取られているのがわかる

mysql> insert into t1 values(1, 'fff');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

mysql> insert into t1 values(8, 'aaab');
Query OK, 1 row affected (0.01 sec)

supremumとのロック

存在するレコードの末尾を排他ロックするとsupremumとのGAPロックを獲得するので予想外の範囲のロックを取る。

mysql> select * from t1 where name = 'ggg' for update;
+----+------+
| ID | name |
+----+------+
| 21 | ggg  |
+----+------+

------------
TRANSACTIONS
------------
Trx id counter 2016
Purge done for trx's n:o < 2015 undo n:o < 0 state: running but idle
History list length 0
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421304856972944, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421304856972024, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 2015, ACTIVE 183 sec
3 lock struct(s), heap size 1136, 3 row lock(s)
MySQL thread id 2, OS thread handle 139829509609216, query id 445 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`t1` trx id 2015 lock mode IX
RECORD LOCKS space id 24 page no 4 n bits 80 index id_index of table `test`.`t1` trx id 2015 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

Record lock, heap no 5 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 3; hex 676767; asc ggg;;
 1: len 4; hex 80000015; asc     ;;

RECORD LOCKS space id 24 page no 3 n bits 80 index PRIMARY of table `test`.`t1` trx id 2015 lock_mode X locks rec but not gap
Record lock, heap no 5 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
 0: len 4; hex 80000015; asc     ;;
 1: len 6; hex 000000000707; asc       ;;
 2: len 7; hex a70000011b0134; asc       4;;
 3: len 3; hex 676767; asc ggg;;

こうなってしまうとggg以降へのinsertができなくなってしまう。

mysql> insert into t1 values(11, 'gggg');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

mysql> insert into t1 values(8, 'hhhh');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

【MySQL】テーブルロック

テーブルのロック状態を確認する方法 テーブルのロック状態を確認するには、innodb_lock_waitsを確認することでロックされている状態を取得できます。

SELECT * FROM sys.innodb_lock_waits; テーブルのロック テーブルをロックするには、LOCK TABLES文を使います。

LOCK TABLEES テーブル名1[as 別名1] ロックの種類1 [,テーブル名2[as 別名2] ロックの種類2] ; ・ロックの種類  READ:データの読み込みのみ可能な状態、変更は出来ない  READ LOCAL:セッション内でのみREADロック  WRITE:ロックかけたスレッド以外は一切の操作が出来ない  LOW_PRIORITY WRITE:READとWRITEが競合した場合にREADを優先

テーブルのロック解除 テーブルをロック解除するには、UNLOCK TABLES文を使います。

UNLOCK TABLES;

【MySQL】ロックモニターのロックの内容ごとのメモ

プライマリインデックスに対する等価検索、空振り

lock_mode X locks gap before rec

select * from t1 where id = 5 for update;

------------
TRANSACTIONS
------------
Trx id counter 1957
Purge done for trx's n:o < 1939 undo n:o < 0 state: running but idle
History list length 0
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421304856972944, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421304856972024, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 1956, ACTIVE 10 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 2, OS thread handle 139829509609216, query id 318 localhost root starting
show engine innodb status
Trx read view will not see trx with id >= 1956, sees < 1956
TABLE LOCK table `test`.`t1` trx id 1956 lock mode IX
RECORD LOCKS space id 24 page no 3 n bits 80 index PRIMARY of table `test`.`t1` trx id 1956 lock_mode X locks gap before rec
Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
 0: len 4; hex 80000006; asc     ;;
 1: len 6; hex 000000000722; asc      ";;
 2: len 7; hex 3b00000130036d; asc ;   0 m;;
 3: len 3; hex 626262; asc bbb;;

プライマリインデックスに対する範囲検索、空振り

lock_mode X

select * from t1 where id between 4 and 5 for update;

------------
TRANSACTIONS
------------
Trx id counter 1958
Purge done for trx's n:o < 1939 undo n:o < 0 state: running but idle
History list length 0
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421304856972944, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421304856972024, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 1957, ACTIVE 3 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 2, OS thread handle 139829509609216, query id 322 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`t1` trx id 1957 lock mode IX
RECORD LOCKS space id 24 page no 3 n bits 80 index PRIMARY of table `test`.`t1` trx id 1957 lock_mode X
Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
 0: len 4; hex 80000006; asc     ;;
 1: len 6; hex 000000000722; asc      ";;
 2: len 7; hex 3b00000130036d; asc ;   0 m;;
 3: len 3; hex 626262; asc bbb;;

プライマリインデックスに対する等価検索

lock_mode X locks rec but not gap

------------
TRANSACTIONS
------------
Trx id counter 1959
Purge done for trx's n:o < 1939 undo n:o < 0 state: running but idle
History list length 0
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421304856972944, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421304856972024, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 1958, ACTIVE 3 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 2, OS thread handle 139829509609216, query id 326 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`t1` trx id 1958 lock mode IX
RECORD LOCKS space id 24 page no 3 n bits 80 index PRIMARY of table `test`.`t1` trx id 1958 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
 0: len 4; hex 80000006; asc     ;;
 1: len 6; hex 000000000722; asc      ";;
 2: len 7; hex 3b00000130036d; asc ;   0 m;;
 3: len 3; hex 626262; asc bbb;;