数据库
Django试图在所有数据库后端上支持尽可能多的功能。但是,并非所有数据库后端都是相似的,我们必须就哪些功能需要支持以及我们可以安全地做出哪些假设做出设计决策。
该文件描述了可能与Django使用相关的一些功能。当然,它不能替代服务器特定的文档或参考手册。
1. 一般注意事项
1.1 持久连接
持久连接避免了在每个请求中重新建立与数据库的连接的开销。它们由CONN_MAX_AGE定义连接最大生命周期的参数控制 。可以为每个数据库单独设置。
默认值为0,保留在每个请求结束时关闭数据库连接的历史行为。要启用持久连接,请设置CONN_MAX_AGE为
正数秒。对于无限制的持久连接,请将其设置为None
。
1.2 连接管理
Django在首次进行数据库查询时会打开与数据库的连接。它保持此连接打开并在后续请求中重用它。一旦超过定义的最大时限 (CONN_MAX_AGE
)或不再可用时Django会关闭连接 。
详细地说,Django会在需要时自动打开与数据库的连接,并且不会预备一个连接 - 因为这是第一个连接,或者因为先前的连接已关闭。
在每个请求开始时,如果连接达到其最大时限,Django将关闭连接。如果数据库在一段时间后终止空闲连接,则应设置CONN_MAX_AGE
为较低的值,以便Django不会尝试使用已由数据库服务器终止的连接。(此问题可能只会影响非常低流量的网站。)
在每个请求结束时,Django会在连接达到最大时限或者处于不可恢复的错误状态时关闭连接。如果在处理请求时发生任何数据库错误,Django会检查连接是否仍然有效,如果没有则关闭它。因此,数据库错误最多只影响一个请求; 如果连接变得不可用,则下一个请求将获得新连接。
1.3 警告
由于每个线程都维护自己的连接,因此您的数据库必须至少支持与工作线程一样多的同时连接。
有时,大多数视图都不会访问数据库,例如,因为它是外部系统的数据库,或者归功于缓存。在这种情况下,您应该设置CONN_MAX_AGE
为低值或甚至 0,因为维护不太可能重用的连接没有意义。这将有助于保持与此数据库的同时连接数量。
开发服务器为它处理的每个请求创建一个新线程,降低持久连接的影响。在开发过程中不要启用它们。
当Django建立与数据库的连接时,它会根据所使用的后端设置适当的参数。如果启用持久连接,则不会再对每个请求重复此设置。如果修改连接的隔离级别或时区等参数,则应在每个请求结束时恢复Django的默认值,在每个请求开始时强制使用适当的值,或者禁用持久连接。
1.4 编码
Django假设所有数据库都使用UTF-8编码。使用其他编码可能会导致意外行为,例如对于在Django中有效的数据值,但是在数据库中出现“值太长”错误。有关如何正确设置数据库的信息,请参阅下面的数据库特定说明。
2. Mysql注意事项
2.1 版本支持
Django支持MySQL 5.6及更高版本。
Django的inspectdb
功能用到了information_schema数据库(此库包含了有关所有数据库模式的详细数据)。
Django希望数据库支持Unicode(UTF-8编码),并将执行事务和参照完整性的任务委托给它。要注意这两个点在使用MySQL的MyISAM存储引擎时并没有强制执行,请参阅下一节。
2.2 存储引擎
MySQL有几个存储引擎。您可以在服务器配置中更改默认存储引擎。
MySQL的默认存储引擎是InnoDB。此引擎是完全事务性的,并支持外键引用。这是推荐的选择。但是,InnoDB自动增量计数器在MySQL重启时丢失,因为它不记得AUTO_INCREMENT
值,而是将其重新创建为max(id)+1
。这可能导致无意中重用AutoField 值。
MyISAM的主要缺点是它不支持事务或强制执行外键约束。
2.3 MySQL DB API驱动程序
MySQL有几个驱动程序实现了Python中描述的数据库API PEP 249:
- mysqlclient是一个本地驱动程序。这是推荐的选择。
- MySQL Connector / Python 是Oracle的纯Python驱动程序,不需要MySQL客户端库或标准库之外的任何Python模块。
这些驱动程序是线程安全的,并提供连接池。
除了DB API驱动程序之外,Django还需要一个适配器来从其ORM访问数据库驱动程序。Django为mysqlclient提供了一个适配器,而MySQL Connector / Python包含了它自己的适配器。
2.3.1 mysqlclient
Django需要mysqlclient 1.3.7或更高版本。
2.3.2 MySQL Connector/Python
MySQL Connector / Python可从下载页面获得。Django适配器在1.1.X及更高版本中可用。它可能不支持最新版本的Django。
2.4 时区定义
如果您打算使用Django的 时区支持,请使用 mysql_tzinfo_to_sql 将时区表加载到MySQL数据库中。这只需要为MySQL服务器做一次,而不是每个数据库。
2.5 创建数据库
您可以使用命令行工具和此SQL 创建数据库:
CREATE DATABASE <dbname> CHARACTER SET utf8;
这可确保默认情况下所有表和列都将使用UTF-8。
2.5.1 字符集排序规则的设置
列的排序规则设置控制数据的排序顺序以及比较字符串相等。它可以在数据库范围内设置,也可以在每个表和每列上设置。这在MySQL文档中有详细记录。在所有情况下,您可以通过直接操作数据库表来设置排序规则; Django没有提供在模型定义上设置它的方法。
默认情况下,使用UTF-8数据库,MySQL将使用 utf8_general_ci
排序规则。这导致所有字符串相等性比较以不区分大小写的方式完成。也就是说,Fred
和freD
被认为是在数据库级别相等。如果你有一个字段的唯一约束,将aa
和 AA
放到到同一列将是非法的尝试,因为它们的比较结果在默认排序规则下相等(and, hence,非唯一)。如果要对特定列或表进行区分大小写的比较,请更改列或表以使用 utf8_bin
排序规则。
请注意,根据MySQL Unicode字符集,在比较方面,utf8_general_ci
比utf8_unicode_ci
更快,但正确性稍差。如果您的应用程序可以接受,那么您应该使用utf8_general_ci
,因为它更快。如果这是不可接受的(例如,如果您需要德语字典顺序),请使用utf8_unicode_ci
,因为它更准确。
警告
模型formsets以区分大小写的方式验证唯一字段。因此,当使用不区分大小写的排序规则时,具有唯一字段值的formset将仅通过大小写进行验证,但在调用save()
时, 将引发一个IntegrityError
。
2.6 连接数据库
请参阅 设置文档。
连接设置按此顺序使用:
(1)OPTIONS
(2)NAME,USER,PASSWORD, HOST,PORT
(3)MySQL选项文件。
换句话说,如果您设置数据库的名称OPTIONS,这将优先于NAME,它将覆盖MySQL选项文件中的任何内容。
以下是使用MySQL选项文件的示例配置:
# my.cnf
[client]
database = NAME
user = USER
password = PASSWORD
default-character-set = utf8
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'OPTIONS': {
'read_default_file': '/path/to/my.cnf',
},
}
}
几个其他 MySQLdb的连接选项 可能是有用的,例如ssl, init_command,和sql_mode
。
2.6.1 设定sql_mode
从MySQL 5.7开始以及MySQL 5.6的全新安装,该sql_mode选项的默认值包含STRICT_TRANS_TABLES
。该值得意思是 当数据被插入时如果造成截断会报错,所以Django的强烈建议为MySQ激活 严格模式 以防止数据丢失(无论是 STRICT_TRANS_TABLES
或STRICT_ALL_TABLES
)。
如果需要自定义SQL模式,可以像其他MySQL选项一样设置变量sql_mode
:在配置文件中或在 DATABASES
配置的OPTIONS
部分中增加条目:'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"
2.7 隔离级别
运行并发加载时,来自不同会话的数据库事务(例如,处理不同请求的单独线程)可能会相互交互。这些交互受每个会话的事务隔离级别的影响。您可以在OPTIONS
部分中增加条目'isolation_level'
来设置连接的隔离级别 。此条目的有效值是四个标准隔离级别:
- ‘read uncommitted’
- ‘read committed’
- ‘repeatable read’
- ‘serializable’
或None,使用Mysql服务器配置的隔离级别。但是,Django最适合使用的默认设置应该是 read committed 而不是MySQL的默认设置(repeatable read)。repeatable read 可能造成丢失数据。
在Django 2.0中更改:
在旧版本中,MySQL数据库后端默认使用数据库的隔离级别(默认为repeatable read)而不是read committed 。
2.8 创建表
当Django生成数据库时,它不指定存储引擎,因此将使用数据库服务器配置的任何默认存储引擎创建表。最简单的解决方案是将数据库服务器的默认存储引擎设置为所需的引擎。
如果您正在使用托管服务而无法更改服务器的默认存储引擎,那么您有几个选择。
(1)创建表后,执行ALTER TABLE
语句将表转换为新的存储引擎(如InnoDB):
ALTER TABLE <tablename> ENGINE=INNODB;
(2)如果你有很多表,这可能很乏味。
另一个选择是在创建表之前使用MySQLdb 的选项init_command
:
'OPTIONS': {
'init_command': 'SET default_storage_engine=INNODB',
}
这会在连接到数据库时设置默认存储引擎。创建表后,应删除此选项,因为它只在表创建期间向每个数据库连接添加一个查询。
2.9 表名
甚至在最新版本的MySQL中也存在已知问题,这些问题可能导致在某些条件下执行某些SQL语句时更改表名称的情况。如果可能,建议您使用小写表名,以避免此行为可能引起的任何问题。Django在从模型中自动生成表名时使用小写表名,因此如果您通过 db_table 参数覆盖表名,这主要是考虑因素。
2.10 保存点
Django ORM和MySQL(使用InnoDB 存储引擎时)都支持数据库保存点。
如果您使用MyISAM存储引擎,请注意如果您尝试使用事务API与保存点相关的方法,您将收到数据库生成的错误。这样做的原因是检测MySQL数据库/表的存储引擎是一项昂贵的操作,因此根据这种检测结果确定在no-op中动态转换这些方法是不值得的。
2.11 关于特殊字段的说明
2.11.1 字符字段
如果字段使用了unique=True
,并且用来存储则VARCHAR
列类型,那么max_length会限制为255个字符。这会影响CharField, SlugField。
2.11.2 TextField 限制
MySQL只能索引一个BLOB或TEXT列的前N个字符。由于 TextField没有定义的长度,因此无法将其标记为 unique=True。MySQL将报告:“BLOB/TEXT column ‘<db_column>’ used in key specification without a key length”.
。
2.11.3 时间和日期时间字段的小数秒支持
MySQL 5.6.4及更高版本可以存储小数秒,前提是列定义包含小数指示(例如DATETIME(6)
)。早期版本根本不支持它们。
如果数据库服务器支持,Django不会升级现有列以包含小数秒。如果要在现有数据库上启用它们,则可以通过执行以下命令来手动更新目标数据库上的列:
ALTER TABLE `your_table` MODIFY `your_datetime_column` DATETIME(6)
2.11.4 TIMESTAMP列
如果您使用的是包含TIMESTAMP列的旧数据库,为避免数据损坏必须设置USE_TZ = False。 inspectdb 将这些列映射到DateTimeField ,如果启用时区支持,MySQL和Django都会尝试将值从UTC转换为本地时间。
2.11.5 QuerySet.select_for_update()上的行锁定
MySQL不支持在SELECT ... FOR UPDATE
语句上的NOWAIT,SKIP LOCKED,和OF
选项。如果select_for_update()
与nowait=True
和skip_locked=True
一起使用,或者of,将会引发NotSupportedError
。
2.11.6 自动类型转换会导致意外结果
在对字符串类型执行查询但使用整数值时,MySQL会在执行比较之前将表中所有值的类型强制转换为整数。如果您的表包含值'abc'
,'def'
并且你的查询是WHERE mycolumn=0
,则两个行都将匹配。同样,WHERE mycolumn=1
将匹配'abc1'
。因此,Django中包含的字符串类型字段将始终在将值用于查询之前将其强制转换为字符串。
如果您实现一个自定义模型字段并继承自Field,会被 get_prep_value(), RawSQL, extra()或 raw()覆盖,你应该确保你进行适当的类型转换。