跳轉至內容

資料庫:入門

簡介

幾乎每個現代 Web 應用程式都需要與資料庫互動。Laravel 透過原生 SQL、流暢的查詢構建器以及 Eloquent ORM,使得與各種受支援的資料庫進行互動變得極其簡單。目前,Laravel 為以下五種資料庫提供了官方支援:

此外,MongoDB 透過 mongodb/laravel-mongodb 包提供支援,該包由 MongoDB 官方維護。有關更多資訊,請檢視 Laravel MongoDB 文件。

配置

Laravel 資料庫服務的配置位於應用程式的 config/database.php 配置檔案中。在該檔案中,你可以定義所有的資料庫連線,並指定預設使用的連線。此檔案中的大多數配置選項都由應用程式的環境變數驅動。檔案中提供了 Laravel 支援的多數資料庫系統的示例。

預設情況下,Laravel 的示例 環境變數配置已適配 Laravel Sail,這是一種用於在本地機器上開發 Laravel 應用程式的 Docker 配置。當然,你可以根據需要隨時修改本地資料庫配置。

SQLite 配置

SQLite 資料庫儲存在檔案系統中的單個檔案中。你可以透過終端的 touch 命令建立一個新的 SQLite 資料庫:touch database/database.sqlite。建立資料庫後,只需將 DB_DATABASE 環境變數設定為該資料庫的絕對路徑,即可輕鬆配置環境變數。

1DB_CONNECTION=sqlite
2DB_DATABASE=/absolute/path/to/database.sqlite

預設情況下,SQLite 連線已啟用外部索引鍵約束。如果你想停用它們,可以將 DB_FOREIGN_KEYS 環境變數設定為 false

1DB_FOREIGN_KEYS=false

如果你使用 Laravel 安裝程式建立 Laravel 應用程式並選擇了 SQLite 作為資料庫,Laravel 會自動為你建立 database/database.sqlite 檔案並執行預設的 資料庫遷移

Microsoft SQL Server 配置

要使用 Microsoft SQL Server 資料庫,你需要確保已安裝 sqlsrvpdo_sqlsrv PHP 擴充套件,以及它們可能需要的依賴項,例如 Microsoft SQL ODBC 驅動程式。

使用 URL 配置

通常,資料庫連線是使用多個配置值(如 hostdatabaseusernamepassword 等)來配置的。這些配置值中的每一個都有其對應的環境變數。這意味著在生產伺服器上配置資料庫連線資訊時,你需要管理多個環境變數。

一些託管資料庫服務提供商(如 AWS 和 Heroku)提供了一個單一的資料庫“URL”,其中包含單個字串形式的所有連線資訊。一個數據庫 URL 示例可能如下所示:

1mysql://root:[email protected]/forge?charset=UTF-8

這些 URL 通常遵循標準的模式約定:

1driver://username:password@host:port/database?options

為方便起見,Laravel 支援使用這些 URL 作為配置資料庫的多項選項的替代方案。如果存在 url(或對應的 DB_URL 環境變數)配置選項,它將被用於提取資料庫連線和憑據資訊。

讀寫分離連線

有時你可能希望將一個數據庫連線用於 SELECT 語句,將另一個連線用於 INSERT、UPDATE 和 DELETE 語句。Laravel 讓這一切變得非常簡單,無論你是使用原生查詢、查詢構建器還是 Eloquent ORM,系統都會自動使用正確的連線。

要了解如何配置讀/寫連線,請看這個例子:

