Django Read-Write Router
Django 读写分离 / Read-Write Separation 数据库路由器。主从复制场景下,写走主库、读走从库,支持事务感知与一致性哈希路由。
如果你在用 Django + PostgreSQL/MySQL 主从复制,需要把读请求分流到从库以减轻主库压力,或遇到「读己之写」不可见、复制滞后导致的数据不一致问题,这个包可以帮你快速接入读写分离。
适用场景 / When to Use
- 主从复制架构:主库负责写,从库负责读,降低主库负载
- 读多写少业务:大量查询走从库,写操作集中主库
- 复制滞后一致性:事务内读自动走主库(Read Your Writes)
- 单调读:同一用户多次读走同一从库,避免「时光倒流」
Features
- 基础读写分离:写走主库,读走从库
- 事务感知路由: 事务内读自动走主库(读己之写)
- 一致性哈希路由: 同一用户始终读同一从库(单调读)
- 请求上下文追踪: 中间件自动管理请求上下文
- 主库 QuerySet: 可强制指定查询走主库
Installation
pip install django-rw-router
Or with uv:
uv add django-rw-router
Quick Start
1. 配置数据库
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydb',
'USER': 'user',
'PASSWORD': 'password',
'HOST': 'primary.db.example.com',
'PORT': '5432',
},
'readonly': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydb',
'USER': 'readonly_user',
'PASSWORD': 'password',
'HOST': 'replica.db.example.com',
'PORT': '5432',
},
}
2. 添加路由器
# settings.py
DATABASE_ROUTERS = [
'django_rw_router.routers.TransactionPrimaryReplicaRouter',
]
3. 添加中间件(HashPrimaryReplicaRouter 必选)
# settings.py
MIDDLEWARE = [
'django_rw_router.middleware.RequestContextMiddleware',
# ... 其他中间件
]
Router Options
| 路由器 | 场景 | 一致性 |
|---|---|---|
PrimaryReplicaRouter |
基础读写分离 | 最终一致 |
TransactionPrimaryReplicaRouter |
写后读 | Read Your Writes |
HashPrimaryReplicaRouter |
同一用户多次读 | Monotonic Reads |
PrimaryReplicaRouterWithCtx |
追踪写操作上下文 | - |
PrimaryReplicaRouter
基础读写分离。写走主库,读在从库间随机分配。
DATABASE_ROUTERS = [
'django_rw_router.routers.PrimaryReplicaRouter',
]
TransactionPrimaryReplicaRouter(推荐)
事务内读写均走主库,避免复制滞后导致的"读己之写"不可见。
DATABASE_ROUTERS = [
'django_rw_router.routers.TransactionPrimaryReplicaRouter',
]
HashPrimaryReplicaRouter
基于 user_id 的一致性哈希,同一用户始终读同一从库,保证单调读。
DATABASE_ROUTERS = [
'django_rw_router.routers.hash.HashPrimaryReplicaRouter',
]
# 需启用 RequestContextMiddleware 以注入 user_id
PrimaryReplicaRouterWithCtx
在基础路由器上增加写操作上下文追踪,供上层逻辑判断。
DATABASE_ROUTERS = [
'django_rw_router.routers.PrimaryReplicaRouterWithCtx',
]
一致性场景 (DDIA)
| 场景 | 含义 | 推荐路由器 |
|---|---|---|
| Read Your Writes | 写入后立即读可见 | TransactionPrimaryReplicaRouter |
| Monotonic Reads | 同一用户不出现时光倒流 | HashPrimaryReplicaRouter |
| Consistent Prefix | 有序读取不出现乱序 | HashPrimaryReplicaRouter |
| Eventual Consistency | 接受复制滞后 | PrimaryReplicaRouter |
Configuration
数据库别名
# settings.py
# 读从库别名(单个或列表)
DJANGO_RW_ROUTER_READ_DBS = ['readonly', 'readonly2']
# 写主库别名
DJANGO_RW_ROUTER_WRITE_DB = 'default'
# 一致性哈希虚拟节点数(HashPrimaryReplicaRouter)
DJANGO_RW_ROUTER_HASH_VIRTUAL_NODES = 40
中间件配置
# settings.py
# 从 request 提取 user_id 的路径(支持嵌套,如 user.id)
DJANGO_RW_ROUTER_USER_ID_ATTR = 'user.id'
# 从 request 提取 request_id 的路径
DJANGO_RW_ROUTER_REQUEST_ID_ATTR = 'id'
QuerySet 配置
# settings.py
# 强制 PrimaryQuerySet 所有读走主库
DJANGO_RW_ROUTER_QUERYSET_USING_ENABLE = True
# 启用 @use_primary_db 装饰器
DJANGO_RW_ROUTER_METHOD_USING_ENABLE = True
Usage Examples
Basic Usage
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
# Writes go to 'default', reads go to 'readonly'
book = Book.objects.create(title="Django Guide") # Write to primary
books = Book.objects.all() # Read from replica
Transaction-Aware Reads
from django.db import transaction
with transaction.atomic():
book = Book.objects.create(title="New Book")
# This read goes to PRIMARY (not replica) because we're in a transaction
fresh_book = Book.objects.get(id=book.id)
Using PrimaryManager for Critical Queries
from django.db import models
from django_rw_router.managers import PrimaryManager
class ImportantModel(models.Model):
objects = PrimaryManager()
# When DJANGO_RW_ROUTER_QUERYSET_USING_ENABLE=True,
# all reads through this manager go to primary
obj = ImportantModel.objects.first()
Manual Database Selection
You can always override the router:
# Force read from primary
obj = Book.objects.using('default').first()
# Force write to replica (not recommended)
book.save(using='readonly')
How It Works
- 写操作:始终路由到主库 (
default) - 读操作:
- 事务外:路由到随机从库
- 事务内:路由到主库(避免读己之写不可见)
- Hash 路由:
user_id哈希后稳定选中同一从库 - 上下文:中间件在请求开始时注入、结束时清理
Testing
# uv
uv run pytest -v
# pip
pip install -e ".[dev]"
pytest -v
测试覆盖 context、routers、managers、middleware,以及 DDIA 四种一致性场景。
Requirements
- Python >= 3.8
- Django >= 3.2
- uhashring >= 2.1
常见搜索关键词
Django 读写分离、Django read write separation、Django database router、主从复制、primary replica、read replica、Django 主从、Django 从库读、Django 读写分离中间件
License
MIT License
Contributing
欢迎提交 Pull Request。
Metadata
Release files for django-rw-router 0.0.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| django_rw_router-0.0.4.tar.gz | 59.8 kB | Without provenance | Details for django_rw_router-0.0.4.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| django_rw_router-0.0.4-py3-none-any.whl | Python 3 | none | any | Details for django_rw_router-0.0.4-py3-none-any.whl |
Total release size: 73.9 kB
Release files / django_rw_router-0.0.4.tar.gz
| Download URL | django_rw_router-0.0.4.tar.gz |
|---|---|
| Size | 59.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6ff77c85374983152a36751bc19351a4ea75afccfe49dc47b08c8b890848de24
|
|
BLAKE2b-256 checksum How to use checksums |
628d11155b117301af1e58717a00ada1496a523db14a3d4c589f56ac2a7563fb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.2
|
Release files / django_rw_router-0.0.4-py3-none-any.whl
| Download URL | django_rw_router-0.0.4-py3-none-any.whl |
|---|---|
| Size | 14.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fd4b6da0bf6ef35729d42ad96f382b77cbc0a34aad3568d8dfa0669fecb00708
|
|
BLAKE2b-256 checksum How to use checksums |
5e2634af3bf964fde5f5b25f84d8463d0fc6a0f7bcedaf6c8fda566833559bca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.2
|
Security for django-rw-router 0.0.4
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.