μ΄ κΈμμλ MySQL 8.0 μμ λ΄λΆμ μΌλ‘ μΈμ λ½μ μ¬μ©νλμ§μ λν΄ μμκ°λ κ³Όμ μΌλ‘ MySQL μμ§ λ 벨μ λ½μ μμ§ νμ΅μ΄ λΆμ‘±νμ¬ ν΄λΉ κΈμμ μ μΈνκ² λμμ΅λλ€.
μΆκ°λ‘ Real MySQL μ μ½κ³ , 곡μ λ½κ³Ό λ°°ν λ½μ λ°°κ²½ μ§μμ΄ μλ λΆλ€μ΄ μ½κ² μ΄ν΄ λ κ² κ°μΌλ©°, κ²°λ‘ μ 'λ§μΉλ©°' μ μ 리ν΄λμμ΅λλ€.
λͺ©μ°¨λ λ€μ μμλ‘ μ§νλ©λλ€.
- ν νΈλμμ μμ 곡μ λ½/λ°°ν λ½ νλ μ, λ€λ₯Έ νΈλμμ μμ κΈ°λ€λ¦Ό μμ΄ μ€νν μ μλ 쿼리 μκ°
- 곡μ λ½/λ°°ν λ½ μ¬μ© μ, λ€λ₯Έ νΈλμμ μμ μ°κΈ° μμ μ΄ λκΈ°νλ μ΄μ
- UPDATE / DELETE 쿼리 μ WHERE μ μ μΈλ±μ€ μ¬λΆμ μ€μμ±
- MySQL8.0 Repetable Read μμ Phantom Read κ° λ°μνλ κ²½μ°
- UPDATE / DELETE 쿼리λ₯Ό μ€ν ν λ, MySQL μ΄ λ΄λΆμ μΌλ‘ λ°°ν λ½μ μ¬μ©νμ§λ§ select...for update(JPAμ λΉκ΄μ λ½)μ μ¬μ©νλ μ΄μ
- λ§μΉλ©°
1. ν νΈλμμ μμ 곡μ λ½/λ°°ν λ½ νλ μ, λ€λ₯Έ νΈλμμ μμ κ°λ₯ν 쿼리 μκ°
μ€ν νκ²½
- DBMS: Mysql 8.0 InnoDB
- 격리 μμ€: Repeatable Read
μ€νμ μ°μΌ ν μ΄λΈκ³Ό λ°μ΄ν°μ λλ€.
CREATE TABLE example (
id BININT,
name VARCHAR(255)
);
INSERT INTO example (id, name) VALUES (1, "a"); // 1λ² row
INSERT INTO example (id, name) VALUES (2, "b"); // 2λ² row
μ€ν 1 : λ°°νλ½ & 곡μ λ½
- μΌμͺ½ νΈλμμ
: 1λ² νμ λν΄ λ°°νλ½(Exclusive Lock)μ νλν©λλ€.
- select ... for update
- μ€λ₯Έμͺ½ νΈλμμ
: 1λ² νμ λν΄ κ³΅μ λ½(Shared Lock)μ μλν©λλ€.
- LOCK IN SHARE MODE (Mysql 5.7λ²μ μ΄ν)
- select ... for share ( Mysql 8.0λ²μ μ΄μ)
κ²°κ³Ό
- μ€λ₯Έμͺ½ νΈλμμ μ΄ κ³΅μ λ½μ νλνμ§ λͺ»νκ³ λκΈ° μνμ λ€μ΄κ°μ΅λλ€.
- λ°°νλ½μ΄ κ±Έλ¦° μνμμλ 곡μ λ½λ νλν μ μμΌλ―λ‘, λ°°νλ½ μμ λΉμ°ν νλν μ μμ΅λλ€.
μ€ν 2 : λ°°νλ½ & κΈ°λ³Έ SELECT
- μΌμͺ½ νΈλμμ : 1λ² νμ λν΄ λ°°νλ½μ νλν©λλ€.
- μ€λ₯Έμͺ½ νΈλμμ : 1λ² νμ λν΄ SELECT 쿼리λ₯Ό μ€νν©λλ€.
κ²°κ³Ό
- κΈ°λ³Έμ μΈ SELECT 쿼리λ μ±κ³΅μ μΌλ‘ μ€νλμμ΅λλ€.
- μ¦, λ°°νλ½μ΄ κ±Έλ¦° μνμμλ μ½κΈ° μμ (SELECT)μ νμ©λ©λλ€.
- λ°°νλ½μμ μ½κΈ°κ° νμ© μλλ€λ λ§μ Locking Read κ° μλλ κ²μ΄μ§ Non-Locking Read λ κ°λ₯ν©λλ€. Locking Read κ° κ³΅μ λ½, λ°°νλ½κ°μ΄ λ½μ μ¬μ©ν΄μ λ μ½λλ₯Ό μ§μ μ½λ κ²μ΄κ³ , Non-Locking Read λ μΌλ°μ μΈ select λ¬Έμ μλ―Ένλ©° μ€λ μ·μ μν μΈλ μμμ μ½λ κ²μ λλ€.
μ€ν 3 : λ°°νλ½ & UPDATE/DELETE
- μΌμͺ½ νΈλμμ : 1λ² νμ λν΄ λ°°νλ½μ νλν©λλ€.
- μ€λ₯Έμͺ½ νΈλμμ : 1λ² νμ λν΄ UPDATE λλ DELETE 쿼리λ₯Ό μ€νν©λλ€.
κ²°κ³Ό
- μ€λ₯Έμͺ½ νΈλμμ μ λκΈ° μνμ λ€μ΄κ°μ΅λλ€.
- λ°°νλ½μ΄ κ±Έλ¦° μνμμλ μ°κΈ° μμ (UPDATE, DELETE)μ΄ μ°¨λ¨λ©λλ€.
μ€ν 4 : 곡μ λ½ & SELECT/UPDATE/DELETE
- μΌμͺ½ νΈλμμ : 1λ² νμ λν΄ κ³΅μ λ½(Shared Lock)μ νλν©λλ€.
- μ€λ₯Έμͺ½ νΈλμμ : 1λ² νμ λν΄ SELECT, UPDATE, λλ DELETE 쿼리λ₯Ό μ€νν©λλ€.
κ²°κ³Ό
- SELECT 쿼리λ μ μ μ€νλμμ΅λλ€.
- νμ§λ§ UPDATEμ DELETE 쿼리λ λκΈ° μνμ λ€μ΄κ°μ΅λλ€.
μ€ν5 : 곡μ λ½ & INSERT
INSERT μμλ μ€νμ΄ μ½κ° λ€λ¦ λλ€.
μ€νμ μ¬μ©λ λ μ½λμ λλ€.
// example ν
μ΄λΈμ 3κ°μ λ μ½λ μ‘΄μ¬, id λ PK
mysql> select * from example;
+-----+------+
| id | name |
+-----+------+
| 1 | r |
| 2 | b |
| 500 | a |
+-----+------+
μ€ν 5-1. INSERT κ° λκΈ°νμ§ μμ λ μ λλ€.
1λ² νΈλμμ μμ idκ° 1λΆν° 2κΉμ§μΈ id μ 곡μ λ½μ νλνλ, id κ° 1κ³Ό 2μΈ λ μ½λμ λ μ½λ λ½μ΄ κ±Έλ €μμ΅λλ€.
// νΈλμμ
1μμ idκ° 1λΆν° 2κΉμ§ 곡μ λ½ νλ
mysql> select * from example where id between 1 and 2 for share;
+----+------+
| id | name |
+----+------+
| 1 | r |
| 2 | b |
+----+------+
// REC_NOT_GAPμ λ μ½λ μ체λ₯Ό μ κ·Έμ§λ§, κ°(Gap)μ μ κ·Έμ§ μλ μ κΈμ μλ―Έλ‘ ν΄λΉ λ μ½λλ§ λ³΄νΈνκ³ , μλ‘μ΄ λ μ½λ μ½μ
μ νμ©ν©λλ€.
// id κ° 1κ³Ό 2μΈ λ μ½λκ° κ³΅μ λ½μΈ λ μ½λ λ½μ νλνμ΅λλ€.
mysql> select * from performance_schema.data_locks;
+--------+------------------------------------------------------------------------+
| ENGINE | ... | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+--------+------------------------------------------------------------------------+
| INNODB | ... | NULL | TABLE | IS | GRANTED | NULL |
| INNODB | ... | PRIMARY | RECORD | S | GRANTED | 2 |
| INNODB | ... | PRIMARY | RECORD | S,REC_NOT_GAP | GRANTED | 1 |
+--------+------------------------------------------------------------------------+
2λ² νΈλμμ μμ idκ° 1κ³Ό 2λ₯Ό λ²μ΄λλ μμμ idκ° 10κ³Ό 501μΈ λ μ½λλ₯Ό INSERTνκ³ , μ±κ³΅νμ΅λλ€.
mysql> insert into example (id, name) values (10, "c");
Query OK, 1 row affected (0.00 sec)
mysql> insert into example (id, name) values (501, "c");
Query OK, 1 row affected (0.00 sec)
μ€ν 5-2. INSERT κ° λκΈ°ν λ μ λλ€.
νΈλμμ 1μμ id κ° 2 λΆν° 500 μ¬μ΄μΈ λ μ½λλ₯Ό μ‘°ννλ©° 곡μ λ½μ νλν©λλ€.
// νΈλμμ
1μμ idκ° 2λΆν° 500κΉμ§ 곡μ λ½ νλ
mysql> select * from example where id between 2 and 500 for share;
+----+------+
| id | name |
+----+------+
| 2 | b |
| 500 | a |
+----+------+
// LOCKμ΄ κ±Έλ¦° λ°μ΄ν°μ supremum pseudo-record κ° μΆκ°λμμ΅λλ€.
mysql> select * from performance_schema.data_locks;
+--------+-------------------------------------------------------------------------------------+
| ENGINE | ... | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+--------+-------------------------------------------------------------------------------------+
| INNODB | ... | NULL | TABLE | IS | GRANTED | NULL |
| INNODB | ... | PRIMARY | RECORD | S | GRANTED | supremum pseudo-record |
| INNODB | ... | PRIMARY | RECORD | S | GRANTED | 500 |
| INNODB | ... | PRIMARY | RECORD | S,REC_NOT_GAP | GRANTED | 2 |
+--------+-------------------------------------------------------------------------------------+
νΈλμμ 2μμ μ€ν 5-1κ³Ό λ¬λ¦¬ INSERT κ° μ€ν¨νμ΅λλ€. μ€ν 5-11μ²λΌ λ μ½λ λ½μ΄ λ²μ΄λ μμμΈ id=501 μΈ μμμ INSERT λ₯Ό μλνμ§λ§, λ½ νμμμμ΄ λ°μνμ΅λλ€.
μ€ν5-1μ²λΌ 곡μ λ½ μμ λ°μ INSERT λ₯Ό μλνμ§λ§, μ€ν 5-2 λ§ μ€ν¨ν μ΄μ κ° λ¬΄μμΌκΉμ??
mysql> insert into example (id, name) values (100, "z");
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into example (id, name) values (501, "z");
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
LOCK_DATE μ supremum pseudo-record κ° μκΈ° λλ¬Έμ λλ€. supremum pseudo-record λ μΈλ±μ€ νμ΄μ§μ λμ λνλ΄λ κ°μμ λ μ½λλ‘ μ¬μ€μ "κ° μ κΈ" μν μ νκΈ° λλ¬Έμ λλ€.
κ²°κ³Ό
- supremum pseudo-record κ°μ μ¬μ€μ ∞ μ κ°λ€ν©λλ€. κ·Έλμ, supremum μ¬λΆμ λ°λΌ INSERT λ¬Έμ΄ λ½μ μν΄ λκΈ°ν μλ, μλ μλ μκ² λ©λλ€.
μ€ν κ²°λ‘
κ²°κ³Ό | 곡μ λ½ | λ°°ν λ½ | κΈ°λ³Έ select | update / delete | insert |
곡μ λ½ | O | X | O | X | λ μ½λ λ½μΌ κ²½μ° O κ° λ½μ΄ κ±Έλ €μμ κ²½μ° X |
λ°°ν λ½ | X | X | O | X |
λ½ μ¬μ© μ, λ€μν μν©μ μμ΄μ μ΄λ€ μμ μ΄ νμ©λκ³ , λκΈ°νλμ§ μμλ΄€μ΅λλ€.
μ΄λ₯Ό ν΅ν΄ update / delete λ λ½μ μ¬μ©ν κ² κ°λ€λ μκ°μ΄ λ€λ©°, μ΄μ λν΄ μ°Ύμλ΄€μ΅λλ€.
* μ°Έκ³ - λκ΄μ λ½μ JPAμμ μ 곡νλ κΈ°λ₯μΌλ‘ DBμ 곡μ λ½κ³Ό 무κ΄ν©λλ€
2. 곡μ λ½/λ°°ν λ½ μ¬μ© μ, λ€λ₯Έ νΈλμμ μμ μ°κΈ° μμ μ΄ λκΈ°νλ μ΄μ
1. FOREIGN KEY μ μ½ μ‘°κ±΄μ΄ ν μ΄λΈμ μ μλμ΄ μλ κ²½μ°, μΈλν€ μλ 컬λΌμ INSERT / UPDATE / DELETE μμ μ μνν΄λ λλμ§ λΆλͺ¨ ν μ΄λΈ(PK) μ λ μ½λ μμ€μ 곡μ λ½(shared record-level locks)μ μ€μ ν©λλ€.
- If a FOREIGN KEY constraint is defined on a table, any insert, update, or delete that requires the constraint condition to be checked sets shared record-level locks on the records that it looks at to check the constraint. InnoDB also sets these locks in the case where the constraint fails.
2. μ λν¬ ν€λ INSERT μ μ€λ³΅λ κ°μ 체ν¬νκΈ° μν΄ μ½κΈ° μ κΈμ, λ μ½λλ₯Ό μΆκ°ν λλ μ°κΈ° μ κΈμ μ¬μ©νλ©° μ΄ κ³Όμ μμ λ°λλ½μ΄ μμ£Ό λΉλ²ν λ°μν©λλ€.(Real MySQL 8.0 μΆμ²)
3. UPDATE μ DELETE μνμμλ λ΄λΆμ μΌλ‘ λ½μ μ¬μ©ν©λλ€. μ΄ λ, μ¬μ©λλ λ½μ μ’ λ₯λ 쿼리λ₯Ό μ€ννκΈ° μν΄ μ¬μ©λλ μΈλ±μ€μ WHERE 쑰건μ λ°λΌ λ μ½λ λ½μΈμ§, κ° λ½μΈμ§ λ₯μ€νΈ ν€ λ½μΈμ§ λ€λ₯΄κ² μ§λ§ μ΄λ€μ κ²°κ΅ λ°°ν λ½μ μ¬μ©ν©λλ€. μΆκ°λ‘, UPDATE / DELETE μ WHERE 쑰건μ μ μΈλ±μ€κ° ν¬ν¨λ 컬λΌμ΄λΌλ©΄ λ μ½λ λ½μ, μΈλ±μ€κ° μλ 컬λΌμ μ§μ ν κ²½μ° ν΄λΉ 컬λΌμ΄ WHERE 쑰건μ λ§μ‘±ν λκΉμ§ μ½μ λͺ¨λ λ μ½λμ (InnoDBλ λ μ½λμ μΈλ±μ€μ) λ½μ κ±Έκ²λ©λλ€.
- For locking reads (SELECT with FOR UPDATE or FOR SHARE), UPDATE, and DELETE statements, the locks that are taken depend on whether the statement uses a unique index with a unique search condition or a range-type search condition.
- UPDATE ... WHERE ... sets an exclusive next-key lock on every record the search encounters. However, only an index record lock is required for statements that lock rows using a unique index to search for a unique row.
- μλ μ¬μ§μ νΈλμμ μ μ΄κ³ , update 쿼리λ₯Ό μ€ν ν λ½μ΄ κ±Έλ €μλ λ°μ΄ν°λ₯Ό 보λ λͺ λ Ήμ΄κΉμ§ μ€νν μ¬μ§μ λλ€. LOCK_STATUS κ° GRATED λ λ½μ νλν μνμμ μλ―Ένλ©°, LOCK_DATE λ λ μ½λμ ν΄μκ°μ΄λ©°, LOCK_MODE λ X λ‘ λ°°νλ½μ μλ―Έν©λλ€. κ·Έλ¦¬κ³ , LOCK_TYPE μ ν΅ν΄ λ½ μ νμ λ μ½λ λ½μμ νμΈνμ΅λλ€. μ¦, update 쿼리λ λ°°ν λ½μ΄ 걸립λλ€.
4. MySQL 8.0 μ InnoDB μ€ν λ¦¬μ§ μμ§μ λ μ½λκ° μμ²΄κ° μλλΌ μΈλ±μ€μ λ μ½λλ₯Ό μ κ·Έκ² λ©λλ€. μΈλ±μ€κ° νλλ μλ ν μ΄λΈμ΄λλΌλ λ΄λΆμ μΌλ‘ μλ μμ±λ ν΄λ¬μ€ν° μΈλ±μ€λ₯Ό μ΄μ©ν΄ λ μ½λλ₯Ό μ κΈλλ€.
μΆμ²
- https://dev.mysql.com/doc/refman/8.4/en/innodb-locks-set.html
- https://dev.mysql.com/doc/refman/8.0/en/create-table-foreign-keys.html#foreign-key-locking
- Real MySQL 1κΆμ νΈλμμ κ³Ό μ κΈ
μ¦, MySQL 8.0μμλ μΈλ ν€ μ μ½ μ‘°κ±΄κ³Ό UPDATE / DELETE μμλ λ½λ€μ΄ λ΄λΆμ μΌλ‘ μ¬μ©λ©λλ€. μ΄λ‘ μΈν΄ μ μ€νμμ ν νΈλμμ μ΄ λ°°ν λ½/곡μ λ½μ νλνμ λ, λ€λ₯Έ νΈλμμ μμ λ¨μ INSERT / UPDATE / DELETE μμ μ μννλλΌλ MySQL λ΄λΆμ μΌλ‘ μλμΌλ‘ μμ±λ ν΄λ¬μ€ν° μΈλ±μ€λ₯Ό μ΄μ©ν΄ λ μ½λ λ½μ νλνκ³ μ νκ³ , λ μ½λ λ½μ λ°°ν λ½μ΄κΈ° λλ¬Έμ λκΈ° μνμ λ€μ΄κ°λ κ²μ λλ€.
μ¦, MySQL 8.0 μμ MVCCλ κΈ°λ³Έ select 쿼리μ κ°μ΄ μ κΈ μλ μ½κΈ°λ₯Ό μ§μνκΈ° μν¨μ΄κ³ , 곡μ /λ°°ν λ½μ μ°κΈ°λ₯Ό μν¨μΌλ‘ μ 리ν μ μμ΅λλ€.
* INSERT / UPDATE / DELETE μμ μ νλ €λ λ μ½λμ λ²μκ° κ°κ°μ λ μ½λ μμ€μΈμ§, νΉμ λ²μμΈμ§, ν μ΄λΈ μμ€μΈμ§μ λ°λΌ μ¬μ©λλ MySQL λ΄λΆμμ μ¬μ©λλ λ½μ μ’ λ₯λ λ€λ¦ λλ€.
* μΈμ μ΄λ€ λ½μ΄ μ°μ΄λμ§ μμΈν μκ³ μΆλ€λ©΄ μΆμ²μ Locking λΆλΆμ΄λ Real MySQLμ Lock ννΈλ₯Ό μ°Έκ³ νμκΈΈ μΆμ²λ립λλ€.
3. UPDATE / DELETE 쿼리 μ WHERE μ μ μΈλ±μ€ μ¬λΆμ μ€μμ±
λν, MySQLμ UPDATEλ DELETE μμ μ λ΄λΆμ μΌλ‘ λ°°ν λ½μ μ¬μ©νκΈ° λλ¬Έμ WHERE μ‘°κ±΄μ΄ μ€μν©λλ€.
WHERE 쑰건μ μ¬μ©λλ 컬λΌμ μ μ ν μΈλ±μ€κ° μλ κ²½μ°, μ°κΈ° μμ μ ν λ μ½λλΏλ§ μλλΌ λ€λ₯Έ λ μ½λμλ λ½μ΄ 걸릴 μ μκ³ , μ΅μ μ κ²½μ° ν μ΄λΈ λ¨μλ‘ λ½μ΄ 걸릴 μ μμ΅λλ€. μ¦, UPDATE / DELETE 쿼리λ₯Ό μ²λ¦¬ ν λ, WHERE 쑰건μ PKμ κ°μ μΈλ±μ€κ° μλ 컬λΌμ μ¬μ©ν΄μΌ νλ€λ μλ―Έμ λλ€.
WHERE 쑰건μ μΈλ±μ€κ° μλ UPDATE / DELETE μΏΌλ¦¬κ° μ€νλλ©΄ ν μ΄λΈμ λͺ¨λ νμ μ€μΊνκ³ , λΆνμν λ μ½λμλ λ½μ΄ μ€μ λ μ μμ΅λλ€.
μλ₯Ό λ€μ΄, UPDATE / DELETE ... WHERE last_name = "μ’ ν" and first_name = "μ" μ΄λ μΏΌλ¦¬κ° μμ λ, last_name μ΄ "μ’ ν"μΈ λ μ½λκ° 1000건μ΄λΌκ³ κ°μ νκ³ , κ·Έ μ€μμ first_name μ΄ "μ" μ λ§μ‘±νλ λ μ½λκ° λ¨ 1κ°λΌκ³ κ°μ νκ² μ΅λλ€. μ΄ λ, last_name μλ§ μΈλ±μ€κ° μ€μ λμλ€λ©΄ last_name μ΄ "μ’ ν"μ λ§μ‘±νλ 1000건μ λ μ½λμ ν΄λΉνλ μΈλ±μ€μ λ½μ΄ κ±Έλ¦¬κ² λ©λλ€.(* MySQL 8.0μμ λ½μ λ μ½λκ° μλ μΈλ±μ€μ κ²λλ€.)
μ΄ λ, λ μ½λ λ½μ΄ κ±Έλ¦° 1000건μ λ μ½λ μ€, first_name = "μ" μ λ§μ‘±νλ 1건μ λ°μ΄ν°λ₯Ό μ μΈν 999건μ λΆνμνκ² λ½μ΄ κ±Έλ¦¬κ² λμμ΅λλ€. λ§μ½, last_name μλ μΈλ±μ€κ° μλ€λ©΄ last_name="μ’ ν" μ μ°ΎκΈ° μν΄ MySQL μ ν μ΄λΈ ν μ€μΊμ νκ² λ κ²μ΄κ³ , μ΄ κ³Όμ μμ ν μ΄λΈμ λͺ¨λ λ μ½λμλ λ½μ κ±Έκ² λ©λλ€.
μ΄λ¬ν μ΄μ λ‘ UPDATE / DELETE μ μΈλ±μ€κ° μλ 컬λΌμ WHERE 쑰건μ μ μ€μΌλ‘μ¨ λΆνμν λ μ½λμ λ½μ΄ 걸리λ κ²μ λ°©μ§νλ κ²μ΄ μ€μν©λλ€.
λ§μ½, μμμ²λΌ μΈλ±μ€κ° μλ 컬λΌμ WHERE 쑰건μ μ μ€μΌνλ€λ©΄ (last_name , first_name) λ 컬λΌμ λ©ν° μ»¬λΌ μΈλ±μ€λ₯Ό μμ±ν΄μ£Όλ©°, λ λμκ° first_name μ μΉ΄λλ리ν°κ° λ λκΈ° λλ¬Έμ (first_name , last_name) μΌλ‘ μΈλ±μ€λ₯Ό μμ±νλ©° λ½μ λ°©μ§ν μ μμ΅λλ€.
* μΆμ² : Real MySQL 5.3.2 μΈλ±μ€μ μ κΈ
4. MySQL 8.0 Repetable Read μμ λΉκ΄μ λ½ μ¬μ© μ, Phantom Read κ° λ°μνλ κ²½μ°
μΆκ°λ‘, Real MySQL 8.0 μμ select + select ... for update μ‘°ν©μΌλ‘ 쿼리 μ, Phantom Read κ° λ°μ ν μλ μλ€λ μ¬μ€ μκ³ κ³μ ¨λμ? 곡μ λ¬Έμμμλ λ₯μ€νΈ ν€ λ½μΌλ‘ Phantom Read λ¬Έμ λ₯Ό λ§μ μ μλ€κ³ λμ μμ΅λλ€.
νμ§λ§, select ν λΉκ΄μ λ½μΈ select ... for update 쿼리λ₯Ό μ§ννλ©΄, Phantom Read κ° λ°μν μ μμ΅λλ€.
μΏΌλ¦¬κ° [1]μΌμͺ½ νΈλμμ μμ select ... for update → [2]μ€λ₯Έμͺ½ νΈλμμ insert μμλ‘ μ§ν λ κ²½μ°
λ¨Όμ , μΌμͺ½ νΈλμμ μμ select ... for update νμ μ€λ₯Έμͺ½ νΈλμμ μμ insert λ₯Ό νλ©΄ μ€λ₯Έμͺ½ νΈλμμ μ λκΈ° μνκ° λλ©° 곡μλ¬Έμμμ λμ¨ κ²μ²λΌ λ₯μ€νΈ ν€ λ½μ μ¬μ©ν΄μ λ μ½λ μ½μ μ μν΄ λ°μνλ phatom read λ λ°μνμ§ μμ΅λλ€.
μΏΌλ¦¬κ° [1]μΌμͺ½ select → [2]μ€λ₯Έμͺ½ insert → [3]μ€λ₯Έμͺ½ commit → [4]μΌμͺ½ select ... for update μμμΌ κ²½μ°
μλ μ¬μ§μ²λΌ 1λ²λΆν° 4λ²κΉμ§ μ°¨λ‘λ‘ μμλλ‘ μμ±νλ μΌμͺ½ νΈλμμ μ΄ κ°μ νΈλμμ μμλ λΆκ΅¬νκ³ , phantom read κ° λ°μν κ²μ νμΈν μ μμ΅λλ€. μ΄λ select ... for update κ° μΈλ μμμ μ½μ§ μκ³ , κΈ°λ³Έ select μ λ¬λ¦¬ ν μ΄λΈ λ μ½λλ₯Ό λ°λ‘ μ½κΈ° λλ¬ΈμΈλ°μ. MVCCκΈ°λ²μ λ°λΌ κΈ°λ³Έ select μΏΌλ¦¬μΈ 1λ²μμλ μΈλμμμ μ½κ³ , λΉκ΄μ λ½μ΄ κ±Έλ¦° 4λ²μμλ μΈλ μμμ΄ μλ ν μ΄λΈ λ μ½λλ₯Ό μ½κ² λ©λλ€.
μ΄λ° 볡μ‘ν μν©μ΄ κ°μ ν μ΄λΈ(μν°ν°)μμ λ°μν μ μλ μΆκΈ°λ νμ§λ§, κ·Έλ λ€κ³ λ 100%λΌλ 보μ₯μ μμΌλκΉ μ΄λ»κ² ν΄μΌν μ§ μ°Έ μ΄λ ΅μ΅λλ€.
++ μΆκ°. μ΄λ¬ν κ²½μ°λ μ λ°μνμ§ μλλ€κ³ ν©λλ€.
μ 리νμλ©΄, MySQL 8.0 InnoDBλ MVCC λ₯Ό νμ©ν΄ REPEATABLE READ λ₯Ό κ°λ₯νκ² νκ³ , select...for update κ°μ΄ λ μ½λμ λ½μ κ±Έμ΄μΌ ν λλ(MVCCλ₯Ό νμ©νμ§ λͺ»ν λ λ) λ₯μ€νΈ ν€ λ½μ νμ©ν΄ Phantom Read λ₯Ό λ°©μ§νλ€. λ¨, ν νΈλμμ μμ select ν, select...for update μμλ Phantom Read κ° λ°μν μλ μλ€λ‘ μ 리ν μ μκ² μ΅λλ€.
5. UPDATE / DELETE 쿼리λ₯Ό μ€ν ν λ, MySQL μ΄ λ΄λΆμ μΌλ‘ λ°°ν λ½μ μ¬μ©νμ§λ§ select...for update(JPAμ λΉκ΄μ λ½)μ μ¬μ©ν΄ λμμ± μ μ΄λ₯Ό νλ μ΄μ
νλ DBMS λ MVCCλ₯Ό ν΅ν΄ μ κΈ μλ μ½κΈ°λ₯Ό μ§μν©λλ€. MySQL 8.0μ Repeatable Read κ° κΈ°λ³Έ 격리 μμ€μ΄λ©° MVCCλ₯Ό ν΅ν΄ νΈλμμ μ λ€λ₯Έ νΈλμμ μ κ²°κ³Όλ₯Ό λ°μνμ§ μκ³ μλλ°μ.
λ€λ₯Έ νΈλμμ μ κ²°κ³Όλ₯Ό λ°μνμ§ μλ€λ³΄λ λ νΈλμμ μμ ν λ μ½λμ UPDATE 쿼리λ₯Ό κ°κ° μ€ννλ©΄ νλμ νΈλμμ κ²°κ³Όλ§ λ μ½λμ λ°μνκ² λ©λλ€. μ¦, νλμ UPDATE κ²°κ³Όλ§ λ μ½λμ λ°μλκ³ λ€λ₯Έ νΈλμμ μ UPDATE κ²°κ³Όλ λ μ½λμ λ°μλμ§ μμ΅λλ€. κ°μ΄ λμ λμ΄μΌ νλλ°, λμ λμ§ μλ κ²μ΄μ£ . μ΄λ₯Ό Lost Update λ¬Έμ λΌ ν©μλ€.
κ·Έλμ, μ΄λ΄ κ²½μ°λ select ... for update λ₯Ό ν΅ν΄ UPDATE νλ €λ νΈλμμ λ€μ μμ°¨μ μΌλ‘ λ§λ€μ΄μ 2κ°μ λ³κ²½μ¬νμ λμ μν¬ μ μμ΅λλ€. select ... for update λ JPAμ λΉκ΄μ λ½μ ν΅ν΄ μ½κ² μ¬μ©ν μ μμ΅λλ€.
Lost Update λ¬Έμ μ λν μμλ₯Ό λμ λμ΄μΌ νλ λμΌλ‘ κ°μ Έμμ΅λλ€.
- update λ¬Έλ§ μ¬μ©ν΄μ Lost Update λ¬Έμ λ°μνλ κ²½μ°
id=1 μΈ λ μ½λμ money 컬λΌμ κ°(1000)μ μΌμͺ½ νΈλμμ μμλ 500μ λΉΌκ³ , μ€λ₯Έμͺ½ νΈλμμ μλ 100μ λΉΌκ³ , λ νΈλμμ μ 컀λ°νμ΅λλ€. κ·Έλ¬λ, μΌμͺ½ νΈλμμ μ Query OK, 1 row affected (4.28 sec) μ ν΅ν΄ update μ λ°°ν λ½μ΄ κ±Έλ € λκΈ°νμ§λ§ κ²°κ³Όλ id=1μΈ λ μ½λμ money 컬λΌμ κ°μ 500 μΌλ‘, μ€λ₯Έμͺ½ νΈλμμ μ κ²°κ³Όκ° λ μ½λμ λ°μλμ§ μμμ΅λλ€.
- select ... for update μ¬μ©ν΄μ Lost Update λ¬Έμ λ°μνμ§ μλ κ²½μ°
λ°°ν λ½μ μ¬μ©νλ id=1 μΈ νμ money 컬λΌμ κ°(1000)μ μ€λ₯Έμͺ½ νΈλμμ μμλ 100μ λΉΌμλ§μ, μΌμͺ½ νΈλμμ μλ id=1 μΈ νμ money 컬λΌμ κ°μ μ‘°ννλ 1000μμ 100μ λΊ κ°μΈ 900μ΄ λμ€κ² λμμ΅λλ€.
6. λ§μΉλ©°
곡μ λ½/λ°°ν λ½/MVCC λ₯Ό 곡λΆνκ³ , Real MySQLμ 곡λΆνλ©΄μ λ½μ μ¬μ©νλ©΄ μ΄λ€ 쿼리 μμ κΉμ§ μν₯μ λ―ΈμΉλμ§ νμ ν·κ°λ Έκ³ , λ°°ν λ½κ³Ό MySQL μμ μ§μνλ λ μ½λ λ½ / κ° λ½ / λ₯μ€νΈ ν€ λ½μ μ°¨μ΄κ° 무μμΈμ§, update μλ λ°°ν λ½μ΄ 걸리λλ° select ... for update λ μ νμνμ§? μ§μ μ€ννκ³ μ 리ν΄λ³΄μμ΅λλ€.
μ΄λ² μ€νμ ν΅ν΄ UPDATE / DELETE μμ λ λ°°ν λ½μ μ¬μ©νκ³ , μΈλν€/μ λν¬ ν€ μ μ½ μ‘°κ±΄λ€μ μ΄λ»κ² λ°μ΄ν°μ 무결μ±μ 보μ₯νλμ§ κΆκΈνλλ° μ΄λ² κΈ°νμ μ 리ν μ μμμ΅λλ€.
- MySQL λ΄λΆμ μΌλ‘ λ°μ΄ν°μ 무결μ±(μΈλν€, μ λν¬ ν€)μ μ§ν€κΈ° μν΄ λ½μ μ¬μ©ν©λλ€.
- MySQL InnoDBλ UPDATE / DELETE 쿼리 μ μΈλ±μ€κ° μλ 컬λΌμ WHERE μ μ λ£μΌλ©΄ 쑰건μ λ§λ λ μ½λλ₯Ό UPDATE / DELETE νκΈ° μν΄ λ§μ λ μ½λλ₯Ό μ½κ² λλ©°, μ½μ λ μ½λμλ λ°°ν λ½μ κ²λλ€.
- λΉκ΄μ λ½μ λ°°ν λ½μ μ¬μ©νλ©° μ½κΈ° λ½κ³Ό λ°°ν λ½κ³Όμ 곡μ κ° λΆκ°ν©λλ€.
- MySQL 8.0 InnoDBμ κΈ°λ³Έ 격리 μμ€μμ select...for update μ, λ°μνλ Phantom Read λ₯Ό λ§κΈ° μν΄ λ°°ν λ½μΈ λ₯μ€νΈ ν€ λ½(κ° λ½ + λ μ½λ λ½)μ μ¬μ©ν©λλ€.