Django 2.1.3 文件-模型層 支援的資料庫
資料庫
- 1. 一般注意事項
- 2. Mysql注意事項
Django試圖在所有資料庫後端上支援儘可能多的功能。但是,並非所有資料庫後端都是相似的,我們必須就哪些功能需要支援以及我們可以安全地做出哪些假設做出設計決策。
該檔案描述了可能與Django使用相關的一些功能。當然,它不能替代伺服器特定的文件或參考手冊。
1. 一般注意事項
1.1 持久連線
持久連線避免了在每個請求中重新建立與資料庫的連線的開銷。它們由CONN_MAX_AGE定義連線最大生命週期的引數控制 。可以為每個資料庫單獨設定。
預設值為0,保留在每個請求結束時關閉資料庫連線的歷史行為。要啟用持久連線,請設定CONN_MAX_AGE為
正數秒。對於無限制的持久連線,請將其設定為None
。
1.2 連線管理
Django在首次進行資料庫查詢時會開啟與資料庫的連線。它保持此連線開啟並在後續請求中重用它。一旦超過定義的最大時限 (CONN_MAX_AGE
詳細地說,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()覆蓋,你應該確保你進行適當的型別轉換。