ãã®è¨äºã¯ãLayerX Tech Advent Calendar 2024 ã®7æ¥ç®ã®è¨äºã§ãã
ããã«ã¡ã¯ããã¯ã©ã¯ãã¸ãã¹ã«ã¼ãéçºãã¼ã ã¨ã³ã¸ãã¢ã® iwamatsu ã§ãã
ä½ãæ¸ããã¨ãªãããªãã¨é ãæ©ã¾ãã¦ããã¨ãããè¦ããã¨ã®ãªãä¸æè°ãªãããããã¯ã«åºããããã®ã§ãä»åã¯ããã«ã¤ãã¦æ¸ããã¨æãã¾ãã
å®è¡ç°å¢
- ãã¼ã¸ã§ã³: MySQL 8.0.39
- ã¹ãã¬ã¼ã¸ã¨ã³ã¸ã³: InnoDB
- ãã©ã³ã¶ã¯ã·ã§ã³åé¢ã¬ãã«:
REPEATABLE READ
çºçããäºè±¡
以ä¸ã®ãããªã¦ã¼ã¶ã¼ãã¼ãã«ãããã¨ãã¾ãã
CREATE TABLE `users` ( `id` INT NOT NULL, `name` VARCHAR(255) NOT NULL, `lucky_color` VARCHAR(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB;
ã¦ã¼ã¶ã¼ã2,000人ããã¨ãã¾ãã
INSERT INTO users (id, name, lucky_color) VALUES (1, 'user1', 'white'), (2, 'user2', 'white'), (3, 'user3', 'white'), ... (2000, 'user2000', 'white');
ã¾ã Tx1 㧠id ã3ã®ã¦ã¼ã¶ã¼ã®ã©ããã¼ã«ã©ã¼ã赤ã«æ´æ°ãã¾ãã
-- Tx1 BEGIN; UPDATE users SET lucky_color = 'red' WHERE id = 3;
次㫠Tx2 㧠id ãå¶æ°ã®ã¦ã¼ã¶ã¼ã®ã©ããã¼ã«ã©ã¼ãéã«æ´æ°ãã¾ãã
-- Tx2 BEGIN; UPDATE users SET lucky_color = 'blue' WHERE id IN (2, 4, 6, ...2000);
ãã㦠Tx1 ã«æ»ããid ã1ã®ã¦ã¼ã¶ã¼ã®ã©ããã¼ã«ã©ã¼ãç·ã«æ´æ°ãã¾ãã
-- Tx1 UPDATE users SET lucky_color = 'green' WHERE id = 1;
ããã§ãããããã¯ãçºçãã¾ã (!?)ã
Tx1 㯠id ãå¥æ°ã®ã¦ã¼ã¶ã¼ãTx2 㯠id ãå¶æ°ã®ã¦ã¼ã¶ã¼ãããããæ´æ°ãã¦ããã®ã§ãããã¯ã競åãããã¨ã¯ãªãããã«æãã¾ããããªãã¨ãããããã¯ãçºçããã®ã§ãã
ãªãããããããããã¯ã«ãªããã«ã¤ãã¦ã以ä¸ã§èª¬æãã¦ããã¾ãã
調æ»æé
InnoDB ã¢ãã¿ã¼ãè¦ã
ãããããã¯ã®åå ã調æ»ããéã¯ãã¾ã InnoDB ã¢ãã¿ã¼ (SHOW ENGINE INNODB STATUS
) ãè¦ãã®ãããããã§ãã
InnoDB ã¢ãã¿ã¼ãè¦ãéã¯ãäºåã«æ¨æºã¢ãã¿ã¼ã¨ããã¯ã¢ãã¿ã¼ãæå¹åãã¦ããå¿ è¦ãããã¾ãã
SET GLOBAL innodb_status_output=ON; SET GLOBAL innodb_status_output_locks=ON;
æå¹åããããããããã¯ãåç¾ããã¦ãåºåå 容ãè¦ã¦ã¿ã¾ãããã
SHOW ENGINE INNODB STATUS\G
LATEST DETECTED DEADLOCK
ã¨ããã»ã¯ã·ã§ã³ã«ã以ä¸ã®ãããªæ§æã§ãããããã¯ã®æ
å ±ãåºåããã¦ãããã¨æãã¾ãã
*** (1) TRANSACTION: ãã©ã³ã¶ã¯ã·ã§ã³(1)ã®æ å ± *** (1) HOLDS THE LOCK(S): ãã©ã³ã¶ã¯ã·ã§ã³(1)ãä¿æãã¦ãããã㯠*** (1) WAITING FOR THIS LOCK TO BE GRANTED: ãã©ã³ã¶ã¯ã·ã§ã³(1)ãåå¾ãããã¨ãã¦ãããã㯠*** (2) TRANSACTION: ãã©ã³ã¶ã¯ã·ã§ã³(2)ã®æ å ± *** (2) HOLDS THE LOCK(S): ãã©ã³ã¶ã¯ã·ã§ã³(2)ãä¿æãã¦ãããã㯠*** (2) WAITING FOR THIS LOCK TO BE GRANTED: ãã©ã³ã¶ã¯ã·ã§ã³(2)ãåå¾ãããã¨ãã¦ãããã㯠*** WE ROLL BACK TRANSACTION (2)
ã¾ã㯠Tx1 ã®æ å ±ãè¦ã¦ã¿ã¾ãã
Tx1 㯠user3 ã®ã¬ã³ã¼ãããã¯ãä¿æãã¦ãã¾ãã
RECORD LOCKS space id 60646 page no 5 n bits 496 index PRIMARY of table `work`.`users` trx id 1301525 lock_mode X locks rec but not gap Record lock, heap no 425 PHYSICAL RECORD: n_fields 5; compact format; info bits 0 0: len 4; hex 80000003; asc ;; 1: len 6; hex 00000013dc15; asc ;; 2: len 7; hex 0100000126034b; asc & K;; 3: len 5; hex 7573657233; asc user3;; 4: len 3; hex 726564; asc red;;
field0 ã idãfield1, 2 ãå¶å¾¡ç¨ã®å¤ãfield3, 4 ã name, lucky_color ã§ãã
ããã¦æ¬¡ã« user1 ã®ã¬ã³ã¼ãããã¯ãåå¾ãããã¨ãã¦ãã¾ãã
RECORD LOCKS space id 60646 page no 5 n bits 496 index PRIMARY of table `work`.`users` trx id 1301525 lock_mode X locks rec but not gap waiting Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0 0: len 4; hex 80000001; asc ;; 1: len 6; hex 00000013daa3; asc ;; 2: len 7; hex 82000000900110; asc ;; 3: len 5; hex 7573657231; asc user1;; 4: len 5; hex 7768697465; asc white;;
ããã¾ã§ã¯å®è¡ãã UPDATE ã¨ä¸è´ãã¦ããã®ã§æ³å®éãã§ãã
次㫠Tx2 ã®æ å ±ãè¦ã¦ã¿ã¾ãã
Tx2 㯠user1 㨠user2 ã®ã¬ã³ã¼ããã㯠(ã¨ã®ã£ããããã¯) ãä¿æãã¦ãã¾ã (!?)ã
RECORD LOCKS space id 60646 page no 5 n bits 496 index PRIMARY of table `work`.`users` trx id 1301526 lock_mode X Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0 0: len 4; hex 80000001; asc ;; 1: len 6; hex 00000013daa3; asc ;; 2: len 7; hex 82000000900110; asc ;; 3: len 5; hex 7573657231; asc user1;; 4: len 5; hex 7768697465; asc white;; Record lock, heap no 424 PHYSICAL RECORD: n_fields 5; compact format; info bits 0 0: len 4; hex 80000002; asc ;; 1: len 6; hex 00000013dc16; asc ;; 2: len 7; hex 02000000ec1d45; asc E;; 3: len 5; hex 7573657232; asc user2;; 4: len 4; hex 626c7565; asc blue;;
user2 ã¯IN å¥ã§æå®ãã¦ããã®ã§æ³å®éãã§ãããæå®ãã¦ããªã user1 ã®ããã¯ããªããä¿æãã¦ãã¾ãã
ããã¦æ¬¡ã« user3 ã®ã¬ã³ã¼ããã㯠(ã¨ã®ã£ããããã¯) ãåå¾ãããã¨ãã¦ãã¾ã (!?)ã
RECORD LOCKS space id 60646 page no 5 n bits 496 index PRIMARY of table `work`.`users` trx id 1301526 lock_mode X waiting Record lock, heap no 425 PHYSICAL RECORD: n_fields 5; compact format; info bits 0 0: len 4; hex 80000003; asc ;; 1: len 6; hex 00000013dc15; asc ;; 2: len 7; hex 0100000126034b; asc & K;; 3: len 5; hex 7573657233; asc user3;; 4: len 3; hex 726564; asc red;;
ããã¾ã IN å¥ã§æå®ãã¦ããªã user3 ã®ããã¯ãåå¾ãããã¨ãã¦ãã¾ãã
ä¸æ¦ããã¾ã§ã®æ å ±ããã以ä¸ã®ç¶æ ã§ãããããã¯ãçºçãããã¨ãåããã¾ããã
- Tx1 㯠user3 ã®ã¬ã³ã¼ãããã¯ãä¿æãuser1 ã®ã¬ã³ã¼ãããã¯ãåå¾ãããã¨ãã¦ããã
- Tx2 㯠user1, 2 ã®ã¬ã³ã¼ãããã¯ãä¿æãuser3 ã®ã¬ã³ã¼ãããã¯ãåå¾ãããã¨ãã¦ããã
å®è¡è¨ç»ãè¦ã
ã©ããã Tx2 ãæ³å®å¤ã®æåããã¦ããããªã®ã§ã次ã¯å®è¡è¨ç»ãè¦ã¦ã¿ã¾ãã
EXPLAIN UPDATE users SET lucky_color = 'blue' WHERE id IN (2, 4, 6, ...2000);
ããã¨ä»¥ä¸ã®ãããªçµæã«ãªãã¾ããã
Name |Value | -------------+-----------+ id |1 | select_type |UPDATE | table |users | partitions | | type |index | possible_keys|PRIMARY | key |PRIMARY | key_len |4 | ref | | rows |2000 | filtered |100.0 | Extra |Using where|
é常 IN å¥ã§ä¸»ãã¼ãè¤æ°æå®ããå ´åãtype 㯠range (æå®ç¯å²ã®ã¿æ¤ç´¢) ãæå¾ ãã¾ãããå®è¡è¨ç»ãè¦ã¦ã¿ã㨠type ã index (ãã«ã¤ã³ããã¯ã¹ã¹ãã£ã³) ã«ãªã£ã¦ãã¾ãã
IN å¥ã®è¦ç´ ã1,000åã¨å¤ãããã§ããªããã£ãã¤ã¶ããã«ã¹ãã£ã³ãé¸æãããã¼ãã«å ¨ä½ã®ã¬ã³ã¼ããèµ°æ» & ããã¯ãããã¨ãã¦ããå¯è½æ§ãããããã§ãã
IN å¥ã®è¦ç´ ãæ¸ããããã©ããªããã試ãã«è¦ç´ æ°ã500åã¾ã§æ¸ããã¦ç¢ºèªãã¦ã¿ã¾ãã
Name |Value | -------------+-----------+ id |1 | select_type |UPDATE | table |users | partitions | | type |range | possible_keys|PRIMARY | key |PRIMARY | key_len |4 | ref |const | rows |500 | filtered |100.0 | Extra |Using where|
ãã㨠type ã range ã«å¤ããã¾ããã
å®è¡è¨ç»ã®çµæãããIN å¥ã®è¦ç´ ãå¤ããããã«ã¹ãã£ã³ãé¸æãããTx2 ããã¼ãã«å ¨ä½ã®ã¬ã³ã¼ããããã¯ãããã¨ãããã¨ãã仮説ãç«ã¦ããã¾ãã
performance_schema ãè¦ã
念ã®çºã仮説ãæ£ãããæå¾ã«å®éã®æåã確èªãã¦ããã¾ãããã
Tx2 ã ããå度å®è¡ããããã¯ç¶æ³ã確èªãã¾ãã
ããã¯ç¶æ³ã¯ performance_schema.data_locks
ããåç
§ã§ãã¾ãã
SELECT ENGINE_TRANSACTION_ID, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE, LOCK_STATUS, LOCK_DATA FROM performance_schema.data_locks;
çµæã¯ä»¥ä¸ã®ããã«ãªãã¾ããã
ENGINE_TRANSACTION_ID|OBJECT_NAME|INDEX_NAME|LOCK_TYPE|LOCK_MODE|LOCK_STATUS|LOCK_DATA | ---------------------+-----------+----------+---------+---------+-----------+----------------------+ 1301542|users | |TABLE |IX |GRANTED | | 1301542|users |PRIMARY |RECORD |X |GRANTED |supremum pseudo-record| 1301542|users |PRIMARY |RECORD |X |GRANTED |1 | 1301542|users |PRIMARY |RECORD |X |GRANTED |2 | 1301542|users |PRIMARY |RECORD |X |GRANTED |3 | ... 1301542|users |PRIMARY |RECORD |X |GRANTED |2000 |
ãã¼ãã«å ¨ä½ã®ã¬ã³ã¼ããããã¯ãã¦ãããã¨ã確èªã§ããã®ã§ã仮説ã¯æ£ãããã§ãã
ã¾ã¨ããã¨ã以ä¸ã®æµãã§ãããããã¯ãçºçããã¨ãããã¨ãåããã¾ããã
- Tx1 ã user3 ã®ã¬ã³ã¼ããããã¯ããã
- Tx2 ã® IN å¥ãå¤ããããã«ã¹ãã£ã³ãé¸æããããã¼ãã«å ¨ä½ã®ã¬ã³ã¼ããããã¯ã試ã¿ãã
- Tx2 ã user1, 2 ã®ã¬ã³ã¼ããããã¯ããuser3 ã®ã¬ã³ã¼ããããã¯ãããã¨ããã
- Tx1 ã user1 ã®ã¬ã³ã¼ããããã¯ãããã¨ããã â ãããããã¯çºç
解決ç
IN å¥ã®è¦ç´ ãæ¸ãã
ä¸è¿°ã®éããIN å¥ã®è¦ç´ ãæ¸ãããã¨ã§ãã«ã¹ãã£ã³ãé¿ãããã¾ãã
ãã ãå ·ä½çã«ä½åã«ããã°è¯ãã®ãã¯ãã¼ãã«å®ç¾©ããã¼ã¿ã«ä¾åãããããå®éã«æ¤è¨¼ããªããæ¢ã£ã¦ããå¿ è¦ãããã¾ãã
ã¤ã³ããã¯ã¹ãæå®ãã
使ç¨ãã¦æ¬²ããã¤ã³ããã¯ã¹ãæå®ãããã¨ã解決çã®1ã¤ã§ãã
以ä¸ã®ããã« FORCE INDEX 㧠PRIMARY ãæå®ããã¨ãã«ã¹ãã£ã³ãé¿ãããã¾ãã
UPDATE users FORCE INDEX (PRIMARY) SET lucky_color = 'blue' WHERE id IN (2, 4, 6, ...2000);
ãã ããã«ã¹ãã£ã³ãé¸æãããã¨ãããã¨ã¯ããªããã£ãã¤ã¶ããã®æ¹ãå¹çãè¯ãã¨å¤æãã¦ãããããªã®ã§ãIN å¥ã®è¦ç´ ãæ¸ãããªã©ããç¨åº¦ãã¥ã¼ãã³ã°ããããã§ãã¤ã³ããã¯ã¹ãæå®ããã®ãè¯ãããã§ãã
対å¿æ¹é
å®éã«çºçããã±ã¼ã¹ã§ã¯ãä¸è¨2ã¤ãçµã¿åããã¦å¯¾å¿ããæ¹éã¨ãã¾ããã
ä¸ã§æãã以å¤ã«ã解決çã¯ããããã§ãã (ãã©ã³ã¶ã¯ã·ã§ã³åé¢ã¬ãã«ãä¸ãããªã©)ãä»åã¯èª¿æ»ãããã¦ããªãã®ã§å²æãã¾ãã
ããä»ã®ããæ¹ããåç¥ã§ããããæ¯éã³ã¡ã³ãããã ãã¾ãã¨å¹¸ãã§ãï¼
çµããã«
ä»åã¯å®éã«çºçãããããããã¯ã¨ããã®èª¿æ»éç¨ã«ã¤ãã¦ã¾ã¨ãã¾ãããã¾ã¨ãã¦ã¿ãã¨ã·ã³ãã«ã§ãããå®éã«ã¯ããã¥ã¡ã³ããè¦ã¤ã¤ãæå ã§è²ã 試ããªããåå ãæ¢ã£ã¦ããå°éãªä½æ¥ã§ãããç¥èä¸è¶³ãçæããã®ã§ãå¹´æ«å¹´å§ã« MySQL ã®æ¸ç±ã§ãè²·ã£ã¦åå¼·ããããã¨æãã¾ãã
ææ¥ã¯åãããã¯ã©ã¯ãã¸ãã¹ã«ã¼ãéçºãã¼ã Tech Lead ã® budougumi0617 ããã®è¨äºãåºãäºå®ã§ãï¼ã楽ãã¿ã«ï¼