SQLite 完全指南

SQLite 是一个无服务器、单文件的关系型数据库引擎,整个数据库(包括表、索引、数据)存储在一个 .db 文件中。 sqlite3 是 Python 标准库模块,无需安装,遵循 DB-API 2.0 规范(PEP 249)。 sqlite3.connect() 返回 Connection 对象,代表一个数据库连接。 Cursor 是执行 SQL 语句和获取结果的核心对象。 始终使用参数绑定,严禁用字符串拼接构造 SQL,防止 SQL 注入。 Python 3.12 新增,与命名参数等价,此处不展开。 默认情况下,查询结果的每行是元组。设置 row_fa

分享

官方文档:https://www.sqlite.org/docs.html
适用版本:SQLite 3.43+(2026-05-07 整理)


目录


核心特点

SQLite 是一个无服务器、单文件的关系型数据库引擎,整个数据库(包括表、索引、数据)存储在一个 .db 文件中。

适用场景

  • 嵌入式应用(桌面软件、移动端)
  • 单元测试和集成测试(替代真实数据库)
  • 小型工具和脚本(配置存储、本地缓存)
  • 原型开发和快速验证
  • 数据量在数 GB 以内的单机应用

与 MySQL/PostgreSQL 的差异

特性 SQLite MySQL PostgreSQL
架构 无服务器,进程内库 客户端/服务器 客户端/服务器
存储 单文件 多文件目录 数据目录
并发写入 同一时刻只有一个写入者 行级锁,高并发 MVCC,高并发
并发读取 WAL 模式下支持多读单写 支持 支持
数据类型 动态类型(类型亲和性) 强类型 强类型
ALTER TABLE 功能有限(不能删列/改列) 完整支持 完整支持
外键 支持但默认关闭 支持(InnoDB) 支持
JSON 通过扩展支持 原生 JSON 类型 原生 JSONB
全文搜索 FTS5 扩展 FULLTEXT 索引 tsvector
网络访问 不支持 支持 支持
最大数据库大小 281 TB(理论) 无限制 无限制
适用场景 嵌入式/测试/小工具 中大型 Web 应用 复杂查询/大型应用

Python sqlite3 标准库

sqlite3 是 Python 标准库模块,无需安装,遵循 DB-API 2.0 规范(PEP 249)。

import sqlite3

sqlite3.connect()

