MySQL Engine Lock

MySQL ์—”์ง„ ๋ ˆ๋ฒจ์˜ ๋ฝ์€ ๋ชจ๋“  ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„์— ์˜ํ–ฅ์„ ์ฃผ๊ฒŒ ๋˜๋Š” ๋ฝ์œผ๋กœ, ์•„๋ž˜์™€ ๊ฐ™์€ ์ข…๋ฅ˜๊ฐ€ ์žˆ๋‹ค.

๊ธ€๋กœ๋ฒŒ ๋ฝ(Global Lock)

  • ๋ฝ์˜ ๋ฒ”์œ„๊ฐ€ MySQL ์„œ๋ฒ„ ์ „์ฒด๋กœ(์ž‘์—… ๋Œ€์ƒ ํ…Œ์ด๋ธ”์ด๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์•„๋‹ˆ๋”๋ผ๋„ ํฌํ•จ), ์ œ๊ณตํ•˜๋Š” ์ž ๊ธˆ ์ค‘ ๊ฐ€์žฅ ๋ฒ”์œ„๊ฐ€ ํผ

  • ํ•œ ์„ธ์…˜์—์„œ ๊ธ€๋กœ๋ฒŒ ๋ฝ์„ ํš๋“ํ•˜๋ฉด ๋‹ค๋ฅธ ์„ธ์…˜์—์„œ SELECT๋ฅผ ์ œ์™ธํ•œ ๋Œ€๋ถ€๋ถ„์˜ DDL/DML ์ฟผ๋ฆฌ๊ฐ€ ๋ฝ์ด ํ•ด์ œ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ

  • ๋ชจ๋“  ํ…Œ์ด๋ธ”์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์น˜๊ธฐ ๋•Œ๋ฌธ์— ์›น ์„œ๋น„์Šค์—์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Œ

  • FLUSH TABLES WITH READ LOCK ๋ช…๋ น์œผ๋กœ ํš๋“ํ•˜๋ฉฐ, UNLOCK TABLES ๋ช…๋ น์œผ๋กœ ํ•ด์ œ

๋ฐฑ์—… ๋ฝ(Backup Lock)

InnoDB ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„์—์„œ๋Š” ํŠธ๋žœ์žญ์…˜์„ ์ง€์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ธ€๋กœ๋ฒŒ ๋ฝ์„ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ๊ฑฐ์˜ ์—†์–ด์กŒ์œผ๋ฉฐ, ์ข€ ๋” ๊ฐ€๋ฒผ์šด ๋ฝ์ธ ๋ฐฑ์—… ๋ฝ์„ ์‚ฌ์šฉํ•œ๋‹ค. ํŠน์ • ์„ธ์…˜์—์„œ ๋ฐฑ์—… ๋ฝ์„ ํš๋“ํ•˜๋ฉด ์•„๋ž˜์˜ ์ž‘์—…์ด ์ œํ•œ๋˜์ง€๋งŒ ์ผ๋ฐ˜์ ์ธ ํ…Œ์ด๋ธ”์˜ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์€ ํ—ˆ์šฉ๋œ๋‹ค.

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐ ํ…Œ์ด๋ธ” ๋“ฑ์˜ ๋ณ€๊ฒฝ ๋ฐ ์‚ญ์ œ

  • REPAIR TABLE / OPTIMIZE TABLE ๋ช…๋ น

  • ์‚ฌ์šฉ์ž ๊ด€๋ฆฌ ๋ฐ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ

MySQL ์„œ๋ฒ„๋ฅผ ์†Œ์Šค - ๋ ˆํ”Œ๋ฆฌ์นด๋กœ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒฝ์šฐ ์ฃผ๋กœ ๋ฐฑ์—… ์ž‘์—…์€ ๋ ˆํ”Œ๋ฆฌ์นด ์„œ๋ฒ„์—์„œ ์ด๋ฃจ์–ด ์ง€๋Š”๋ฐ, ์ด ๋•Œ ๋ ˆํ”Œ๋ฆฌ์นด ์„œ๋ฒ„์—์„œ ๋ฐฑ์—… ๋ฝ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฑ์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์žˆ๋‹ค.

ํ…Œ์ด๋ธ” ๋ฝ(Table Lock)

