KR_SQLite - somaz94/python-study GitHub Wiki

6️⃣ νŠΈλžœμž­μ…˜ 처리

νŠΈλžœμž­μ…˜μ€ λ°μ΄ν„°λ² μ΄μŠ€ μž‘μ—…μ„ 논리적 λ‹¨μœ„λ‘œ λ¬Άμ–΄ μ‹€ν–‰ν•˜λŠ” 방법이닀. λͺ¨λ“  μž‘μ—…μ΄ μ„±κ³΅ν•˜λ©΄ 변경사항이 μ €μž₯되고, ν•˜λ‚˜λΌλ„ μ‹€νŒ¨ν•˜λ©΄ λͺ¨λ“  변경사항이 μ·¨μ†Œλœλ‹€.

νŠΈλžœμž­μ…˜μ€ λ‹€μŒκ³Ό 같은 κΈ°λ³Έ 속성(ACID)을 κ°€μ§„λ‹€:

  • μ›μžμ„±(Atomicity): νŠΈλžœμž­μ…˜ λ‚΄ λͺ¨λ“  μž‘μ—…μ€ μ „λΆ€ μ„±κ³΅ν•˜κ±°λ‚˜ μ „λΆ€ μ‹€νŒ¨ν•œλ‹€
  • 일관성(Consistency): νŠΈλžœμž­μ…˜ μ‹€ν–‰ μ „ν›„λ‘œ λ°μ΄ν„°λ² μ΄μŠ€κ°€ μΌκ΄€λœ μƒνƒœλ₯Ό μœ μ§€ν•œλ‹€
  • 격리성(Isolation): λ™μ‹œμ— μ‹€ν–‰λ˜λŠ” νŠΈλžœμž­μ…˜λ“€μ΄ μ„œλ‘œ 영ν–₯을 μ£Όμ§€ μ•ŠλŠ”λ‹€
  • 지속성(Durability): νŠΈλžœμž­μ…˜μ΄ μ»€λ°‹λ˜λ©΄ 변경사항이 영ꡬ적으둜 μ €μž₯λœλ‹€

κΈ°λ³Έ νŠΈλžœμž­μ…˜ κ΅¬ν˜„

import sqlite3

conn = sqlite3.connect('example.db')
try:
    conn.execute('BEGIN TRANSACTION')
    conn.execute('INSERT INTO users (name, email) VALUES (?, ?)', ('Kim', '[email protected]'))
    conn.execute('UPDATE balances SET amount = amount - 100 WHERE user_id = 1')
    conn.execute('UPDATE balances SET amount = amount + 100 WHERE user_id = 2')
    conn.commit()
    print("νŠΈλžœμž­μ…˜μ΄ μ„±κ³΅μ μœΌλ‘œ μ™„λ£Œλ˜μ—ˆλ‹€")
except Exception as e:
    conn.rollback()
    print(f"였λ₯˜ λ°œμƒμœΌλ‘œ νŠΈλžœμž­μ…˜μ΄ λ‘€λ°±λ˜μ—ˆλ‹€: {e}")
finally:
    conn.close()

νŠΈλžœμž­μ…˜ 관리 클래슀 κ΅¬ν˜„

class TransactionManager:
    def __init__(self, db_path):
        self.db_path = db_path
        self.connection = None
        
    def __enter__(self):
        self.connection = sqlite3.connect(self.db_path)
        self.connection.execute('BEGIN TRANSACTION')
        return self.connection
        
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None:
            self.connection.commit()
        else:
            self.connection.rollback()
        self.connection.close()
        return False  # μ˜ˆμ™Έ μ „νŒŒ

# μ‚¬μš© μ˜ˆμ‹œ
def transfer_money(from_id, to_id, amount):
    with TransactionManager('finance.db') as conn:
        # μž”μ•‘ 확인
        cursor = conn.execute('SELECT balance FROM accounts WHERE id = ?', (from_id,))
        from_balance = cursor.fetchone()[0]
        
        if from_balance < amount:
            raise ValueError("μž”μ•‘μ΄ λΆ€μ‘±ν•˜λ‹€")
            
        # μ†‘κΈˆ μ‹€ν–‰
        conn.execute('UPDATE accounts SET balance = balance - ? WHERE id = ?', (amount, from_id))
        conn.execute('UPDATE accounts SET balance = balance + ? WHERE id = ?', (amount, to_id))
        
        # 거래 기둝 μ €μž₯
        conn.execute(
            'INSERT INTO transactions (from_id, to_id, amount, timestamp) VALUES (?, ?, ?, datetime("now"))',
            (from_id, to_id, amount)
        )

