開發者必讀
此部分對于 ORM
的學習和使用非常重要。遇到使用問題時,請先確保自己認真看完了 ORM
組件的所有使用文檔,在對問題進行排查后,實在不能解決時再進行提問和反饋。
設計思想
ORM
全稱:object relational mapping
,目的是想像操作對象一樣去操作數據庫,是符合面向對象開發思想的。
如將一條數據的插入,映射成一個對象的實例化。偽代碼如下所示:
<?php
$user = new UserModel();
$user->setData([
'attr' => 'value'
]);
$user->insert();
常見問題匯總
重復使用 Model
對象
一個對象映射一條數據,此時會有很多用習慣了 db
封裝組件的小伙伴把 Model
當成了 db
封裝使用,重復調用一個 Model
對象,如下所示:
<?php
// 錯誤使用
// 假設id自增
$user = new UserModel();
// 插入一條新用戶
$user->setData([
'attr' => 'value'
]);
$user->insert();
// 插入第二條新用戶,此時由于重復調用同一個對象,產生報錯,自增 id 主鍵重復
$user->setData([
'attr' => 'value2'
]);
$user->insert();
ORM
生成復雜 sql
-
ORM
是基于mysqli 3.x
組件實現,內部引用mysqli
組件中的QueryBuilder 類
完成sql
的構造,并且在QueryLimit
章節 注明了閉包函數使用方式(可直接使用mysqli
組件中的絕大部分連貫操作,如having
等特殊條件) -
如果
mysqli
組件的連貫操作也無法滿足您的需求,您有以下幾種方式解決該問題:- 使用自定義
sql
執行 - 嘗試給組件貢獻代碼,新增功能特性
- 提出反饋,我們會在精力允許和大眾所需時,對組件進行維護升級
- 使用自定義
優雅刪除數據
在 ORM
設計思想中,對數據的操作映射為對對象的操作,如果按照此原則,那么需要我們先查詢出對象,然后再調用對象的 delete()
方法進行刪除。
但是對于執行效率的消耗來說,此次查詢在部分業務場景下是無用的。
那么我們到底是否需要遵守設計原則?一般情況下是在 操作前需要校驗數據是否存在
時遵守,無需校驗則直接根據刪除條件傳參刪除即可。
設計原則代表思想,在某些場景下遵守它需要付出一定代價,開發者可以根據喜好去決定是否遵守。
刪除數據偽代碼如下所示:
<?php
$user = (new UserModel())->get($param['id']);
if (!$user){
return '操作數據不存在,請檢查再試';
}
$res = $user->delete();
連接預熱
為了避免連接空檔期突如其來的高并發,我們可以對數據庫連接預熱,也就是 Worker
進程啟動的時候,提前準備好數據庫連接。
具體使用可查看 連接預熱 章節
斷線問題
為什么會斷線?
在連接池模式下,一個連接創建后,并不會因為因為請求結束而斷開,就好比 php-fpm
下的 pconnect
特性一樣。而一個連接建立后,可能會因為太久沒有使用(執行 sql
),而被 mysql
服務端主動斷開了連接,或者是因為鏈路問題,切斷了連接。而連接被切斷的時候。我們并不知道這件事。因此就導致了我們用了一個斷線的數據庫連接去執行 sql
,從而出現斷線錯誤或者異常。
如何解決短線問題?
與 java
全家桶的原理一致,我們需要做的事情就是:
- 定時檢查連接是否可用
- 定時檢查連接的最后一次使用狀態
因此在 EasySwoole
的 ORM
中,ORM
組件提供了 IntervalCheckTime
配置項,它指定的就是多久做一次周期檢查,MaxIdleTime
配置項指的是如果一個連接超過這個時間沒有使用,則會被回收。AutoPing
配置項指的是多久執行一個 select 1
用來觸發這個連接,讓這個連接被 mysql
服務端標記為活躍而不會被回收。如果經常出現斷線,可以適當縮短 周期性檢查
和 AutoPing
的時間(即調整 IntervalCheckTime
、AutoPing
配置項的值)。
百分百不會斷線了?
理論上,做了上面的步驟,出現使用斷線連接的概率是非常低的,但是并不是真的就百分百穩了,比如極端情況:mysql
服務重啟,或者是鏈路斷線了。因此,我們一定要做類似如下這樣的處理措施:
<?php
try {
$client = $pool->getClient();
$cilient->query(xxxxxx);
} catch (\Throwable $t) {}
也就是說,任何 orm
的使用,一定要 try
。至于為何,請參考 java
為何強制對任何數據庫 io
做 try
處理。
為何不能做自動重連
我們可以看到,在某些自以為很聰明的框架中,有下面這樣的操作:
<?php
$client = $pool->getClient();
try {
return $client->query();
} catch(\Throwable $t) {
// 2006 2002 為斷線
if ($client->getError() == '2006') {
$client->connect();
return $client->query();
} else {
throw $t;
}
}
乍一看,沒有什么問題。實際上,按照上面的重連,我們來看看下面的偽代碼:
<?php
$client = $pool->getClient();
$client->startTransaction();
$client->query(query one);
// client disconnect case network
$client->reconnect();
$client->query(query two);
$client->commit();
這樣,在極端情況下,會導致 query one
結果丟失,但是 query two
卻執行了,這對于事務來說,是不可原諒的。此刻又會有人說,那我判斷下鏈接是不是在事務中不就好了。實際上,遠遠沒這么簡單。為此,最好的方式就是我們養成良好的習慣。任何的數據庫 io
,都做 try
操作,與 java
一致。