๊ฐœ๋ณ„ ํ…Œ์ด๋ธ” ๋‹จ์œ„๋กœ ์„ค์ •๋˜๋Š” ์ž ๊ธˆ์œผ๋กœ, ๋ช…์‹œ์  ๋˜๋Š” ๋ฌต์‹œ์ ์œผ๋กœ ํŠน์ • ํ…Œ์ด๋ธ”์˜ ๋ฝ์„ ํš๋“ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ๋ช…์‹œ์  ๋ฝ

    • LOCK TABLES tbl_name [READ | WRITE] ๋ช…๋ น์œผ๋กœ ํš๋“

    • UNLOCK TABLES ๋ช…๋ น์œผ๋กœ ํ•ด์ œ

    • ํŠน๋ณ„ํ•œ ์ƒํ™ฉ์ด ์•„๋‹ˆ๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‚ฌ์šฉํ•  ์ผ์ด ๊ฑฐ์˜ ์—†์Œ

    • ๋ช…์‹œ์ ์œผ๋กœ ํ…Œ์ด๋ธ”์„ ์ž ๊ทธ๋Š” ์ž‘์—…์€ ๊ธ€๋กœ๋ฒŒ ๋ฝ๊ณผ ๋™์ผํ•˜๊ฒŒ ๋‹ค๋ฅธ ์ž‘์—…์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์นจ

  • ๋ฌต์‹œ์  ๋ฝ

    • (MyISAM / MEMORY ํ•œ์ •)DML ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋ฐœ์ƒ

    • ํ•˜์ง€๋งŒ InnoDB ์—”์ง„์—์„œ๋Š” ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„ ์ฐจ์›์—์„œ ๋ ˆ์ฝ”๋“œ ๊ธฐ๋ฐ˜์˜ ์ž ๊ธˆ์„ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— DML ์ฟผ๋ฆฌ๋กœ ๋ฌต์‹œ์  ๋ฝ์ด ์„ค์ •๋˜์ง€ ์•Š์Œ

    • (๋ชจ๋“  ์—”์ง„ ํฌํ•จ)DDL ์ฟผ๋ฆฌ๋กœ ์Šคํ‚ค๋งˆ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒฝ์šฐ ๋ฌต์‹œ์  ๋ฝ์ด ์„ค์ •๋จ

๋„ค์ž„๋“œ ๋ฝ(Named Lock)

๋„ค์ž„๋“œ ๋ฝ์€ GET_LOCK() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ์ž„์˜์˜ ๋ฌธ์ž์—ด์— ๋Œ€ํ•ด ๋ฝ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ž ๊ธˆ์˜ ๋Œ€์ƒ์ด ํ…Œ์ด๋ธ”/๋ ˆ์ฝ”๋“œ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŠน์ • ๊ฐ์ฒด๊ฐ€ ์•„๋‹ˆ๋ผ ์ง€์ •ํ•œ ๋ฌธ์ž์—ด(String)์— ๋Œ€ํ•œ ์ž ๊ธˆ์ด๋‹ค.

-- lock_name ๋ฌธ์ž์—ด์„ 10์ดˆ ๋™์•ˆ ๋ฝ์„ ํš๋“
SELECT GET_LOCK('lock_name', 10);

-- lock_name ๋ฌธ์ž์—ด์— ๋Œ€ํ•ด ์ž ๊ธˆ ํ™•์ธ
SELECT IS_FREE_LOCK('lock_name');

-- lock_name ๋ฌธ์ž์—ด์— ๋Œ€ํ•ด ์ž ๊ธˆ ํ•ด์ œ
SELECT RELEASE_LOCK('lock_name');

ํŠธ๋žœ์žญ์…˜์ด ์ข…๋ฃŒ๋  ๋•Œ ์ž๋™์œผ๋กœ ํ•ด์ œ ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ์ž ๊ธˆ ์‹œ๊ฐ„์ด ์ง€๋‚˜๊ฑฐ๋‚˜ ๋ช…์‹œ์ ์œผ๋กœ ํ•ด์ œํ•ด์•ผ ํ•œ๋‹ค.

์‘์šฉ - ๋ถ„์‚ฐ ๋ฝ

