Database
데이터베이스
구조화된 데이터의 조직적 저장소. 관계형(RDBMS)과 비관계형(NoSQL)으로 구분되며, DBMS를 통해 데이터를 효율적으로 저장, 조회, 수정, 삭제합니다.
데이터베이스
구조화된 데이터의 조직적 저장소. 관계형(RDBMS)과 비관계형(NoSQL)으로 구분되며, DBMS를 통해 데이터를 효율적으로 저장, 조회, 수정, 삭제합니다.
데이터베이스(Database)는 체계적으로 구조화된 데이터의 집합으로, DBMS(Database Management System)를 통해 관리됩니다. 애플리케이션의 영속적 데이터 저장, 빠른 검색, 동시 접근 제어, 데이터 무결성 보장 등의 핵심 기능을 제공합니다.
테이블(행/열) 기반, SQL 쿼리 사용, 스키마 정의 필수, ACID 트랜잭션 보장. 정형화된 데이터와 복잡한 JOIN 연산에 적합.
스키마 유연, 수평 확장 용이, BASE 특성. 대용량 비정형 데이터와 높은 쓰기 처리량에 적합.
| 특성 | ACID (관계형) | BASE (NoSQL) |
|---|---|---|
| 일관성 | 강한 일관성 | 최종적 일관성 |
| 가용성 | 일관성 우선 | 가용성 우선 |
| 확장 | 수직 확장 (Scale-up) | 수평 확장 (Scale-out) |
| 적합 케이스 | 금융, 주문 시스템 | 로그, 소셜, IoT |
-- 테이블 생성 (정규화된 스키마)
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
total_amount DECIMAL(10, 2) NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 인덱스 생성
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_status ON orders(status);
-- CRUD 작업
INSERT INTO users (email, name) VALUES ('user@example.com', '홍길동');
SELECT u.name, COUNT(o.id) AS order_count, SUM(o.total_amount) AS total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id
HAVING COUNT(o.id) > 0;
// 컬렉션에 문서 삽입
db.users.insertOne({
email: "user@example.com",
name: "홍길동",
profile: {
age: 30,
interests: ["AI", "Database"]
},
orders: [
{ product: "서버", amount: 1000000, date: new Date() }
],
created_at: new Date()
});
// 집계 파이프라인 - 사용자별 총 주문 금액
db.users.aggregate([
{ $unwind: "$orders" },
{ $group: {
_id: "$email",
total_spent: { $sum: "$orders.amount" },
order_count: { $sum: 1 }
}},
{ $sort: { total_spent: -1 } },
{ $limit: 10 }
]);
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
# 데이터베이스 연결
DATABASE_URL = "postgresql://user:password@localhost:5432/mydb"
engine = create_engine(DATABASE_URL)
Session = sessionmaker(bind=engine)
Base = declarative_base()
# 모델 정의
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
email = Column(String(255), unique=True, nullable=False)
name = Column(String(100), nullable=False)
orders = relationship("Order", back_populates="user", lazy="selectin")
class Order(Base):
__tablename__ = 'orders'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id'))
total_amount = Column(Integer)
user = relationship("User", back_populates="orders")
# CRUD 작업
session = Session()
# Create
new_user = User(email="test@example.com", name="테스트")
session.add(new_user)
session.commit()
# Read with relationship (N+1 방지)
users = session.query(User).options(
selectinload(User.orders)
).filter(User.name.like('%테스트%')).all()
# Transaction
try:
session.begin()
user = session.query(User).filter_by(id=1).first()
user.name = "수정된 이름"
session.commit()
except Exception:
session.rollback()
raise
import redis
import json
r = redis.Redis(host='localhost', port=6379, db=0)
# 캐시 패턴: Cache-Aside
def get_user(user_id: int):
cache_key = f"user:{user_id}"
# 1. 캐시 확인
cached = r.get(cache_key)
if cached:
return json.loads(cached)
# 2. DB 조회 (캐시 미스)
user = db.query(User).get(user_id) # SQLAlchemy
# 3. 캐시 저장 (TTL 1시간)
r.setex(cache_key, 3600, json.dumps(user.to_dict()))
return user
# 세션 저장
r.hset("session:abc123", mapping={
"user_id": "1",
"email": "user@example.com",
"login_at": "2024-01-01T00:00:00"
})
r.expire("session:abc123", 86400) # 24시간 만료
ORM에서 관계 데이터를 조회할 때 반복문마다 추가 쿼리가 발생하는 N+1 문제 주의! Eager Loading(selectinload, joinedload)이나 데이터로더 패턴을 사용하세요.
인덱스가 많을수록 INSERT/UPDATE가 느려집니다. 실제 쿼리 패턴을 분석해서 필요한 인덱스만 생성하고, 복합 인덱스의 컬럼 순서(선택도 높은 것 먼저)를 신중히 결정하세요.
데이터베이스 커넥션은 비싼 리소스입니다. Connection Pool을 적절히 설정하고, 쿼리 후 반드시 커넥션을 반환하세요. 장기 트랜잭션은 커넥션을 점유하므로 피해야 합니다.