1'mysql' => [
2 'driver' => 'mysql',
3 
4 'read' => [
5 'host' => [
6 '192.168.1.1',
7 '196.168.1.2',
8 ],
9 ],
10 'write' => [
11 'host' => [
12 '192.168.1.3',
13 ],
14 ],
15 'sticky' => true,
16 
17 'port' => env('DB_PORT', '3306'),
18 'database' => env('DB_DATABASE', 'laravel'),
19 'username' => env('DB_USERNAME', 'root'),
20 'password' => env('DB_PASSWORD', ''),
21 'unix_socket' => env('DB_SOCKET', ''),
22 'charset' => env('DB_CHARSET', 'utf8mb4'),
23 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
24 'prefix' => '',
25 'prefix_indexes' => true,
26 'strict' => true,
27 'engine' => null,
28 'options' => extension_loaded('pdo_mysql') ? array_filter([
29 (PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
30 ]) : [],
31],

注意,配置陣列中添加了三個鍵:readwritestickyreadwrite 鍵的值是包含單個鍵 host 的陣列。讀寫連線的其他資料庫選項將從主要的 mysql 配置陣列中合併。

只有當你希望覆蓋主要 mysql 陣列中的值時,才需要在 readwrite 陣列中新增項。因此,在這種情況下,192.168.1.1 將被用作“讀”連線的主機,而 192.168.1.3 將被用作“寫”連線的主機。資料庫憑據、字首、字元集以及主要 mysql 陣列中的所有其他選項將在兩個連線之間共享。當 host 配置陣列中存在多個值時,每次請求都會隨機選擇一個數據庫主機。

sticky 選項

sticky 選項是一個*可選*值,可用於允許在當前請求週期內讀取剛剛寫入資料庫的記錄。如果啟用了 sticky 選項,並且在當前請求週期內已對資料庫執行了“寫”操作,則後續的任何“讀”操作都將使用“寫”連線。這確保了在請求週期內寫入的任何資料都可以在同一個請求中立即從資料庫讀取。是否需要此行為由你決定。

執行 SQL 查詢

配置好資料庫連線後,你可以使用 DB 門面來執行查詢。DB 門面為每種查詢型別提供了相應的方法:selectupdateinsertdeletestatement

執行 Select 查詢

要執行基本的 SELECT 查詢,可以使用 DB 門面上的 select 方法:

1<?php
2 
3namespace App\Http\Controllers;
4 
5use Illuminate\Support\Facades\DB;
6use Illuminate\View\View;
7 
8class UserController extends Controller
9{
10 /**
11 * Show a list of all of the application's users.
12 */
13 public function index(): View
14 {
15 $users = DB::select('select * from users where active = ?', [1]);
16 
17 return view('user.index', ['users' => $users]);
18 }
19}

傳遞給 select 方法的第一個引數是 SQL 查詢,第二個引數是需要繫結到查詢的引數繫結。通常,這些是 where 子句約束的值。引數繫結提供了針對 SQL 注入的保護。

select 方法將始終返回一個結果 array。陣列中的每個結果都將是一個代表資料庫記錄的 PHP stdClass 物件:

1use Illuminate\Support\Facades\DB;
2 
3$users = DB::select('select * from users');
4 
5foreach ($users as $user) {
6 echo $user->name;
7}

選擇標量值

有時你的資料庫查詢可能只產生一個標量值。Laravel 允許你使用 scalar 方法直接檢索該值,而無需從記錄物件中提取:

1$burgers = DB::scalar(
2 "select count(case when food = 'burger' then 1 end) as burgers from menu"
3);

選擇多個結果集

如果你的應用程式呼叫返回多個結果集的儲存過程,可以使用 selectResultSets 方法來檢索儲存過程返回的所有結果集:

1[$options, $notifications] = DB::selectResultSets(
2 "CALL get_user_options_and_notifications(?)", $request->user()->id
3);

使用命名繫結

除了使用 ? 來表示引數繫結外,你還可以使用命名繫結來執行查詢:

1$results = DB::select('select * from users where id = :id', ['id' => 1]);

執行 Insert 語句

要執行 insert 語句,可以使用 DB 門面上的 insert 方法。與 select 一樣,該方法接受 SQL 查詢作為第一個引數,繫結作為第二個引數:

1use Illuminate\Support\Facades\DB;
2 
3DB::insert('insert into users (id, name) values (?, ?)', [1, 'Marc']);

執行 Update 語句

update 方法應僅用於更新資料庫中的現有記錄。該方法會返回語句影響的行數:

1use Illuminate\Support\Facades\DB;
2 
3$affected = DB::update(
4 'update users set votes = 100 where name = ?',
5 ['Anita']
6);

執行 Delete 語句

delete 方法應僅用於刪除資料庫中的記錄。與 update 一樣,該方法會返回受影響的行數:

1use Illuminate\Support\Facades\DB;
2 
3$deleted = DB::delete('delete from users');

執行通用語句

某些資料庫語句不返回任何值。對於這些型別的操作,你可以使用 DB 門面上的 statement 方法:

1DB::statement('drop table users');

執行未經預處理的語句

有時你可能希望執行一條不繫結任何值的 SQL 語句。你可以使用 DB 門面的 unprepared 方法來實現:

1DB::unprepared('update users set votes = 100 where name = "Dries"');

由於未經預處理的語句不繫結引數,因此它們可能容易受到 SQL 注入攻擊。你不應在未經預處理的語句中包含任何使用者可控的值。

隱式提交

在事務中使用 DB 門面的 statementunprepared 方法時,必須小心避免會導致 隱式提交 的語句。這些語句會導致資料庫引擎間接地提交整個事務,從而導致 Laravel 無法知曉資料庫的事務級別。此類語句的一個例子是建立資料庫表:

1DB::unprepared('create table a (col varchar(1) null)');

請參閱 MySQL 手冊以獲取 所有觸發隱式提交的語句列表

使用多個數據庫連線

如果你的應用程式在 config/database.php 配置檔案中定義了多個連線,你可以透過 DB 門面提供的 connection 方法訪問每個連線。傳遞給 connection 方法的連線名稱應對應於 config/database.php 配置檔案中列出的連線名稱,或在執行時使用 config 輔助函式配置的名稱:

1use Illuminate\Support\Facades\DB;
2 
3$users = DB::connection('sqlite')->select(/* ... */);

你可以使用連線例項上的 getPdo 方法來訪問連線底層的原生 PDO 例項:

1$pdo = DB::connection()->getPdo();

監聽查詢事件

如果你想指定一個在應用程式執行每條 SQL 查詢時都會被呼叫的閉包,可以使用 DB 門面的 listen 方法。這對於查詢日誌記錄或除錯非常有用。你可以在 服務提供者boot 方法中註冊你的查詢監聽器閉包:

1<?php
2 
3namespace App\Providers;
4 
5use Illuminate\Database\Events\QueryExecuted;
6use Illuminate\Support\Facades\DB;
7use Illuminate\Support\ServiceProvider;
8 
9class AppServiceProvider extends ServiceProvider
10{
11 /**
12 * Register any application services.
13 */
14 public function register(): void
15 {
16 // ...
17 }
18 
19 /**
20 * Bootstrap any application services.
21 */
22 public function boot(): void
23 {
24 DB::listen(function (QueryExecuted $query) {
25 // $query->sql;
26 // $query->bindings;
27 // $query->time;
28 // $query->toRawSql();
29 });
30 }
31}

監控累計查詢時間

現代 Web 應用程式的一個常見效能瓶頸是查詢資料庫所花費的時間。幸運的是,當 Laravel 在單個請求中查詢資料庫花費的時間過長時,它可以呼叫你選擇的閉包或回撥。首先,透過 whenQueryingForLongerThan 方法提供查詢時間閾值(以毫秒為單位)和閉包。你可以在 服務提供者boot 方法中呼叫此方法:

1<?php
2 
3namespace App\Providers;
4 
5use Illuminate\Database\Connection;
6use Illuminate\Support\Facades\DB;
7use Illuminate\Support\ServiceProvider;
8use Illuminate\Database\Events\QueryExecuted;
9 
10class AppServiceProvider extends ServiceProvider
11{
12 /**
13 * Register any application services.
14 */
15 public function register(): void
16 {
17 // ...
18 }
19 
20 /**
21 * Bootstrap any application services.
22 */
23 public function boot(): void
24 {
25 DB::whenQueryingForLongerThan(500, function (Connection $connection, QueryExecuted $event) {
26 // Notify development team...
27 });
28 }
29}

資料庫事務

你可以使用 DB 門面提供的 transaction 方法在一組資料庫事務中執行操作。如果在事務閉包內丟擲異常,事務將自動回滾,並且異常會被重新丟擲。如果閉包執行成功,事務將自動提交。使用 transaction 方法時,無需擔心手動回滾或提交:

1use Illuminate\Support\Facades\DB;
2 
3DB::transaction(function () {
4 DB::update('update users set votes = 1');
5 
6 DB::delete('delete from posts');
7});

處理死鎖

transaction 方法接受一個可選的第二個引數,用於定義發生死鎖時事務應該重試的次數。一旦重試次數耗盡,就會丟擲異常:

1use Illuminate\Support\Facades\DB;
2 
3DB::transaction(function () {
4 DB::update('update users set votes = 1');
5 
6 DB::delete('delete from posts');
7}, attempts: 5);

手動使用事務

如果你想手動開始事務並完全控制回滾和提交,可以使用 DB 門面提供的 beginTransaction 方法:

1use Illuminate\Support\Facades\DB;
2 
3DB::beginTransaction();

你可以透過 rollBack 方法回滾事務:

1DB::rollBack();

最後,你可以透過 commit 方法提交事務:

1DB::commit();

DB 門面的事務方法同時控制 查詢構建器Eloquent ORM 的事務。

連線資料庫 CLI

如果你想連線到資料庫的 CLI,可以使用 db Artisan 命令:

1php artisan db

如果需要,你可以指定一個數據庫連線名稱,以連線到非預設的資料庫連線:

1php artisan db mysql

檢查你的資料庫

使用 db:showdb:table Artisan 命令,你可以深入瞭解你的資料庫及其關聯的表。要檢視資料庫的概覽(包括其大小、型別、開啟的連線數以及表摘要),可以使用 db:show 命令:

1php artisan db:show

你可以透過 --database 選項將資料庫連線名稱傳遞給命令,以指定要檢查的資料庫連線:

1php artisan db:show --database=pgsql

如果你想在命令的輸出中包含錶行數和資料庫檢視詳情,可以分別提供 --counts--views 選項。在大型資料庫上,檢索行數和檢視詳情可能會很慢:

1php artisan db:show --counts --views

此外,你可以使用以下 Schema 方法來檢查你的資料庫:

1use Illuminate\Support\Facades\Schema;
2 
3$tables = Schema::getTables();
4$views = Schema::getViews();
5$columns = Schema::getColumns('users');
6$indexes = Schema::getIndexes('users');
7$foreignKeys = Schema::getForeignKeys('users');

如果你想檢查非應用程式預設連線的資料庫連線,可以使用 connection 方法:

1$columns = Schema::connection('sqlite')->getColumns('users');

表概覽

如果你想獲取資料庫中單個表的概覽,可以執行 db:table Artisan 命令。該命令提供了資料庫表的常規概覽,包括列、型別、屬性、鍵和索引:

1php artisan db:table users

監控你的資料庫

使用 db:monitor Artisan 命令,你可以指示 Laravel 在資料庫管理的開啟連線數超過指定數量時分發一個 Illuminate\Database\Events\DatabaseBusy 事件。

首先,你應該將 db:monitor 命令安排為 每分鐘執行一次。該命令接受你想要監控的資料庫連線配置名稱,以及在分發事件前所能容忍的最大開啟連線數:

1php artisan db:monitor --databases=mysql,pgsql --max=100

僅安排此命令不足以觸發通知來提醒你開啟的連線數。當命令遇到開啟連線數超過閾值的資料庫時,會分發一個 DatabaseBusy 事件。你應該在應用程式的 AppServiceProvider 中監聽此事件,以便向你或你的開發團隊傳送通知:

1use App\Notifications\DatabaseApproachingMaxConnections;
2use Illuminate\Database\Events\DatabaseBusy;
3use Illuminate\Support\Facades\Event;
4use Illuminate\Support\Facades\Notification;
5 
6/**
7 * Bootstrap any application services.
8 */
9public function boot(): void
10{
11 Event::listen(function (DatabaseBusy $event) {
12 Notification::route('mail', '[email protected]')
13 ->notify(new DatabaseApproachingMaxConnections(
14 $event->connectionName,
15 $event->connections
16 ));
17 });
18}