๋„ค์ž„๋“œ ๋ฝ์€ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ช…์‹œ์ ์œผ๋กœ ๋ฝ์„ ์„ค์ •ํ•˜๊ณ  ํ•ด์ œํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ถ„์‚ฐ ๋ฝ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

// LockRepository.java
public interface LockRepository extends JpaRepository<Stock, Long> {

    @Query(value = "SELECT GET_LOCK(:key, 3000)", nativeQuery = true)
    void getLock(String key);

    @Query(value = "SELECT RELEASE_LOCK(:key)", nativeQuery = true)
    void releaseLock(String key);
}

// NamedLockService.java
public class NamedLockService {

    private LockRepository lockRepository;
    private StockService stockService;

    @Transactional
    public void decrease(Long id, Long quantity) {
        try {
            // 1. ๋ฝ ํš๋“, ์ด๋ฏธ ๋ฝ์ด ์„ค์ •๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋Œ€๊ธฐํ•˜๊ฒŒ ๋จ
            lockRepository.getLock(id.toString());
            // 2. ์žฌ๊ณ  ๊ฐ์†Œ ๋กœ์ง
            stockService.decreaseStock(id, quantity);
        } finally {
            // 3. ๋ฝ ํ•ด์ œ
            lockRepository.releaseLock(key);
        }
    }
}

๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ฝ(Metadata Lock)

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ์ฒด(ํ…Œ์ด๋ธ”์ด๋‚˜ ๋ทฐ ๋“ฑ)์˜ ์ด๋ฆ„์ด๋‚˜ ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒฝ์šฐ์— ํš๋“ํ•˜๋Š” ์ž ๊ธˆ์œผ๋กœ, ๋ช…์‹œ์ ์œผ๋กœ ํš๋“ํ•˜๊ฑฐ๋‚˜ ํ•ด์ œํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ํ…Œ์ด๋ธ”์˜ ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ ํš๋“๋œ๋‹ค.

-- ํ…Œ์ด๋ธ” ์ด๋ฆ„ ๋ณ€๊ฒฝ 1
RENAME TABLE rank TO rank_backup;
-- ํ…Œ์ด๋ธ” ์ด๋ฆ„ ๋ณ€๊ฒฝ 2
RENAME TABLE rank_new TO rank;

์œ„์™€ ๊ฐ™์ด ๋‘ ๊ฐœ์˜ ๋ช…๋ น๋ฌธ์œผ๋กœ ๋‚˜๋ˆ„์–ด ์‹คํ–‰ํ•˜๋ฉด, ๊ธฐ์กด rank ํ…Œ์ด๋ธ”์— ์ ‘๊ทผํ•˜๋ ค๋Š” ๋‹ค๋ฅธ ์„ธ์…˜์—์„œ Table Not Found ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

-- ํ…Œ์ด๋ธ” ์ด๋ฆ„ ๋ณ€๊ฒฝ
RENAME TABLE rank TO rank_backup, rank_new TO rank;

ํ•˜์ง€๋งŒ ์œ„์™€ ๊ฐ™์ด RENAME TABLE ๋ช…๋ น๋ฌธ์— ๋‘ ๊ฐœ์˜ ์ž‘์—…์„ ํ•œ ๋ฒˆ์— ์‹คํ–‰ํ•˜๋ฉด ๋‘ ํ…Œ์ด๋ธ”์— ๋Œ€ํ•ด ์ „๋ถ€ ๋ฝ์„ ํš๋“ํ•˜๊ฒŒ ๋˜์–ด ๋‹ค๋ฅธ ์„ธ์…˜์—์„œ Table Not Found ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

์‘์šฉ - ํ…Œ์ด๋ธ” ๊ตฌ์กฐ ๋ณ€๊ฒฝ

