데이터 베이스에 급격스런 데이터가 추가 되었을 때
전략
데이터 베이스에 급격스런 데이터가 추가 되면 성능에 큰 하자가 생길 때가 있다. 이럴 경우 어떤 방식으로 데이터를 분산할까?
1. DB Sharding
2. DB Partitioning
3. Replication
4. Distributed db system
다음과 같은 전략이 사용될 수 있을 것이다. 이중 이번엔 sharding에 대하 기록하도록 한다.
1. 샤딩이랑 무엇인가?
샤딩(sharding)은 db를 나누어 다른 machine(샤드(shard) 라고 불린다)에 db를 저장하는 방식을 말한다.

2. 샤딩의 전략
A. 수평적 샤딩(Horizontal Sharding)

row단위로 테이블을 나누어 갹 샤드마다 row의 범위를 지정하여 저장한다.
위의 그림과 같이 샤드마다 range[0,100], range[100,200]이런식으로 저장하는 방법이다.
저장된 data는 pk의 범위에 따라 불러오는 db가 다르다.
보통 database level에서 사용하지만 application level에서도 사용할 수 있다.
B. 수직적 샤딩 (Vertical Sharding)

테이블 단위로 각 샤드마다 나누어 저장한다.
테이블에 따라 샤드를 다르게 해서 데이터를 가져와야한다.
주로 application level에서 사용된다.
※ Vertical Sharding 의문점? M-N관계에서 호출테이블 혹은 bridge table은 어떻게 해야하는가?
M-N 관계일 때 중간역할을 하는 테이블을 어디에 저장해야 될 지 고민이 될 것이다.
다음과 같은 예시를 보자
user table
item table
user-item table
방법 A. Centralized DB
- centralized DB에 user-item table을 넣는다.
- 샤드 1에 user table을 넣는다.
- 샤드 2에 item table을 넣는다.
장점
- 간단하게 관계를 관리할 수 있다.
- centralized를 통해 m-n관계를 통제가능하다.
단점
- 병목현상이 centralized db에서 발생할 수 있다.
-- Central database
CREATE TABLE User_Item (
user_id INT,
item_id INT,
PRIMARY KEY (user_id, item_id),
FOREIGN KEY (user_id) REFERENCES shard1.User(id),
FOREIGN KEY (item_id) REFERENCES shard2.Item(id)
);
-- Shard 1 (User database)
CREATE TABLE User (
id INT PRIMARY KEY,
username VARCHAR(50)
-- other user fields
);
-- Shard 2 (Item database)
CREATE TABLE Item (
id INT PRIMARY KEY,
item_name VARCHAR(50)
-- other item fields
);
방법 B. Applicaation level Join
- 샤드 1에 user table / user-item table을 넣는다.
- 샤드 2에 item table을 넣는다.
def get_user_items(user_id):
# Query the User database
user_shard = connect_to_user_shard()
user_items = user_shard.execute("SELECT item_id FROM User_Item WHERE user_id = ?", (user_id,))
# Collect item_ids
item_ids = [row['item_id'] for row in user_items]
# Query the Item database
item_shard = connect_to_item_shard()
items = item_shard.execute("SELECT * FROM Item WHERE id IN (?)", (item_ids,))
return items
다음과 같이 app단위에서 user와 item을 조인하여 데이터를 조회한다.
장점
- 샤드에 부하를 분산할 수 있다.
- 단일 실패 지점(single point of failure) 병목현상의 위험이 없다
※ 단일 실패 지점이란 시스템이나 네트워크의 한 부분이 고장 났을 때 전체 시스템이 다운되는 것을 말한다.
단점
- 로직이 길어지고 복잡해진다
- 쿼리가 훨씬 더 복잡해질 가능성이 높고 데이터 핸들링이 복잡하다.
추가적인 vertical shard 코드 예시
샤드 1에 user table / user-item table을 만든다
샤드 2에 item table을 만든다
-- User table in user_shard database
CREATE TABLE User (
id INT PRIMARY KEY,
username VARCHAR(50)
-- other user fields
);
-- User_Item bridge table in user_shard database
-- Note: Foreign key constraints to Item table are not enforced
-- Note: foreign key는 선언하지 않는다.
CREATE TABLE User_Item (
user_id INT,
item_id INT,
PRIMARY KEY (user_id, item_id)
);
-- Item table in item_shard database
CREATE TABLE Item (
id INT PRIMARY KEY,
item_name VARCHAR(50)
-- other item fields
);
각 샤드에 연결한 뒤 데이터를 삽입 혹은 조회하는 방법
import sqlite3
def connect_to_user_shard():
conn = sqlite3.connect('user_shard.db')
conn.row_factory = sqlite3.Row
return conn
def connect_to_item_shard():
conn = sqlite3.connect('item_shard.db')
conn.row_factory = sqlite3.Row
return conn
def add_user_item_relation(user_id, item_id):
# Connect to the user shard
user_shard = connect_to_user_shard()
# Connect to the item shard
item_shard = connect_to_item_shard()
# Verify that the user exists in the user shard
user = user_shard.execute("SELECT id FROM User WHERE id = ?", (user_id,)).fetchone()
if not user:
raise ValueError("User ID does not exist")
# Verify that the item exists in the item shard
item = item_shard.execute("SELECT id FROM Item WHERE id = ?", (item_id,)).fetchone()
if not item:
raise ValueError("Item ID does not exist")
# Insert into the User_Item bridge table
user_shard.execute("INSERT INTO User_Item (user_id, item_id) VALUES (?, ?)", (user_id, item_id))
user_shard.commit()
# Example usage
try:
add_user_item_relation(1, 101)
print("User-Item relation added successfully")
except ValueError as e:
print(e)