격리 μˆ˜μ€€ μ„€μ •

SQLiteλŠ” λ‹€μŒ 격리 μˆ˜μ€€μ„ μ§€μ›ν•œλ‹€:

  • DEFERRED(κΈ°λ³Έκ°’): 데이터 μˆ˜μ • μ‹œμ—λ§Œ 락 νšλ“
  • IMMEDIATE: νŠΈλžœμž­μ…˜ μ‹œμž‘ μ‹œ 락 νšλ“
  • EXCLUSIVE: 독점적 락 νšλ“
# 격리 μˆ˜μ€€ μ§€μ •
conn = sqlite3.connect('example.db')
conn.execute('BEGIN IMMEDIATE TRANSACTION')
# μž‘μ—… μˆ˜ν–‰
conn.commit()

7️⃣ SQLite κ³ κΈ‰ κΈ°λŠ₯

SQLiteλŠ” κ²½λŸ‰ λ°μ΄ν„°λ² μ΄μŠ€μ§€λ§Œ λ‹€μ–‘ν•œ κ³ κΈ‰ κΈ°λŠ₯을 μ œκ³΅ν•œλ‹€.

μ „λ¬Έ 검색(FTS5)

μ „λ¬Έ 검색(Full-Text Search)은 ν…μŠ€νŠΈ λ‚΄μš©μ„ 효율적으둜 검색할 수 있게 ν•΄μ£ΌλŠ” ν™•μž₯ κΈ°λŠ₯이닀.

import sqlite3

conn = sqlite3.connect('library.db')
# FTS5 ν…Œμ΄λΈ” 생성
conn.execute('''
CREATE VIRTUAL TABLE IF NOT EXISTS books_fts USING fts5(
    title, 
    author, 
    content,
    tokenize='unicode61'
)
''')

# 데이터 μ‚½μž…
conn.execute('''
INSERT INTO books_fts (title, author, content) VALUES 
(?, ?, ?)
''', ('파이썬 ν”„λ‘œκ·Έλž˜λ°', '홍길동', 'νŒŒμ΄μ¬μ€ κ°„κ²°ν•˜κ³  읽기 μ‰¬μš΄ 문법을 κ°€μ§„ ν”„λ‘œκ·Έλž˜λ° 언어이닀.'))

# 검색 μ˜ˆμ‹œ
cursor = conn.execute('SELECT * FROM books_fts WHERE books_fts MATCH ?', ('ν”„λ‘œκ·Έλž˜λ°',))
for row in cursor:
    print(row)

conn.close()

JSON ν™•μž₯ κΈ°λŠ₯

SQLite 3.9.0 μ΄μƒμ—μ„œλŠ” JSON 데이터λ₯Ό μ €μž₯ν•˜κ³  쿼리할 수 μžˆλŠ” κΈ°λŠ₯을 μ œκ³΅ν•œλ‹€.

import sqlite3
import json

conn = sqlite3.connect('config.db')

# JSON 데이터λ₯Ό μ €μž₯ν•  ν…Œμ΄λΈ” 생성
conn.execute('''
CREATE TABLE IF NOT EXISTS configs (
    id INTEGER PRIMARY KEY,
    name TEXT UNIQUE,
    settings TEXT
)
''')

# JSON 데이터 μ €μž₯
app_config = {
    "version": "1.0.0",
    "features": {
        "dark_mode": True,
        "notifications": {
            "enabled": True,
            "sound": "default"
        }
    },
    "limits": [10, 20, 30]
}

conn.execute(
    "INSERT OR REPLACE INTO configs (name, settings) VALUES (?, ?)",
    ("app_config", json.dumps(app_config))
)

# JSON μ†μ„±μœΌλ‘œ 쿼리
cursor = conn.execute(
    "SELECT * FROM configs WHERE json_extract(settings, '$.features.dark_mode') = 1"
)
for row in cursor:
    config = json.loads(row[2])
    print(f"ID: {row[0]}, Name: {row[1]}")
    print(f"μ„€μ •: {config}")

conn.close()

R-Tree 인덱싱

곡간 데이터λ₯Ό 효율적으둜 μΈλ±μ‹±ν•˜κ³  μΏΌλ¦¬ν•˜κΈ° μœ„ν•œ ν™•μž₯ κΈ°λŠ₯이닀.

import sqlite3

conn = sqlite3.connect('locations.db')

# R-Tree ν…Œμ΄λΈ” 생성
conn.execute('''
CREATE VIRTUAL TABLE IF NOT EXISTS locations USING rtree(
    id,              -- μ •μˆ˜ κΈ°λ³Έ ν‚€
    min_lat, max_lat,  -- μœ„λ„ λ²”μœ„
    min_lng, max_lng   -- 경도 λ²”μœ„
)
''')

