MySQL MMM 구성에서 유동적으로 writer 및 마스터 역할을 변경할 수 있도록 writer에 구성하는 Virtual IP입니다.
즉, MMM에서 롤 체인지를 할 때 이동하게 되는 VIP인데요.
🤔 그렇다면 VIP는 어떻게 상황을 판단하고 이동하는 걸까? 라는 궁금증이 생겨 찾아보니
VIP를 누가 소유하는지 판별하기 위해서 VRRP라는 프로토콜을 사용한다는 것을 알게 되어 정리해보고자 합니다.
네트워크 이중화 작업을 하는 이유는 크게 두 가지입니다.
1. Load balancing: 똑같은 기능을 수행하는 장비를 여러 개 구성하여 네트워크 부하를 분산
2. Failover: 하나의 장비가 죽었을 때 다른 장비로 전환되어 서비스 단절 최소화
VRRP는 주로 Failover를 목적으로 Master/Slave 장비 간 전환을 위해 사용됩니다.
그럼 VRRP는 어떤 프로토콜이고 어떻게 동작할까요?
VRRP(Virtual Router Redundancy Protocol)
가상 라우터 다중화 프로토콜, 가상 라우터 장애 복구 프로토콜
게이트웨이 이중화 프로토콜(FHRP, First Hop Redundancy Protocol) 중 하나로 게이트웨이 장애 복구를 위한 프로토콜
FHRP에는 VRRP, HSRP(Cisco), GLBP 등이 있으며 VRRP와 HSRP는 동작방식이 거의 유사
Master 선출 기준
이중화에 사용되는 게이트웨이 역할을 하는 두개의 장비는 Master/Slave 장비로 동작합니다.
Master와 Slave는 하나의 VIP(Virtual IP)로 묶여있으며, 각각 RIP(Real IP)를 가지고 있습니다.
VIP를 누가 소유하고 있느냐에 따라 Master(O)/Slave(X)를 구분할 수 있으며
Master의 선출 기준은 다음과 같습니다.
VIP와 RIP가 같은 장비
VRRP Priority(우선순위) 값이 큰 장비(default: 100)
RIP의 주소가 큰 장비
Master가 살아있는지 어떻게 알까?
Master 장비가 살아있는지 죽었는지 VRRP에서 확인하는 방법은 헬스 체크와 같이 반복적으로 패킷을 보내는 것입니다.
Master 장비는 VRRP Advertisement Packet을 가상 그룹에 반복적으로 전송(advertisement-interval 주기 보통 2초)
VRRP는 Singel Virtual Router 방식으로 물리 인터페이스에 설정하게 되어 가상 그룹을 하나만 만들 수 있다. (VLAN 설정 시 여러 개의 그룹 생성 가능 -> 더 알아볼 내용)
가상 그룹에 속한 Slave 장비는 해당 패킷을 받는 중에는 Master 장비가 살아있다고 판단(Standby 상태를 유지)
패킷이 수신되지 않으면 일정 시간(Master Down Timer = Dead Timer) 후에 Master가 죽었다고 판단
Slave에서는 자신이 Master가 되었음을 알리기 위해 VRRP Advertisement Packet과 GARP Packet을 전송
GARP는 자신의 IP에게 ARP 요청을 보내는 것으로 동일 서브넷 상에 존재하는 호스트/라우터의 ARP table을 갱신할 수 있음 GARP를 수신한 노드들은 GARP를 송신한 IP와 MAC 주소를 테이블에 저장하게 됨 여기서는 Slave와 연결된 스위치나 라우터로 GARP 패킷을 통해 ARP table를 갱신하여 Slave 장비가 VIP를 소유하게 되었음을 알릴 수 있음
정리
여러 대(최소 2대 이상)의 Router를 하나의 group으로 묶어서, Client가 바라보는 Gateway에 대한 IP Address를 공유하는 것
Priority가 높은 Router가 동작하다가 Router에 문제가 발생하면 그 다음 Priority를 가진 Router가 Active role를 가지고 Gateway IP Address를 넘겨 받아 동작
Client 입장에서는 Router의 장애와 관계 없이 Gateway의 IP Address를 변경하지 않고 Data를 전달
pt-query-digest slow query가 많을 경우, 쿼리를 parsing하고 결과를 summary하여 보여줌 쿼리의 유형, 빈도, 비중 등 확인 pt-summary 하드웨어 정보 요약, DB서버의 하드웨어 스펙를 뽑아서 저장할 때 좋음 디스크, 메모리 등 pt-online-schema-change online alter를 할 수 있게 해주는 툴
InnoDB 테이블은 레코드 기반의 잠금을 제공하기 때문에 단순 DML 쿼리로 인해서는 테이블 락이 설정되지 않습니다.
네임드 락
임의의 문자열에 대해 잠금을 설정하며 유저 레벨 락으로도 불립니다.
🤔 문자열에 잠금을 건다는 건 어떤 의미일까?
단순히 사용자가 지정한 문자열 자체에 의미를 두어 같은 상황을 만들지 않는 용도입니다. 예를 들면 특정 유저가 동일 작업을 하는 것을 방지하기 위해 트랜잭션 시 GET_LOCK("user_name")으로 락을 걸어 동일한 user의 해당 작업을 잠금시켜 버릴 수 있습니다.
-- "mylock"이라는 문자열에 대해 잠금을 획득한다. 이미 잠금을 사용 중이면 2초 동안만 대기한다.
select get_lock('mylock', 2);
-- "mylock"이라는 문자열에 대해 잠금이 설정돼 있는지 확인한다.
select is_free_lock('mylock');
-- "mylock"이라는 문자열에 대해 획득했던 잠금을 반납(해제)한다.
select release_lock('mylock');
-- 3개 함수 모두 정상적으로 락을 획득하거나 해제한 경우에는 1을, 아니면 NULL이나 0을 반환한다.
8.0 부터 네임드 락을 중첩해서 사용할 수 있고, 동시에 모두 해제하는 기능도 추가되었습니다.
바이너리 로그에 기록되는 쿼리가 레플리카 서버에서 실행될 때 소스 서버에서 만들어 낸 결과와 동일한 결과를 만들어내도록 보장하는 것
하지만 의외로 이로 인해 데드락이나 발생하거나 다른 트랜잭션을 기다리게 만드는 일이 자주 발생합니다.
→ 가능하다면 로그 포맷을 ROW 형태로 바꿔서 넥스트 키 락이나 갭 락을 줄이는 것이 좋습니다. → 8.0에서는 ROW 형태의 바이너리 로그가 디폴트가 되었습니다.
💡 바이너리 로그 포맷
STATEMENT
가장 오래된 포맷으로 데이터 변경에 사용되는 모든 쿼리를 쿼리대로 저장하는 방식 (5.7 기본)
한번에 많은 변경의 경우 효과적 (WHERE절로 수백개 데이터 UPDATE)
ROW
변경 작업으로 변경된 모든 ROW의 정보를 기록하는 방식 (8.0 기본)
작은 변경이 여러번 일어나는 경우 효과적
MIXED
두 방식을 혼합
자동 증가 락
🤔 AUTO_INCREMENT 가 사용된 테이블에 동시에 여러 레코드가 INSERT 된다면?
중복되지 않고 저장된 순서대로 증가하는 일련번호 값을 가져야 합니다.
이를 위해 내부적으로 자동 증가 락이라는 테이블 수준의 잠금을 사용합니다.
트랜잭션과 관계없이 INSERT, REPLACE 문에서 AUTO_INCREMENT 값을 가져오는 순간만 락이 걸렸다가 즉시 해제됩니다.
UPDATE, DELETE 문에서는 걸리지 않습니다.
명시적으로 획득하고 해제하는 방법은 없지만
아주 짧은 시간 동안 걸렸다가 해제되는 잠금이라서 대부분의 경우 문제가 되지 않습니다.
AUTO_INCREMENT 컬럼에 명시적으로 값을 설정하더라도 자동 증가 락을 걸게 됩니다.
잠금을 최소화하기 위해 한 번 증가하면 절대 자동으로 줄어들지 않습니다.
🤔 두 개의 INSERT 쿼리가 동시에 실행된다면?
자동 증가 락은 테이블에 단 하나만 존재하기 때문에 하나의 쿼리가 락을 걸면 나머지 쿼리는 락을 기다려야 합니다.
innodb_autoinc_lock_mode 변수로 작동 방식을 설정할 수 있습니다. (5.1 이상)
0 값 : 모든 INSERT문에 사용합니다.
1 값 : 서버가 레코드 건수 예측 가능 시 자동 증가 락을 사용하지 않고, 훨씬 가볍고 빠른 래치(뮤텍스)를 이용해 처리합니다.
2 값 : 무조건 래치를 이용해 처리합니다.
인덱스와 잠금
레코드 락에서 나오는 인덱스 잠금에 대해 알아봅니다.
인덱스를 잠그는 방식이란?
변경할 레코드를 찾을 때 검색한 인덱스의 레코드를 모두 락을 걸어야 하는 것
인덱스 설계가 중요한 이유?
인덱스가 하나도 없다면 테이블을 풀 스캔하면서 모든 레코드를 잠궈야 하는 일이 발생합니다.
예시) 🤔 아래 쿼리를 실행하면 1개의 UPDATE를 위해 몇 개의 레코드에 락을 걸어야 할까요?
-- 멤버로 담긴 ix_firstname이라는 인덱스가 준비돼 있다는 가정으로 진행
mysql> SELECT COUNT(*) FROM employees WHERE first_name='Georgi';
+-----------+
| 253 |
+-----------+
mysql> SELECT COUNT(*) FROM employees WHERE first_name='Georgi' AND last_name='Klassen';
+-----------+
| 1 |
+-----------+
mysql> UPDATE employees SET hire_date=NOW() WHERE first_name='Georgi' AND last_name='Klassen';
first_name에는 인덱스가 존재하지만 last_name에는 인덱스가 없기 때문에 레코드 253건이 모두 잠깁니다.
🤔 만약 인덱스가 아예 존재하지 않는다면?
풀 스캔이 일어나면서 1개의 UPDATE를 위해 모든 레코드가 잠기게 됩니다. 그래서 UPDATE와 같이 잠금이 걸리는 작업 같은 경우, 잠금의 범위를 최소화 하도록 인덱스 설계를 잘 하는 것이 중요합니다.
레코드 수준의 잠금 확인 및 해제
레코드 수준의 잠금은 테이블 수준 잠금보다 조금 더 복잡하고 문제의 원인을 발견하고 해결하기도 어렵습니다.
🤔 버전별로 레코드 잠금과 잠금을 대기하는 클라이언트의 정보를 확인하고 싶다면?
5.1 ~ : information_schema의 INNODB_TRX, INNODB_LOCKS, INNODB_LOCK_WAITS 테이블을 통해 확인
8.0 ~ : information_schema 정보는 조금씩 deprecated 됨 performance_schema의 data_locks와 data_lock_waits 테이블로 대체되는 중입니다.
🤔 특정 스레드가 어떤 잠금을 가지고 있는지 상세히 확인하고 싶다면?
performance_schema의 data_locks 테이블이 가진 컬럼을 모두 살펴보면 됩니다.
mysql> SELECT * FROM performance_schema.data_locks\G
🤔 강제로 잠금을 해제하려면?
KILL 명령을 이용해 특정 스레드를 강제 종료합니다.
-- KILL {특정 스레드번호}
mysql> KILL 17
4. MySQL의 격리 수준
🤔 트랜잭션의 격리 수준이란?
여러 트랜잭션이 동시에 처리될 때 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있게 할지 못 보게 할지를 결정하는 것
격리 수준
아래로 갈수록 고립성이 높아지며, 동시 처리 성능이 떨어집니다.
DIRTY READ
NON-REPEATABLE READ
PHANTOM READ
비고
READ UNCOMMITTED
발생
발생
발생
거의 사용하지 않음
READ COMMTTIED
없음
발생
발생
오라클, PostgreSQL (기본)
REPEATABLE READ
없음
없음
발생(InnoDB는 없음)
InnoDB (기본)
SERIALIZABLE
없음
없음
없음
거의 사용하지 않음
AUTOCOMMIT
트랜잭션 격리 수준을 테스트 하기 위해서는 AUTOCOMMIT을 OFF로 해두어야 합니다.
MySQL에서는 default로 enabled 되어 있습니다.
-- 활성화 여부 조회
mysql> SELECT @@AUTOCOMMIT;
-- OFF로 변경
mysql> SET autocommit=OFF;
1. READ UNCOMMITED
트랜잭션에서 변경 내용이 아직 COMMIT 되지 않은 상태에서도 조회할 수 있습니다.
Dirty Read 문제 발생
어떤 트랜잭션의 작업이 완료되지 않았는데도, 다른 트랜잭션에서 볼 수 있는 데이터 불일치 현상
Dirty Read 문제 때문에 실제 DB에 있는 데이터와 결과가 다른 문제가 발생할 수 있습니다. 예를 들어 사용자 A가 트랜잭션을 끝마치지 못해 롤백을 하더라도 사용자 B는 무효가 된 데이터 값(Lara)를 읽고 처리를 하게 될 것입니다. RDBMS 표준에서 트랜잭션의 격리수준으로 인정하지 않을 정도로 정합성에 많은 문제가 있습니다.
2. READ COMMITED
오라클, PostgreSQL에서 기본으로 사용되는 격리수준
온라인 서비스에서 가장 많이 사용되는 격리수준
트랜잭션에서 변경 내용이 COMMIT 된 상태에서만 조회할 수 있습니다. 트랜잭션이 이루어지는 동안 다른 사용자는 해당 데이터에 접근이 불가능합니다.
READ COMMITED 동작 방식
사용자 A가 트랜잭션을 시작하여 데이터를 변경하였고, 아직 커밋은 하지 않은 상태입니다. 그러면 테이블은 먼저 갱신되고, 언두 로그로 변경 전의 데이터가 백업됩니다.
이때 사용자 B가 데이터를 조회하려고 하면, READ COMMITTED에서는 커밋된 데이터만 조회할 수 있으므로, 언두 로그에서 변경 전의 데이터를 찾아서 반환하게 됩니다.
최종적으로 사용자 A가 트랜잭션을 커밋하면 그때부터 다른 트랜잭션에서도 새롭게 변경된 값을 참조할 수 있게 됩니다.
NON-REPLEATABLE READ 문제 발생
하나의 트랜잭션 안에서 똑같은 SELECT 쿼리로 데이터를 여러번 요청했을 때, 항상 같은 결과를 가져오지 못하는 데이터 불일치 현상
중간에 다른 트랜잭션에서 커밋한 데이터 때문에 한 트랜잭션 안에서 같은 SELECT문의 실행 결과가 달라지는 문제가 발생합니다.
일반적으로는 크게 문제가 되지 않을 수 있지만, 금전적인 내용을 다루는 경우 주의해야 합니다.
오늘 입금된 금액의 총합을 조회하는 상황에서 NON-REPEATABLE READ가 발생할 경우, 총합을 계산하는 SELECT 쿼리가 실행될 때 마다 다른 결과를 가져올 수 있기 때문입니다.
3. REPEATABLE READ
InnoDB에서 기본으로 사용되는 격리 수준
MVCC를 이용해 한 트랜잭션 내에서는 동일한 결과를 보여줄 수 있게 보장합니다.
단, 새로운 레코드가 추가되는 경우에는 데이터 불일치가 생길 수 있습니다.
보장하는 방법
각각의 트랜잭션은 순차 증가하는 고유한 트랜잭션 번호가 존재합니다.
주기적으로 언두 영역의 데이터를 삭제할 때 MVCC를 보장하기 위해 실행 중인 트랜잭션보다 이전 트랜잭션 번호를 가진 언두 영역의 데이터는 삭제하지 않습니다.
그러다보니 트랜잭션이 제때 종료되지 않으면 언두 영역이 무한정 커지게 되어서 성능 이슈가 생길 수 있습니다.
바이너리 로그를 가진 MySQL 서버에서는 최소 이 격리 수준 이상을 사용해야 합니다.
REPEATABLE READ 동작 방식
사용자 A가 emp_no가 500000인 사원의 이름을 UPDATE 하고 사용자 B가 emp_no가 500000인 사원을 SELECT 하는 과정
그리고 이때 다른 사용자 A의 트랜잭션에서 first_name이 Toto인 레코드를 갱신하는 상황입니다. 그러면 MVCC를 통해 기존 데이터는 변경되지만, 백업된 데이터가 언두 로그에 남게 됩니다.
이전에 사용자 B가 데이터를 조회했던 트랜잭션은 아직 종료되지 않은 상황에서, 사용자 B가 동일한 SELECT문을 실행하면 어떻게 될까요?
사용자 B의 트랜잭션(10)은 사용자 A의 트랜잭션(12)이 시작하기 전에 이미 시작된 상태입니다. 이 때 REPEATABLE READ는 트랜잭션 번호를 참고해 자신보다 먼저 실행된 트랜잭션의 데이터만을 조회합니다. 만약 테이블에 자신보다 이후에 실행된 트랜잭션의 데이터가 존재한다면? 언두 로그를 참고해서 데이터를 조회합니다.
따라서 사용자 A의 트랜잭션이 시작되고 커밋까지 되었지만 해당 트랜잭션(12)는 현재 트랜잭션(10)보다 나중에 실행되었기 때문에 조회 시 기존과 동일한 데이터를 얻게 됩니다.
즉, REPEATABLE READ는 어떤 트랜잭션이 읽은 데이터를 다른 트랜잭션이 수정하더라도 동일한 결과를 반환할 것을 보장해줍니다.
READ COMMITED vs. REPEATABLE READ
🤔 두 격리 수준 모두 언두 영역에 데이터를 백업해두는데 어떤 차이가 있을까요? READ COMMITED도 언두 영역에서 데이터를 읽어오지만, REPEATABLE READ는 이보다 훨씬 전 버전의 데이터를 읽을 수 있습니다.
PHANTOM READ 문제 발생
트랜잭션 시작 시점 데이터를 읽었을 때 존재하지 않았던 데이터가 다시 같은 조건으로 데이터를 읽어 들였을 때 유령처럼 나타나는 현상
REAPETABLE READ는 새로운 레코드의 추가까지는 막지 않습니다. 따라서 SELECT로 조회한 경우 트랜잭션이 끝나기 전에 다른 트랜잭션에 의해 추가된 레코드가 발견될 수 있는데, 이를 Phantom Read 라고 합니다.
하지만 MVCC 덕분에 일반적인 조회에서는 Phantom Read가 발생하지 않습니다. 왜냐하면 자신보다 나중에 실행된 트랜잭션이 추가한 레코드는 무시하면 되기 때문입니다.
🤔 그렇다면 언제 Phantom Read가 발생하는 것일까요? 바로 잠금이 사용되는 경우입니다.
사용자 A가 employees 테이블에 INSERT를 실행하는 도중에 사용자 B가 SELECT FOR UPDATE 쿼리로 employees 테이블을 조회하는 상황
MySQL은 갭 락으로 인해 다른 RDBMS와 동작이 다른 부분이 있어 일반적인 RDBMS 경우부터 살펴보겠습니다.
일반적인 RDBMS
사용자 B가 먼저 데이터를 조회하는데, SELECT FOR UPDATE 쿼리를 이용해 쓰기 잠금을 걸었습니다.
SELECT ... FOR UPDATE : 배타 락(쓰기 잠금)을 거는 것 LOCK IN SHARE MODE : 공유 락(읽기 잠금)을 거는 것
그리고 사용자 A가 새로운 데이터를 삽입하는 상황이라고 가정하겠습니다. 일반적인 DBMS는 갭 락이 없기 때문에 emp_no=500000인 레코드만 잠금이 걸린 상태이고, 사용자 A의 요청은 잠금 없이 즉시 실행됩니다.
이 때 사용자 B가 동일한 쓰기 잠금 쿼리로 다시 한번 데이터를 조회하면 이번에는 2건의 데이터가 조회됩니다. 동일한 트랜잭션 내에서도 새로운 레코드가 추가되는 경우에 조회 결과가 달라지는데 Phantom Read가 발생한 것입니다. 이는 다른 트랜잭션에서 새로운 레코드를 추가하거나 삭제하는 경우 발생할 수 있습니다.
이 경우에도 MVCC를 통해 해결될 것 같지만, 두 번째 실행되는 SELECT FOR UPDATE 때문에 그럴 수 없습니다. MVCC에서는 데이터를 먼저 테이블에 반영하고, 언두 로그에 백업합니다.
즉, SELECT FOR UPDATE로 잠금을 걸어도 테이블에는 반영이 되고, 언두 로그에는 다른 트랜잭션에 의한 데이터가 계속해서 쌓이는 것입니다. 만약 먼저 시작된 트랜잭션이 존재하여 작업을 하면 테이블에는 반영되고, 언두 로그에는 이전 트랜잭션의 데이터가 쌓이게 됩니다. 그러므로 MVCC 만으로 정확한 데이터 제공이 불가능하고, 언두 로그에도 잠금을 걸어야 하는데, 언두 로그는 append only 형태이므로 잠글 수가 없습니다.
따라서 SELECT FOR UPDATE나 LOCK IN SHARE MODE로 레코드를 조회하는 경우 언두 영역의 데이터가 아니라 현재 테이블의 레코드를 가져오게 되고, 이로 인해 Phantom Read가 발생하는 것입니다.
하지만 MySQL에는 갭 락이 존재하기 때문에 위의 상황에서 문제가 발생하지 않습니다.
MySQL
사용자 B가 SELECT FOR UPDATE로 데이터를 조회합니다. MySQL은 emp_no=500000인 레코드에는 레코드 락, emp_no > 500000인 범위에는 갭 락으로 넥스트 키 락을 겁니다. 따라서 사용자 A가 emp_no=500001 인 멤버 삽입 시, 사용자 B의 트랜잭션이 종료될 때까지 대기하다가 대기가 너무 길어지면 락 타임아웃이 발생하게 됩니다.
따라서 일반적으로 MySQL의 REAPEATABLE READ에서는 Phantom Read가 발생하지 않습니다.
MySQL에서 Phantom Read가 발생하는 거의 유일한 케이스는 다음과 같습니다.
사용자 B는 트랜잭션을 시작하고, 잠금이 없는 SELECT 문으로 데이터를 조회합니다. 그리고 사용자 A는 INSERT 문을 사용해 데이터를 추가하였습니다. 이때 잠금이 없으므로 바로 COMMIT이 됩니다. 하지만 사용자 B가 SELECT FOR UPDATE로 조회를 했다면, 언두 로그가 아닌 테이블로부터 레코드를 조회하므로 Phantom Read가 발생합니다.
하지만 이러한 케이스는 거의 존재하지 않으므로, MySQL의 REPEATABLE READ에서는 PHANTOM READ가 발생하지 않는다고 봐도 될 것 같습니다.
아래는 MySQL 기준으로 정리한 내용입니다.
SELECT FOR UPDATE 이후 SELECT: 갭 락 때문에 팬텀 리드 X
SELECT FOR UPDATE 이후 SELECT FOR UPDATE: 갭 락 때문에 팬텀 리드 X
SELECT 이후 SELECT: MVCC 때문에 팬텀 리드 X
트랜잭션 격리 수준이 반복 읽기(기본 수준)인 경우, 동일한 트랜잭션 내의 모든 일관된 읽기는 해당 트랜잭션의 첫 번째 읽기에 의해 설정된 스냅샷을 읽습니다. 현재 트랜잭션을 커밋하고 그 후에 새 쿼리를 실행하면 쿼리에 대한 최신 스냅샷을 얻을 수 있습니다.
언두 로그는 잠금을 걸수가 없어서 현재 상태의 데이터를 읽게 된다. SELECT FOR UPDATE 문으로 이미 쓰여진 레코드들에 쓰기 잠금을 걸면서 읽기 때문에 언두 로그를 참조하려 했지만 INSERT 된 언두 로그는 잠금을 걸 수가 없어서 현재 상태의 데이터를 읽게 됩니다.
NON-REPEATABLE READ vs. PHANTOM READ
NON-REPEATABLE READ : 원래 있던 행의 값이 달라지는 것
PHANTOM READ : 새로운 행이 생기는 것
🤔 InnoDB의 REPEATABLE READ에서는 왜 PHANTOM READ가 생기지 않을까?
PHANTOM READ는 언두 영역에 락을 못걸어서 바로 변경된 값을 가져오다보니 생기는 현상인데 InnoDB는 갭 락과 넥스트 키 락을 사용해 현재 트랜잭션에서 검색하는 레코드와 주변의 갭에 잠금이 걸리기 때문에 갭에 새 레코드를 삽입할 수 없어서 PHANTOM READ가 생기지 않습니다.
4. SERIALIZABLE
한 트랜잭션에서 사용하는 데이터를 다른 트랜잭션에서 접근할 수 없습니다.
PHANTOM READ를 방지하기 위해 사용하는 락입니다.
가장 엄격한 격리 수준이고, 가장 낮은 동시성 처리 성능을 보여줍니다.
InnoDB는 순수한 SELECT 읽기 작업의 경우 잠금이 필요없는 일관된 읽기를 지원하므로 아무런 레코드 잠금 없이 실행됩니다.
읽기 작업도 읽기 잠금이 무조건 걸려야 하고, 따라서 같은 읽기 작업이 아니라면 트랜잭션을 모두 기다려야 하기 때문에 성능이 저하됩니다.
InnoDB에서는 PHANTOM READ 현상이 REPEATABLE READ 격리 수준에서 발생하지 않기 때문에 굳이 사용할 필요는 없습니다.
위의 과제를 진행하면서 한 인스턴스에 각각 다른 버전의 Redis를 설치해주어야 했습니다.
Redis의 기본 포트는 6379이기 때문에, 두 Redis의 포트가 겹치지 않도록 포트 번호를 변경해 설치되도록 진행해보겠습니다!
아래 과정대로 진행해줍니다.
# Redis 소스 코드 다운로드 및 압축 해제
$ wget http://download.redis.io/releases/redis-x.x.x.tar.gz
$ tar xzf redis-x.x.x.tar.gz
$ cd redis-x.x.x
# redis.conf 파일 열고 포트 번호 변경
$ vi redis.conf
# port 6379 -> port 6378
# Redis 서버 컴파일 및 설치
$ make
$ make install
# 변경된 포트 번호로 Redis 서버 실행
$ src/redis-server redis.conf