๋งŒ์•ฝ ์„œ๋น„์Šค ์œ ์ง€ ์ค‘ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผํ•˜๋Š” ์ƒํ™ฉ์ด๋ผ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ํ•  ์ˆ˜ ์žˆ๋‹ค.

  1. ์ƒˆ๋กœ์šด ๊ตฌ์กฐ์˜ ํ…Œ์ด๋ธ” ์ƒ์„ฑ

  2. ์ตœ๊ทผ(1์‹œ๊ฐ„ ์ง์ „ ๋˜๋Š” ํ•˜๋ฃจ ์ „)๊นŒ์ง€์˜ ๋ฐ์ดํ„ฐ๋ฅผ Primary Key์ธ id ๊ฐ’์„ ๋ฒ”์œ„๋ณ„๋กœ ๋‚˜๋ˆ ์„œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋กœ ๋ณต์‚ฌ

  3. ํŠธ๋žœ์žญ์…˜์„ auto commit์œผ๋กœ ์„ค์ •

  4. ์ž‘์—… ๋Œ€์ƒ ํ…Œ์ด๋ธ”์— ๋Œ€ํ•ด ํ…Œ์ด๋ธ” ๋ฝ์„ ์„ค์ •

  5. ๊ธฐ์กด ํ…Œ์ด๋ธ”์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒˆ๋กœ์šด ํ…Œ์ด๋ธ”๋กœ ๋ณต์‚ฌ

  6. ๋ชจ๋“  ๋ฐ์ดํ„ฐ ๋ณต์‚ฌ ์™„๋ฃŒ ํ›„ ํ…Œ์ด๋ธ” ์ด๋ฆ„ ๋ณ€๊ฒฝ

  7. ํ…Œ์ด๋ธ” ์“ฐ๊ธฐ ๋ฝ ํ•ด์ œ ๋ฐ ํ…Œ์ด๋ธ” ์‚ญ์ œ

-- 1. ์ƒˆ๋กœ์šด ๊ตฌ์กฐ์˜ ํ…Œ์ด๋ธ” ์ƒ์„ฑ
CREATE TABLE access_log_new
(
    id        BIGINT NOT NULL AUTO_INCREMENT,
    client_ip INT UNSIGNED,
--  ...
    PRIMARY KEY (id)
)

-- 2. ์ตœ๊ทผ(1์‹œ๊ฐ„ ์ง์ „ ๋˜๋Š” ํ•˜๋ฃจ ์ „) ๋ฐ์ดํ„ฐ๊นŒ์ง€๋Š” Primary Key์ธ id ๊ฐ’์„ ๋ฒ”์œ„๋ณ„๋กœ ๋‚˜๋ˆ ์„œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋กœ ๋ณต์‚ฌ
INSERT INTO access_log_new
SELECT *
FROM access_log_new
WHERE id BETWEEN 1 AND 1000000;

INSERT INTO access_log_new
SELECT *
FROM access_log_new
WHERE id BETWEEN 1000001 AND 2000000;

INSERT INTO access_log_new
SELECT *
FROM access_log_new
WHERE id BETWEEN 2000001 AND 3000000;
-- ...

-- 3. ํŠธ๋žœ์žญ์…˜์„ auto commit์œผ๋กœ ์„ค์ •
SET autocommit = 0;

-- 4. ์ž‘์—… ๋Œ€์ƒ ํ…Œ์ด๋ธ” 2๊ฐœ์— ๋Œ€ํ•ด ํ…Œ์ด๋ธ” ์“ฐ๊ธฐ ๋ฝ ์„ค์ •
LOCK TABLES access_log WRITE, access_log_new WRITE;

-- 5. ๊ธฐ์กด ํ…Œ์ด๋ธ”์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒˆ๋กœ์šด ํ…Œ์ด๋ธ”๋กœ ๋ณต์‚ฌ
SELECT MAX(id) as max_id
FROM access_log;
INSERT INTO access_log_new
SELECT *
FROM access_log
WHERE id > max_id;
COMMIT;

-- 6. ๋ชจ๋“  ๋ฐ์ดํ„ฐ ๋ณต์‚ฌ ์™„๋ฃŒ ํ›„ ํ…Œ์ด๋ธ” ์ด๋ฆ„ ๋ณ€๊ฒฝ
RENAME TABLE access_log TO access_log_old, access_log_new TO access_log;

-- 7. ํ…Œ์ด๋ธ” ์“ฐ๊ธฐ ๋ฝ ํ•ด์ œ ๋ฐ ํ…Œ์ด๋ธ” ์‚ญ์ œ
UNLOCK TABLES;
DROP TABLE access_log_old;

์ฐธ๊ณ ์ž๋ฃŒ

Last updated

Was this helpful?