이런 lock에도 여러가지 종류가 있고, 명시적으로 사용되는 경우/묵시적으로 사용되는 경우가 있는데, 간단하게 알아보자
시작하기 전 3줄요약
Lock이 미치는 범위를 level로 나눈다
Lock 모드별로 충돌하는 관계가 존재한다
Lock은 트랜잭션 종료 시 혹은 롤백시에 풀린다.
lock이 미치는 범위를 Level로 나누는데, Table-Level Lock, Row-Level Lock, Page-Level Lock, Database-Level Lock 까지 다양하게 존재한다.
Table-Level Lock
Table Level Lock은 테이블 수준에 락을 거는 방법이다.
만약 테이블 내에 100개의 로우가 있다고 하면 하나의 로우에 접근하는 동안 나머지 99개의 로우에 접근 할 수 없기 때문에, 다중 사용자 환경에서는 사용하지 않는 편이 좋다.
(보통 테이블 전체 로우의 변경이 있는 DDL 구문과 함께 사용됨. (ex) TRUNCATE, ALTER))
이런 Table Level Lock은 LOCK 명령어를 이용해서 명시적으로 걸어줄 수도 있지만, 우리가 특정 쿼리문을 사용할 때 마다 묵시적으로 걸리게 된다. 어느 상황에 어느 락이 걸리는지는 해당 문서를 보면 알 수 있다.
Row-Level Lock
Row-Level Lock은 row 수준에 락을 거는 방법이다.
SELECT ~ FOR SHARE과 같은 DML 구문과 함께 가장 자주 사용되는 Lock이다.
만약 이런 Lock의 종류가 하나밖에 없다면, 여러 종류의 트랜잭션에서 lock을 알맞게 사용하기 어려워지는데, 이런 상황을 처리하기 위해 Lock에는 Lock모드라는 것이 존재한다.
Lock 모드?
Lock 모드 별로 서로 충돌하는 Lock 모드가 있으며, 충돌한다면 해당 리소스(table, row)에 동시에 접근할 수 없게 된다다.
ACCESS SHARE락은 ACCESS EXCLUSIVE 락과 충돌한다,
ROW SHARE락은 EXCLUSIVE, ACCESS EXCLUSIVE 락과 충돌한다
Table-Level Lock에는ACCESS SHARE, ROW SHARE 등, Row-Level Lock에는 FOR UPDATE, FOR KEY SHARE 등 여러가지 Lock 모드가 존재한다. 이런 모드들의 이름은 일반적으로 사용되는 경우를 나타내지만, 모드마다 기능적으로 다른 점은 없다.
1. Bastion VM pgcopydb 설치 및 연결 테스트
2. Source DB유저에게 모든 sequence 권한 부여
3. Target DB유저에게 임시로 superuser 권한 부여
4. DB 동기화
5. 복제 상황 모니터링
6. 복제 지연 최소화 대기 및 데이터 검증
7. api, worker, cron pod 제거
8. 복제 지연 없음 확인
9. pgcopydb 프로세스 종료
10. replication slot, origin 삭제
11. target DB유저 superuser 권한 제거
12. api, worker, cron 재배포
순서대로 차근차근 진행해보자
1. Bastion VM pgcopydb 설치 및 연결 테스트
sudo apt update
sudo apt install pgcopydb
mkdir -p pgcopydb-migration
cd pgcopydb-migration
export PGCOPYDB_SOURCE_PGURI="postgres://source-db:thisispassword@127.0.0.1:5432/source-db"
export PGCOPYDB_TARGET_PGURI="postgres://target-db:thisispassword@127.0.0.1:5433/target-db"
pgcopydb ping --source "$PGCOPYDB_SOURCE_PGURI" --target "$PGCOPYDB_TARGET_PGURI"
# 기대 출력
## INFO Successfully could connect to source database
## INFO Successfully could connect to target database
2. Source DB유저에게 모든 sequence 권한 부여
-- source-db 유저에게 모든 sequence 권한 부여
GRANT SELECT, USAGE ON ALL SEQUENCES IN SCHEMA public TO "source-db";
3. Target DB유저에게 임시로 superuser 권한 부여
-- 임시로 superuser 권한 부여
ALTER USER "target-db" WITH SUPERUSER;
4. DB 동기화
nohup pgcopydb clone --follow \
--source "$PGCOPYDB_SOURCE_PGURI" \
--target "$PGCOPYDB_TARGET_PGURI" \
--no-owner \
--no-acl \
--table-jobs 1 \
--index-jobs 1 \
--verbose \
--dir . \
> ./pgcopydb-migration.log 2>&1 &
# 프로세스 ID 저장
echo $! > ./pgcopydb-migration.pid
# 실시간 로그 확인
tail -f ./pgcopydb-migration.log
# 진행 상황 확인
./replication_monitoring.sh
6. 복제 지연 최소화 대기 및 데이터 검증
복제지연 확인
SELECT
slot_name,
active,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) as lag_size,
pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn) as lag_bytes
FROM pg_replication_slots
WHERE slot_name = 'pgcopydb';
-- Result --
slot_name | active | lag_size | lag_bytes
-----------+--------+----------+-----------
pgcopydb | t | 541 kB | 553920
(1 row)
데이터 일관성 검증
pgcopydb compare schema --verbose
pgcopydb compare data --verbose
약 541KB 만큼의 복제 지연이 있다는 의미이다.
# 데이터 정합성 확인
pgcopydb compare data
# Output
16:11:44.270 162208 INFO Running pgcopydb version 0.17-1.pgdg22.04+1 from "/usr/bin/pgcopydb"
16:11:44.357 162208 INFO Using work dir "/tmp/pgcopydb"
16:11:44.358 162208 INFO SOURCE: Connecting to "postgres://target-db@127.0.0.1:5432/target-db?keepalives=1&keepalives_idle=10&keepalives_interval=10&keepalives_count=60"
16:11:44.361 162208 INFO Re-using catalog caches
16:11:44.365 162208 INFO Starting 4 table compare processes
Table Name | ! | Source Checksum | Target Checksum
-------------------------------+---+--------------------------------------+-------------------------------------
public.user | | 9136aaf4-0f32-cd2b-850f-49270ff7c03 | 9136aaf4-0f32-cd2b-850f-49270ff7c03
확장의 용이성MongoDB의 데이터 모델과 지속성 전략은 높은 읽기/쓰기 효율과 자동 장애조치를 통한 확장의 용이성을 염두에 두고 만들어졌다.
MongoDB는 웹 애플리케이션과 인터넷 기반을 위해 설계된 데이터베이스 관리 시스템이다.
직관적인 데이터 모델예를 들자면 아래에 사용자에 대한 정보를 몇 개의 필드로 저장한 도큐먼트가 있다.만약 하나의 사용자가 여러개의 이메일을 가질 수 있다면 어떻게 해야할까?하지만 MongoDB는 아래와 같이 사용하면 된다.예시와 같이 MongoDB는 한명의 사람 정보를 얻기 위해서 하나의 도큐먼트로 표현할 수 있다.
만약 RDBMS였다면 여러개의 테이블에 나눠져 있는 정보를 조인 연산으로 가득찬 SQL 쿼리를 만들어 표현했어야 할 것이다.
PostgreSQL에서는 동시성 제어를 위해 여러가지 모드의 lock을 제공합니다. 이런 lock에도 여러가지 종류가 있고, 명시적으로 사용되는 경우/묵시적으로 사용되는 경우가 있는데, 자세히 알아보도록 합시다.
시작하기 전 3줄요약
Lock이 미치는 범위를 level로 나눈다
Lock 모드별로 충돌하는 관계가 존재한다
Lock은 트랜잭션 종료 시 혹은 롤백시에 풀린다.
lock이 미치는 범위를 Level로 나누는데, Table-Level Lock, Row-Level Lock, Page-Level Lock, Database-Level Lock 까지 다양하게 존재합니다. 해당 글에서는 Table-Level Lock과 Row-Level Lock 에 대해서만 설명하겠습니다.
Table-Level Lock
Table Level Lock은 테이블 수준에 락을 거는 방법입니다. 만약 테이블 내에 100개의 로우가 있다고 하면 하나의 로우에 접근하는 동안 나머지 99개의 로우에 접근 할 수 없기 때문에, 다중 사용자 환경에서는 사용하지 않는 편이 좋습니다. *(보통 테이블 전체 로우의 변경이 있는 DDL 구문과 함께 사용됩니다. (ex) TRUNCATE, ALTER)) *
이런 Table Level Lock은 LOCK 명령어를 이용해서 명시적으로 걸어줄 수도 있지만, 우리가 특정 쿼리문을 사용할 때 마다 암묵적으로 걸리게 됩니다. 어느 상황에 어느 락이 걸리는 지는 해당 문서를 보면 알 수 있습니다.
Row-Level Lock
Row-Level Lock은 row 수준에 락을 거는 방법입니다.
SELECT ~ FOR SHARE과 같은 DML 구문과 함께 가장 자주 사용되는 Lock입니다.
만약 이런 Lock 종류가 하나밖에 없다면, 여러 종류의 트랜잭션에서 lock을 알맞게 사용하기 어려워지는데요, 이런 상황을 처리하기 위해 Lock에는 Lock모드라는 것이 존재합니다.
Lock 모드?
Lock 모드 별로 서로 충돌하는 Lock 모드가 있으며, 충돌한다면 해당 리소스(table, row)에 동시에 접근할 수 없게 됩니다. ex)
ACCESS SHARE락은 ACCESS EXCLUSIVE 락과 충돌한다,
ROW SHARE락은 EXCLUSIVE, ACCESS EXCLUSIVE 락과 충돌한다
Table-Level Lock에는ACCESS SHARE, ROW SHARE 등, Row-Level Lock에는 FOR UPDATE, FOR UPDATE 등 여러가지 Lock 모드가 존재합니다. 이런 모드들의 이름은 일반적으로 사용되는 경우를 나타내지만, 모드마다 기능적으로 다른 점은 없습니다.
각 모드마다 차이점은 오직 "어떤 모드의 lock과 충돌하는가" 입니다. 부연설명하자면 충돌하는 lock 모드의 종류가 다른 것이 차이점이라고 볼 수 있겠습니다.
인덱스는 데이터베이스 테이블에 대한 검색 성능을 높혀주는 도구이다. 이런 인덱스를 구현하는 알고리즘에는 여러가지가 있는데, 각 상황에 맞게 사용된다.
먼저 인덱스가 무엇인지 알아보고, 어떤 기준으로 상황에 맞는 알고리즘을 고르면 될지 알아보도록 하자
그리고, 이 글은 꽤나 길기때문에 원하는 정보가 있다면 우측의 인덱스를 잘 활용하길 바란다 👉
🤔 인덱스란?
인덱스는 메모리 영역에 존재하며, 지정된 컬럼을 기준으로 생성된 목차를 의미한다. 아래 그림을 예시로 들어보자.
위 인덱스는 지정된 컬럼(company_id)을 기준으로 정렬되어 있고, pointer가 테이블의 row를 가리키고 있다.
만약 인덱스를 거치지 않고 company_id가 18인 값을 찾으러면 테이블은 company_id 로 정렬되어 있지 않기 때문에 모든 row를 탐색해야 할 것이다 (full scan)
이런 인덱스를 정렬하는 알고리즘은 B-tree, Hash, 비트맵 등 여러가지가 있는데, 알고리즘 마다 쓰이는 상황이 다르다. 자세히 알아보자!
B-tree
B-tree 알고리즘은 인덱스에서 가장 일반적으로 사용되는 알고리즘이다. B tree의 구조는 아래와 같은데,
이 구조의 특징은
하나의 노드가 여러개의 데이터를 가질 수 있으며
하위 노드의 최대 갯수는 상위 노드 데이터 수 + 1 로 결정된다는 점이 있다.
이런 구조 덕분에 검색 속도의 균일성(logN)을 보장할 수 있는데, 그 이유는 다음과 같다.
트리가 만약 왼쪽과 같이 비균형상태라고 쳐보자, 그럼 6이라는 값을 찾기 위해서는 몇개의 노드를 탐색해야 할까? 루트 노드부터 순서대로 6번 탐색해야 할 것이다.
하지만 트리 구조가 오른쪽과 같이 균형이 잡혀있다면 최대 3번의 탐색 안에 무엇이든 원하는 값을 가지게 될 수 있을 것이다.
이런 B-tree 인덱스는 보통 데이터의 값의 종류가 많고동일한 데이터가 적은 컬럼에 사용된다.
주의할 점은 OR 오퍼레이터를 사용하는 쿼리문에 비효율적이다. 이를 해결하기 위해 보통 IN 오퍼레이터를 사용한 쿼리문으로 튜닝하곤 한다.
Hash
Hash 인덱스는 범용적으로 쓰이는 인덱스는 아니지만 동등 비교 검색(=)에서 빠른 속도를 보여주는 인덱스이다.
동작 방식은 아래와 같다
지정된 컬럼의 검색하고자 하는 값을 hash function에 입력
hash function 출력값으로 해시값을 bucket에서 찾아 데이터 레코드 주소를 알아냄
알아낸 데이터 레코드 주소로 원하는 데이터 레코드에 접근
해시맵을 사용하기 때문에 (hash collision이 없는 경우) 시간 복잡도가 1이라는 장점이 있다.
시간 복잡도가 1이라면 Hash 인덱스가 최고 아닌가? 라는 의문이 들 수도 있다. 물론 동등 비교 검색에서는 빠른 성능을 보여주지만, 범위 검색(>, between) 에서는 N의 시간 복잡도를 가지게 때문에 특수한 상황에서만 쓰이는 것이다.
이런 Hash 인덱스는 아래의 상황에서 쓰인다.
데이터 값의 종류가 많음 (hash collision 방지)
InnoDB Adaptive Hash Index(자주 사용되는 값만 해싱하여 버킷에 저장)비트맵비트맵 인덱스는 기존 B-Tree가 가지고 있던 단점을 보완하기 위해 등장한 인덱스다. B-Tree는 아래와 같은 단점을 가지고 있었다.
실제 칼럼 값을 인덱스에 저장하고 있다보니 저장 공간 낭비가 심하다
NOT 이나 NULL을 사용하거나 복잡한 OR 조건에서 성능이 떨어짐
이런 단점을 비트맵 인덱스는 아래와 같이 해결했다.
칼럼 값을 인덱스에 저장하는 것이 아닌 컴퓨터의 가장 작은 단위인 비트로 값을 저장해 저장 공간 절약
bitwise(|, &, ^) 연산으로 복잡한 OR 연산이 빠름
비트맵 탐색을 예시를 들어보자면 아래 조건에 만족하는 row를 찾고 싶다고 가정해보자
여성
미혼
18 - 34세
위 조건과 같을 때 비트맵 값이 101000인 컬럼을 찾으면 되는 것이다.
이런 비트맵 인덱스는 아래의 상황에서 쓰인다.
데이터 값의 종류가 적음(성별, Enum)
쿼리가 OR 연산자를 포함하는 여러개의 WHERE 조건을 가질 때
하지만 비트맵 인덱스는 레코드 하나만 변경되더라도 모든 레코드에 lock이 걸린다는 점 때문에 다른 인덱스 알고리즘에 비해 데이터 갱신비용이 크다는 단점이 있다. 또한, 하나의 비트맵 인덱스만으로는 별로 효과가 없고, 여러 비트맵 인덱스를 동시에 사용할 때 성능 향상에 도움을 준다.