# μœ„μΉ˜ 데이터 μ‚½μž… (μ„œμšΈμ‹œ 강남ꡬ μ’Œν‘œ λ²”μœ„ μ˜ˆμ‹œ)
conn.execute('''
INSERT INTO locations VALUES (?, ?, ?, ?, ?)
''', (1, 37.45, 37.53, 127.02, 127.08))

# νŠΉμ • 지점(37.5, 127.05)μ—μ„œ κ°€κΉŒμš΄ μœ„μΉ˜ 검색
cursor = conn.execute('''
SELECT id FROM locations
WHERE min_lat <= ? AND max_lat >= ?
  AND min_lng <= ? AND max_lng >= ?
''', (37.5, 37.5, 127.05, 127.05))

for row in cursor:
    print(f"μœ„μΉ˜ ID: {row[0]}")

conn.close()

μ£Όμš” 팁

SQLite μ‚¬μš© μ‹œ μœ μš©ν•œ νŒμ΄λ‹€:

  1. λ™μ‹œμ„± 처리: SQLiteλŠ” μ—¬λŸ¬ ν”„λ‘œμ„ΈμŠ€κ°€ λ™μ‹œμ— 읽을 수 μžˆμ§€λ§Œ, μ“°κΈ°λŠ” ν•œ λ²ˆμ— ν•˜λ‚˜λ§Œ κ°€λŠ₯ν•˜λ‹€. λ§Žμ€ λ™μ‹œ μ“°κΈ°κ°€ ν•„μš”ν•œ 경우 λŒ€κΈ° μ‹œκ°„μ„ κ΄€λ¦¬ν•˜μž.

  2. νŠΈλžœμž­μ…˜ ν™œμš©: μ—¬λŸ¬ μž‘μ—…μ„ ν•œ λ²ˆμ— μ²˜λ¦¬ν•΄μ•Ό ν•  λ•ŒλŠ” 항상 νŠΈλžœμž­μ…˜μ„ μ‚¬μš©ν•˜μ—¬ 데이터 일관성을 μœ μ§€ν•˜μž.

  3. WAL λͺ¨λ“œ μ‚¬μš©: Write-Ahead Logging λͺ¨λ“œλŠ” 읽기 μž‘μ—…κ³Ό μ“°κΈ° μž‘μ—…μ˜ μΆ©λŒμ„ 쀄여 μ„±λŠ₯을 ν–₯μƒμ‹œν‚¨λ‹€.

    conn = sqlite3.connect('example.db')
    conn.execute('PRAGMA journal_mode=WAL')
  4. 인덱슀 관리: 자주 κ²€μƒ‰ν•˜λŠ” μ—΄μ—λŠ” 인덱슀λ₯Ό μƒμ„±ν•˜λ˜, λ„ˆλ¬΄ λ§Žμ€ μΈλ±μŠ€λŠ” μ“°κΈ° μ„±λŠ₯을 μ €ν•˜μ‹œν‚¬ 수 μžˆλ‹€.

  5. νŒŒλΌλ―Έν„°ν™”λœ 쿼리 μ‚¬μš©: SQL μΈμ μ…˜ 방지와 μ„±λŠ₯ ν–₯상을 μœ„ν•΄ 항상 νŒŒλΌλ―Έν„°ν™”λœ 쿼리λ₯Ό μ‚¬μš©ν•˜μž.

  6. ν™•μž₯ κΈ°λŠ₯ ν™œμš©: FTS, JSON, R-Tree λ“±μ˜ ν™•μž₯ κΈ°λŠ₯을 적절히 ν™œμš©ν•˜μ—¬ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ κΈ°λŠ₯을 κ°•ν™”ν•˜μž.

  7. λ©”λͺ¨λ¦¬ λ°μ΄ν„°λ² μ΄μŠ€ ν™œμš©: μž„μ‹œ μž‘μ—…μ΄λ‚˜ ν…ŒμŠ€νŠΈμ—λŠ” :memory:λ₯Ό μ‚¬μš©ν•˜μ—¬ λ©”λͺ¨λ¦¬μ—μ„œ μž‘μ—…ν•˜λ©΄ 속도가 λΉ λ₯΄λ‹€.

  8. 주기적 VACUUM: λ°μ΄ν„°λ² μ΄μŠ€ 파일 크기λ₯Ό μ΅œμ ν™”ν•˜κΈ° μœ„ν•΄ 주기적으둜 VACUUM λͺ…령을 μ‹€ν–‰ν•˜μž.

⚠️ **GitHub.com Fallback** ⚠️