conn = sqlite3.connect(
    database,
    timeout=5.0,
    detect_types=0,
    isolation_level='',
    check_same_thread=True
)
参数 类型 默认值 说明
database str 必填 数据库文件路径;:memory: 为内存数据库;'' 为临时文件数据库
timeout float 5.0 等待锁释放的超时时间(秒),超时后抛出 OperationalError
detect_types int 0 类型检测模式:sqlite3.PARSE_DECLTYPES 按列声明类型转换,sqlite3.PARSE_COLNAMES 按列别名转换,可用 `
isolation_level str/None '' 事务隔离级别:'' 自动提交 DDL,DML 开启隐式事务;None 关闭隐式事务(手动控制);'DEFERRED'/'IMMEDIATE'/'EXCLUSIVE' 显式事务类型
check_same_thread bool True 是否检查跨线程使用,多线程时需设为 False(见踩坑)
# 基础连接
conn = sqlite3.connect('app.db')

# 关闭隐式事务(完全手动控制)
conn = sqlite3.connect('app.db', isolation_level=None)

# 启用类型检测(配合 register_adapter/register_converter 使用)
conn = sqlite3.connect(
    'app.db',
    detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES
)

Connection 对象

sqlite3.connect() 返回 Connection 对象,代表一个数据库连接。

方法/属性 说明
cursor() 创建并返回 Cursor 对象
commit() 提交当前事务
rollback() 回滚当前事务
close() 关闭连接(未提交的事务会回滚)
execute(sql, params) 快捷方式,内部创建 Cursor 执行,返回 Cursor
executemany(sql, seq) 快捷方式,批量执行
executescript(sql_script) 执行多条 SQL 语句(以 ; 分隔的脚本),自动提交未提交事务
create_function(name, narg, func) 注册自定义 SQL 函数
create_aggregate(name, narg, cls) 注册自定义聚合函数
row_factory 属性,设置行工厂(如 sqlite3.Row
text_factory 属性,设置文本解码方式,默认 str
isolation_level 属性,读取或修改隔离级别
in_transaction 属性(只读),当前是否在事务中
total_changes 属性,连接生命周期内的总变更行数
conn = sqlite3.connect('app.db')

# 创建表
conn.execute('''
    CREATE TABLE IF NOT EXISTS users (
        id   INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT    NOT NULL,
        age  INTEGER
    )
''')
conn.commit()

# 插入数据
conn.execute('INSERT INTO users (name, age) VALUES (?, ?)', ('Alice', 30))
conn.commit()

# 批量插入
conn.executemany(
    'INSERT INTO users (name, age) VALUES (?, ?)',
    [('Bob', 25), ('Carol', 28)]
)
conn.commit()

# 注册自定义函数
import hashlib
conn.create_function('md5', 1, lambda s: hashlib.md5(s.encode()).hexdigest())
rows = conn.execute("SELECT md5(name) FROM users").fetchall()

conn.close()

Cursor 对象

Cursor 是执行 SQL 语句和获取结果的核心对象。

方法/属性 说明
execute(sql, parameters=()) 执行单条 SQL 语句
executemany(sql, seq_of_parameters) 对序列中每组参数执行 SQL(仅 INSERT/UPDATE/DELETE)
executescript(sql_script) 执行 SQL 脚本(多条语句)
fetchone() 返回下一行,无更多行时返回 None
fetchall() 返回所有剩余行,结果为列表
fetchmany(size=cursor.arraysize) 返回最多 size 行,默认 arraysize(默认值 1)
rowcount 上次 execute() 影响的行数(INSERT/UPDATE/DELETE),SELECT 为 -1
lastrowid 上次 INSERT 操作的行 ID(仅 execute() 有效)
description 查询结果的列描述(7 元组列表),SELECT 后有值
arraysize fetchmany() 默认大小,默认 1,可修改
close() 关闭游标
conn = sqlite3.connect('app.db')
cursor = conn.cursor()

# execute + fetchall
cursor.execute('SELECT id, name, age FROM users WHERE age > ?', (20,))
rows = cursor.fetchall()
# [(1, 'Alice', 30), (2, 'Bob', 25), ...]

# execute + fetchone(逐行)
cursor.execute('SELECT * FROM users ORDER BY id')
while True:
    row = cursor.fetchone()
    if row is None:
        break
    print(row)

# execute + fetchmany(分批处理大结果集)
cursor.execute('SELECT * FROM users')
cursor.arraysize = 500
while True:
    batch = cursor.fetchmany()
    if not batch:
        break
    process(batch)

# executemany(批量写入)
data = [('Dave', 22), ('Eve', 35)]
cursor.executemany('INSERT INTO users (name, age) VALUES (?, ?)', data)
print(cursor.rowcount)   # 2
conn.commit()

# lastrowid
cursor.execute('INSERT INTO users (name, age) VALUES (?, ?)', ('Frank', 40))
print(cursor.lastrowid)  # 新插入行的 id
conn.commit()

# description(列信息)
cursor.execute('SELECT id, name FROM users LIMIT 1')
print(cursor.description)
# [('id', None, None, None, None, None, None),
#  ('name', None, None, None, None, None, None)]

cursor.close()
conn.close()

参数绑定

始终使用参数绑定,严禁用字符串拼接构造 SQL,防止 SQL 注入。

? 位置占位符

# 单个参数
cursor.execute('SELECT * FROM users WHERE id = ?', (1,))
# 注意:即使只有一个参数也必须传元组

# 多个参数
cursor.execute(
    'SELECT * FROM users WHERE name = ? AND age > ?',
    ('Alice', 20)
)

# executemany
cursor.executemany(
    'INSERT INTO users (name, age) VALUES (?, ?)',
    [('Alice', 30), ('Bob', 25)]
)

:name 命名占位符

# 使用字典传参,可读性更好
cursor.execute(
    'SELECT * FROM users WHERE name = :name AND age > :min_age',
    {'name': 'Alice', 'min_age': 20}
)

# executemany 也支持命名参数
cursor.executemany(
    'INSERT INTO users (name, age) VALUES (:name, :age)',
    [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}]
)

$name 占位符(Python 3.12+)

Python 3.12 新增,与命名参数等价,此处不展开。


Row 对象

默认情况下,查询结果的每行是元组。设置 row_factory = sqlite3.Row 后,每行变为 Row 对象,支持按列名访问。

conn = sqlite3.connect('app.db')
conn.row_factory = sqlite3.Row

cursor = conn.cursor()
cursor.execute('SELECT id, name, age FROM users WHERE id = 1')
row = cursor.fetchone()

# 按列名访问(推荐)
print(row['name'])   # 'Alice'
print(row['age'])    # 30

# 按索引访问(兼容元组语法)
print(row[0])        # 1

# 转为字典
row_dict = dict(row)
# {'id': 1, 'name': 'Alice', 'age': 30}

# keys() 获取列名
print(row.keys())    # ['id', 'name', 'age']

conn.close()

也可以自定义 row_factory 直接返回字典:

def dict_factory(cursor, row):
    return {col[0]: row[idx] for idx, col in enumerate(cursor.description)}

conn.row_factory = dict_factory

上下文管理器

Connection 对象支持上下文管理器协议(with 语句),但行为与直觉有区别:

  • with conn: 不负责关闭连接,只管理事务
  • 代码块正常结束 → 自动 commit()
  • 代码块抛出异常 → 自动 rollback()
conn = sqlite3.connect('app.db')

# 事务管理
with conn:
    conn.execute('INSERT INTO users (name, age) VALUES (?, ?)', ('Alice', 30))
    conn.execute('INSERT INTO users (name, age) VALUES (?, ?)', ('Bob', 25))
    # 正常结束,自动 commit

# 回滚示例
try:
    with conn:
        conn.execute('INSERT INTO users (name) VALUES (?)', ('Carol',))
        raise ValueError('故意报错')
        # 抛出异常,自动 rollback,Carol 不会被插入
except ValueError:
    pass

# 连接需要手动关闭
conn.close()

如果需要同时管理连接的生命周期,用 contextlib.closing

from contextlib import closing
import sqlite3

with closing(sqlite3.connect('app.db')) as conn:
    with conn:
        conn.execute('INSERT INTO users (name, age) VALUES (?, ?)', ('Alice', 30))
# 退出外层 with 时自动关闭连接

WAL 模式

WAL(Write-Ahead Logging,预写日志)是 SQLite 的一种日志模式,相比默认的 DELETE 模式有更好的并发读性能。

默认模式 vs WAL 模式

特性 DELETE 模式(默认) WAL 模式
读写并发 读写互斥(写时不能读) 读写不阻塞(写时可以读)
写写并发 只允许一个写入者 只允许一个写入者
写入性能 一般 更好(顺序写入 WAL 文件)
读取性能 一般 稍有开销(需检查 WAL 文件)
崩溃恢复
跨进程共享 支持 支持(同一文件系统)
文件数量 1 个 .db 文件 额外产生 .db-wal.db-shm 文件

开启 WAL 模式

import sqlite3

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

# 开启 WAL 模式(持久化,重连后生效)
conn.execute('PRAGMA journal_mode=WAL')
conn.commit()

# 验证当前模式
result = conn.execute('PRAGMA journal_mode').fetchone()
print(result[0])  # 'wal'

WAL 模式是持久化设置,设置一次后数据库文件记录该模式,后续连接无需重新设置(但建议每次连接都执行,以免切换到其他模式的数据库)。

WAL checkpoint

WAL 文件会持续增长,需要定期 checkpoint 将 WAL 内容合并回主数据库文件:

# 手动触发 checkpoint
conn.execute('PRAGMA wal_checkpoint(TRUNCATE)')

SQLite 会在适当时机自动 checkpoint(WAL 文件超过 1000 页时),通常不需要手动触发。


常用 PRAGMA

PRAGMA 是 SQLite 的配置指令,用于调整运行时行为。

# 读取 PRAGMA 值
result = conn.execute('PRAGMA cache_size').fetchone()
print(result[0])

# 设置 PRAGMA 值
conn.execute('PRAGMA cache_size = -64000')  # 单位 KB(负数)

常用 PRAGMA 参数表

PRAGMA 默认值 说明
cache_size -2000(约 2 MB) 页缓存大小,负数为 KB,正数为页数,增大可提升读性能
synchronous NORMAL(WAL 模式)或 FULL 磁盘同步级别:OFF(最快,不安全)/NORMAL(推荐)/FULL(最安全,最慢)/EXTRA
foreign_keys OFF 外键约束:ON 开启,OFF 关闭(SQLite 默认不开启!)
temp_store DEFAULT(0) 临时表/索引存储位置:0=默认/1=文件/2=内存
journal_mode DELETE 日志模式:DELETE/WAL/MEMORY/OFF
mmap_size 0 内存映射文件大小(字节),0 关闭 mmap,大文件读取可开启
page_size 4096 数据库页大小(只能在创建时设置),常用 40968192
auto_vacuum NONE 自动整理碎片:NONE/FULL/INCREMENTAL
busy_timeout 0 等待锁的超时毫秒数,等价于 connect(timeout=...)
wal_autocheckpoint 1000 WAL 模式下自动 checkpoint 的页数阈值
# 推荐的生产配置(WAL 模式)
def configure_connection(conn: sqlite3.Connection) -> None:
    conn.execute('PRAGMA journal_mode=WAL')
    conn.execute('PRAGMA synchronous=NORMAL')
    conn.execute('PRAGMA foreign_keys=ON')
    conn.execute('PRAGMA cache_size=-64000')   # 64 MB 缓存
    conn.execute('PRAGMA temp_store=MEMORY')
    conn.execute('PRAGMA busy_timeout=5000')   # 等待锁 5 秒

内存数据库

传入 :memory: 作为数据库路径,创建完全在内存中的数据库,进程退出后数据消失。

import sqlite3

# 创建内存数据库
conn = sqlite3.connect(':memory:')

conn.execute('''
    CREATE TABLE users (
        id   INTEGER PRIMARY KEY,
        name TEXT NOT NULL,
        age  INTEGER
    )
''')
conn.execute("INSERT INTO users (name, age) VALUES ('Alice', 30)")
conn.commit()

result = conn.execute('SELECT * FROM users').fetchall()
print(result)  # [(1, 'Alice', 30)]

conn.close()  # 关闭后数据消失

内存数据库的适用场景

  • 单元测试:测试 ORM 模型和查询逻辑,不需要清理文件
  • 临时计算:将 CSV/JSON 数据加载进内存数据库后做 SQL 聚合
  • 缓存层:应用启动时从磁盘读取热数据到内存数据库加速查询

共享内存数据库(多连接访问同一内存库)

普通 :memory: 每次 connect() 都创建独立的内存库。要让多个连接访问同一内存库,使用 URI 格式:

# 使用 URI 创建共享内存数据库(需要 check_same_thread=False)
conn1 = sqlite3.connect('file:shared_mem?mode=memory&cache=shared', uri=True)
conn2 = sqlite3.connect('file:shared_mem?mode=memory&cache=shared', uri=True)
# conn1 和 conn2 访问同一内存数据库

内存数据库与磁盘数据库互导

# 从磁盘加载到内存(加速读取)
disk_conn = sqlite3.connect('app.db')
mem_conn = sqlite3.connect(':memory:')
disk_conn.backup(mem_conn)
disk_conn.close()
# 后续操作在 mem_conn 上进行,速度更快

# 将内存数据库保存到磁盘
mem_conn = sqlite3.connect(':memory:')
# ... 操作 mem_conn ...
disk_conn = sqlite3.connect('result.db')
mem_conn.backup(disk_conn)
disk_conn.close()
mem_conn.close()

SQLAlchemy 集成

同步连接字符串

from sqlalchemy import create_engine

# 相对路径
engine = create_engine('sqlite:///app.db')

# 绝对路径(Linux/macOS)
engine = create_engine('sqlite:////home/user/app.db')

# 绝对路径(Windows)
engine = create_engine(r'sqlite:///C:\Users\user\app.db')

# 内存数据库
engine = create_engine('sqlite://')         # 每次连接新建,适合测试
engine = create_engine('sqlite:///:memory:')  # 同上

# 共享内存数据库(多个连接访问同一内存库)
engine = create_engine(
    'sqlite:///:memory:',
    connect_args={'check_same_thread': False},
    poolclass=StaticPool   # 强制所有连接使用同一底层连接
)

异步:aiosqlite

pip install aiosqlite
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker

# aiosqlite 作为异步驱动
engine = create_async_engine('sqlite+aiosqlite:///app.db', echo=True)

AsyncSessionLocal = sessionmaker(
    bind=engine,
    class_=AsyncSession,
    expire_on_commit=False
)

async def get_users():
    async with AsyncSessionLocal() as session:
        result = await session.execute(select(User))
        return result.scalars().all()

直接使用 aiosqlite(不经过 SQLAlchemy):

import aiosqlite
import asyncio

async def main():
    async with aiosqlite.connect('app.db') as conn:
        conn.row_factory = aiosqlite.Row
        async with conn.execute('SELECT * FROM users') as cursor:
            async for row in cursor:
                print(dict(row))
        await conn.execute(
            'INSERT INTO users (name, age) VALUES (?, ?)',
            ('Alice', 30)
        )
        await conn.commit()

asyncio.run(main())

FastAPI 集成示例

from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from contextlib import asynccontextmanager

@asynccontextmanager
async def lifespan(app: FastAPI):
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    yield
    await engine.dispose()

app = FastAPI(lifespan=lifespan)

async def get_db():
    async with AsyncSessionLocal() as session:
        yield session

@app.get('/users')
async def list_users(db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(User))
    return result.scalars().all()

踩坑与注意事项

多线程写入需加锁

SQLite 同一时刻只允许一个写入者。默认设置 check_same_thread=True 时,跨线程使用同一连接直接报错。

如果需要多线程写入,有两种方案:

方案一:每个线程独立连接(推荐)

import threading
import sqlite3

_local = threading.local()

def get_conn():
    if not hasattr(_local, 'conn'):
        _local.conn = sqlite3.connect('app.db')
        _local.conn.execute('PRAGMA journal_mode=WAL')
    return _local.conn

def worker():
    conn = get_conn()  # 每个线程自己的连接
    with conn:
        conn.execute('INSERT INTO logs (msg) VALUES (?)', ('log',))

方案二:共享连接 + 互斥锁

import sqlite3
import threading

conn = sqlite3.connect('app.db', check_same_thread=False)
_lock = threading.Lock()

def write_log(msg: str):
    with _lock:
        with conn:
            conn.execute('INSERT INTO logs (msg) VALUES (?)', (msg,))

check_same_thread=False 的风险

设置 check_same_thread=False 关闭了线程检查,但 SQLite 连接对象本身不是线程安全的。如果多个线程在不加锁的情况下并发操作同一个连接,会导致数据库损坏或程序崩溃。设置此参数时,必须配合外部锁机制。

FastAPI + SQLAlchemy 中通常这样处理:

engine = create_engine(
    'sqlite:///app.db',
    connect_args={'check_same_thread': False}
)

SQLAlchemy 的连接池本身确保了每次请求使用独立连接,因此此处是安全的。

数值类型亲和性(动态类型)

SQLite 使用动态类型系统,列类型只是"亲和性(affinity)"提示,实际存储类型由值决定:

类型亲和性 列声明中包含 实际存储
INTEGER INT、INTEGER 整数存为整数
REAL REAL、FLOAT、DOUBLE 浮点存为 8 字节浮点
TEXT TEXT、CHAR、CLOB、VARCHAR 文本存为 UTF-8/16
BLOB BLOB 或无类型 按原始数据存储
NUMERIC 其他声明 尝试转为整数或浮点,否则存为文本
# 坑:SQLite 接受任意类型,不报错
conn.execute("CREATE TABLE t (val INTEGER)")
conn.execute("INSERT INTO t VALUES (?)", ('hello',))  # 不会报错!
row = conn.execute("SELECT val, typeof(val) FROM t").fetchone()
print(row)  # ('hello', 'text')

如果需要严格类型检查,需在应用层验证(使用 Pydantic、SQLAlchemy 类型等)。

ALTER TABLE 功能限制

SQLite 对 ALTER TABLE 的支持非常有限(SQLite 3.35.0 之前尤为严重):

操作 支持情况
ADD COLUMN 支持(有限制:不能有默认值表达式、不能是主键)
DROP COLUMN SQLite 3.35.0+ 支持(有限制)
RENAME COLUMN SQLite 3.25.0+ 支持
RENAME TABLE 支持
修改列类型 不支持
添加/删除约束 不支持

需要复杂的表结构变更时,通用方案是重建表:

with conn:
    conn.execute('CREATE TABLE users_new AS SELECT id, name FROM users')
    conn.execute('DROP TABLE users')
    conn.execute('ALTER TABLE users_new RENAME TO users')

外键默认关闭

SQLite 外键约束默认关闭,需要每次连接时手动开启:

conn = sqlite3.connect('app.db')
conn.execute('PRAGMA foreign_keys=ON')

这个设置不持久化,每次建立新连接都要重新执行。可以用 event 监听器自动设置(SQLAlchemy):

from sqlalchemy import event

@event.listens_for(engine, 'connect')
def set_sqlite_pragma(dbapi_conn, connection_record):
    cursor = dbapi_conn.cursor()
    cursor.execute('PRAGMA foreign_keys=ON')
    cursor.execute('PRAGMA journal_mode=WAL')
    cursor.close()

大量写入时的性能优化

# 单条插入(慢,每条是一个事务)
for row in rows:
    conn.execute('INSERT INTO t VALUES (?)', (row,))
    conn.commit()

# 批量插入(快,一个事务提交所有)
with conn:
    conn.executemany('INSERT INTO t VALUES (?)', [(row,) for row in rows])

# 更快:关闭同步写入(崩溃可能丢失数据,测试环境可用)
conn.execute('PRAGMA synchronous=OFF')
conn.execute('PRAGMA journal_mode=MEMORY')
with conn:
    conn.executemany('INSERT INTO t VALUES (?)', data)

最佳实践

所有写操作必须在事务内执行,提升性能的同时保证原子性:SQLite 的每条独立 INSERT/UPDATE 默认是独立事务(自动提交),磁盘 fsync 开销极大。用 with conn: 包裹批量操作,一次提交可提升写入速度 100 倍以上。

使用 WAL 模式提升并发读性能PRAGMA journal_mode=WAL 允许多个读者与一个写者并发运行,读操作不阻塞写操作,适合 Web 应用的读多写少场景。

为 WHERE、JOIN、ORDER BY 中的列创建索引:SQLite 没有自动统计,不会主动优化全表扫描。用 EXPLAIN QUERY PLAN 确认查询是否命中索引,未命中则手动创建。

生产环境不要在并发写入场景用 SQLite:SQLite 写操作是文件级锁,高并发写入时性能急剧下降。轻量应用(单用户桌面、嵌入式)适合 SQLite;多用户 Web 应用应使用 PostgreSQL 或 MySQL。


常见陷阱

陷阱:并发写入时抛出 database is locked

现象: 多线程或多进程同时写入 SQLite 时,频繁抛出 sqlite3.OperationalError: database is locked

原因: SQLite 写操作持有独占文件锁,其他写操作必须等待。默认超时时间(timeout)很短,多线程争用时容易超时。

解决: 设置较长的 timeoutconnect(db, timeout=30)),开启 WAL 模式,或将 SQLite 替换为支持并发的数据库。

陷阱:不用事务导致批量写入极慢

现象: 插入 1 万行数据需要数十秒,而同等数据量在 MySQL 只需不到 1 秒。

原因: 默认情况下每条 INSERT 是一个独立事务,每次提交都需要 fsync,磁盘 IO 是瓶颈。

解决:with conn: 上下文管理器将批量操作放在一个事务中。

陷阱:直接格式化 SQL 字符串导致 SQL 注入

现象: 代码中出现 f"SELECT * FROM users WHERE name = '{name}'" 的写法,用户输入可以改变 SQL 结构。

原因: 字符串拼接不对特殊字符(', ")转义,恶意输入可以闭合引号并注入任意 SQL。

解决: 始终用参数化查询(? 占位符),让驱动负责转义。

cursor.execute("SELECT * FROM users WHERE name = ?", (name,))

参见

阅读更多

Web 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

HTTP 协议深度指南

HTTP(HyperText Transfer Protocol)是 Web 的基础传输协议,基于 TCP/IP,采用请求/响应模型。 相关文档:Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性:多次执行相同请求,服务器状态结果相同。PUT /users/1 多次执行结果一致;POST /users 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog