拿到一个Django项目,第一件事不是急着写业务代码,而是把数据库这块地基夯实了。很多人觉得配置数据库不就是改改settings.py里的几行参数吗?真上手了才发现,光是数据库选型、连接池、时区、字符集这些细节,就能让新手折腾半天。我见过不少项目,代码写得挺漂亮,结果上线第一天数据库连接超时,或者中文乱码,一查全是配置埋的雷。所以这篇东西,咱们就照着从零开始的顺序,把Django配数据库这件事掰开揉碎了说清楚,每一步都给你讲明白为什么这么做,而不是光告诉你填什么。

先说你最关心的第一步:选哪种数据库。Django官方支持PostgreSQL、MySQL、SQLite、Oracle,但日常项目里九成用的是前两种。SQLite适合本地快速开发,零配置,文件即数据库,但生产环境并发一高就露怯,写入锁能卡死你。MySQL普及率高,文档多,团队招人容易,但要注意引擎选InnoDB,支持事务和外键,千万别用默认的MyISAM,那玩意连行级锁都没有。PostgreSQL功能最强,JSON字段、全文搜索、地理信息全是原生支持,Django官方文档都优先推荐它。我的建议是:个人项目或demo用SQLite,正式项目直接上PostgreSQL,别犹豫。如果你公司已经定了MySQL,那就老老实实用MySQL,但记得把连接参数里的事务隔离级别调成READ COMMITTED,不然高并发下容易出现不可重复读的诡异问题。
选好了库,接下来就是写配置。Django的settings.py里有个DATABASES字典,默认给你配好了SQLite,你要干的就是把它替换成自己的数据库信息。拿PostgreSQL举例,需要填ENGINE、NAME、USER、PASSWORD、HOST、PORT这几项。ENGINE填django.db.backends.postgresql,NAME是数据库名,USER和PASSWORD是数据库账号密码,HOST填数据库服务器IP,本地就填127.0.0.1,PORT填5432。这里有个坑:很多人把密码直接明文写在settings.py里,然后整个项目推到GitHub上,结果被爬虫扫描出来拖库。正确做法是用环境变量或者django-environ这类库来管理敏感信息,settings.py里只写os.environ.get('DBPASSWORD')这种调用。另外,数据库名别用连字符和中文,Django里的ORM生成SQL时会对表名做处理,但数据库名还是越规矩越好。
配置写完了,你以为就完事了?还早。数据库连接池这块,Django原生没实现,但生产环境必须得有。每次请求都新建一个数据库连接,开销极大,高并发下MySQL默认的151个连接数分分钟被打爆。推荐用django-db-connection-pool这个第三方库,它基于SQLAlchemy的连接池实现,配置也不复杂。在settings.py里把ENGINE换成djangodbconnectionpool.backends.postgresql,然后加一个DATABASEPOOLOPTIONS字典,设置POOLSIZE(连接池大小)和MAXOVERFLOW(最大超额连接数)。一般建议POOLSIZE设为CPU核心数乘以2,MAXOVERFLOW设为10左右。别贪多,连接池太大反而浪费内存,而且数据库那边也有最大连接数限制。配好之后,你会发现接口响应时间明显降下来了,这就是连接复用的威力。
再讲讲时区和字符集。Django的USETZ默认是True,意味着所有时间字段都会以UTC格式存储,显示的时候才转成你设置的TIMEZONE。这个设计本身没问题,但有个坑:如果你用MySQL,且历史数据是用本地时间存的,那迁移到Django后所有时间都乱套了。解决办法是迁移前把数据统一转成UTC,或者设置USETZ=False,但这会牺牲Django的时区处理能力,不建议。字符集这块,MySQL一定要在创建数据库时指定utf8mb4,别用utf8,因为utf8在MySQL里是utf8mb3,存不了emoji和生僻字,存进去就是问号。建库语句长这样:CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4unicodeci。PostgreSQL就没这烦恼,默认UTF8,但也要在settings.py里把OPTIONS里的clientencoding设成UTF8,防止连接时被环境变量干扰。
配置写完了,接下来就是跑迁移。python manage.py makemigrations生成迁移文件,python manage.py migrate执行迁移。这个过程中最常见的报错是数据库连接失败,定位方法很简单:先用数据库客户端工具(比如Navicat、DBeaver)测试连接,如果工具能连上但Django连不上,那问题肯定出在settings.py的配置参数上。对了,migrate之前记得先建好数据库,Django不会帮你自动创建数据库,它只负责建表。另外,如果你用的是MySQL且版本在8.0以下,还要装一个mysqlclient库,这个库编译依赖比较多,Windows上容易报错,建议直接用pip安装预编译的wheel包,或者换用PyMySQL,但PyMySQL在纯Python环境下性能稍差,能不用尽量不用。
实战里还有个高频需求:读写分离和分库分表。Django的DATABASES支持配置多个数据库,通过数据库路由(database router)来实现读写分离。比如你配了一个主库一个从库,写操作走主库,读操作走从库。实现方式是在settings.py里定义DATABASEROUTERS,然后写一个Router类,里面定义dbforread和dbforwrite方法,返回对应的数据库别名。这个模式在报表场景特别有用,主库压力大,报表查询全走从库,互不干扰。但要注意,从库有同步延迟,刚写入的数据可能读不到,所以对实时性要求高的操作要强制走主库,方法是在查询集上调用.using('default')。分库分表在Django里实现起来比较麻烦,需要借助第三方库如django-sharding-library,但一般中小项目用不上,真到了那个数据量,直接上TiDB或者云数据库更省心。
说一个很多人忽略的细节:连接超时和重试机制。数据库连接不是永久的,MySQL默认waittimeout是8小时,PostgreSQL是空闲事务超时5分钟。如果你的应用有长连接池,但池里的连接长时间空闲,数据库那边会主动断开,这时候你的代码再拿这个连接去查询,就会报"MySQL server has gone away"。解决办法有三个:一是把连接池的preping设为True,每次取连接时先ping一下,断了就重建;二是设置连接池的回收时间,比如recycle=3600,一小时强制回收一次;三是在Django的DATABASES配置里加CONNMAX_AGE,控制连接的最大存活时间。这三个配合使用,基本能杜绝连接失效问题。
写到这里,你会发现Django配置数据库其实是个系统工程,从选型到连接池,从字符集到读写分离,每一步都有讲究。很多人觉得配置这东西随便填填就行,真出问题了才后悔当初没认真对待。我的建议是,每配完一项就写个简单的查询测试一下,别等到整个项目跑起来了才发现数据库配置是错的,到时候排查成本翻倍。把这篇文章里的坑都避开了,你后续写业务代码就能专心在逻辑上,不用再为基础设施分心。数据库配置这事儿,看起来是体力活,但细节里全是经验,踩过坑的人自然懂。


