跳转到内容

数据库:入门

简介

几乎每个现代 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 环境变量中,轻松配置你的环境变量以指向此数据库

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

默认情况下,外键约束对于 SQLite 连接是启用的。如果要禁用它们,应将 DB_FOREIGN_KEYS 环境变量设置为 false

DB_FOREIGN_KEYS=false
lightbulb

如果你使用 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 的示例可能如下所示:

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

这些 URL 通常遵循标准架构约定:

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

为方便起见,Laravel 支持这些 URL 作为使用多个配置选项配置数据库的替代方法。如果存在 url(或对应的 DB_URL 环境变量)配置选项,则将使用它来提取数据库连接和凭据信息。

读写连接

有时,你可能希望对 SELECT 语句使用一个数据库连接,而对 INSERT、UPDATE 和 DELETE 语句使用另一个数据库连接。Laravel 使这变得轻而易举,无论你使用原始查询、查询构建器还是 Eloquent ORM,都将始终使用正确的连接。

要查看应如何配置读/写连接,让我们看一下此示例:

'mysql' => [
'read' => [
'host' => [
'192.168.1.1',
'196.168.1.2',
],
],
'write' => [
'host' => [
'196.168.1.3',
],
],
'sticky' => true,
 
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => env('DB_CHARSET', 'utf8mb4'),
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],

请注意,配置数组中添加了三个键:readwritestickyreadwrite 键具有包含单个键的数组值:hostreadwrite 连接的其余数据库选项将从主 mysql 配置数组合并。

只有在你想覆盖主 mysql 数组中的值时,才需要在 readwrite 数组中放置项。因此,在这种情况下,192.168.1.1 将用作“read”连接的主机,而 192.168.1.3 将用于“write”连接。数据库凭据、前缀、字符集和主 mysql 数组中的所有其他选项将在两个连接之间共享。当 host 配置数组中存在多个值时,将为每个请求随机选择一个数据库主机。

sticky 选项

sticky 选项是一个可选值,可用于允许立即读取在当前请求周期内已写入数据库的记录。如果启用了 sticky 选项,并且在当前请求周期中对数据库执行了“写入”操作,则任何进一步的“读取”操作都将使用“写入”连接。这确保了在请求周期内写入的任何数据都可以在同一请求期间立即从数据库中读取。是否为你应用程序所需的行为由你决定。

运行 SQL 查询

配置数据库连接后,你可以使用 DB 外观运行查询。DB 外观为每种类型的查询提供方法:selectupdateinsertdeletestatement

运行 Select 查询

要运行基本的 SELECT 查询,可以使用 DB 外观上的 select 方法:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
 
class UserController extends Controller
{
/**
* Show a list of all of the application's users.
*/
public function index(): View
{
$users = DB::select('select * from users where active = ?', [1]);
 
return view('user.index', ['users' => $users]);
}
}

传递给 select 方法的第一个参数是 SQL 查询,而第二个参数是需要绑定到查询的任何参数绑定。通常,这些是 where 子句约束的值。参数绑定提供针对 SQL 注入的保护。

select 方法将始终返回结果的 array。数组中的每个结果都将是一个 PHP stdClass 对象,表示数据库中的一条记录

use Illuminate\Support\Facades\DB;
 
$users = DB::select('select * from users');
 
foreach ($users as $user) {
echo $user->name;
}

选择标量值

有时,你的数据库查询可能会产生单个标量值。Laravel 不必让你从记录对象中检索查询的标量结果,而是允许你使用 scalar 方法直接检索此值:

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

选择多个结果集

如果你的应用程序调用返回多个结果集的存储过程,则可以使用 selectResultSets 方法来检索存储过程返回的所有结果集:

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

使用命名绑定

你可以使用命名绑定执行查询,而不是使用 ? 来表示你的参数绑定:

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

运行 Insert 语句

要执行 insert 语句,可以使用 DB 外观上的 insert 方法。与 select 一样,此方法接受 SQL 查询作为其第一个参数,绑定作为其第二个参数:

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

运行 Update 语句

应使用 update 方法来更新数据库中现有的记录。该方法会返回受该语句影响的行数。

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

运行删除语句

应使用 delete 方法从数据库中删除记录。与 update 一样,该方法会返回受影响的行数。

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

运行通用语句

有些数据库语句不返回任何值。对于这些类型的操作,您可以使用 DB 门面上的 statement 方法。

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

运行未预处理的语句

有时您可能想要执行不绑定任何值的 SQL 语句。您可以使用 DB 门面的 unprepared 方法来实现此目的。

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

由于未预处理的语句不绑定参数,因此它们可能容易受到 SQL 注入的攻击。您绝不应允许在未预处理的语句中使用用户控制的值。

隐式提交

在事务中使用 DB 门面的 statementunprepared 方法时,您必须小心避免导致隐式提交的语句。这些语句将导致数据库引擎间接地提交整个事务,从而使 Laravel 不知道数据库的事务级别。创建数据库表就是此类语句的一个示例。

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

请参阅 MySQL 手册以获取触发隐式提交的所有语句的列表

使用多个数据库连接

如果您的应用程序在 config/database.php 配置文件中定义了多个连接,您可以通过 DB 门面提供的 connection 方法访问每个连接。传递给 connection 方法的连接名称应与 config/database.php 配置文件中列出的连接之一或使用 config 助手在运行时配置的连接相对应。

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

您可以使用连接实例上的 getPdo 方法访问连接的原始底层 PDO 实例。

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

监听查询事件

如果您想为应用程序执行的每个 SQL 查询指定一个闭包来调用,您可以使用 DB 门面的 listen 方法。此方法对于记录查询或调试非常有用。您可以在服务提供者boot 方法中注册您的查询监听器闭包。

<?php
 
namespace App\Providers;
 
use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
 
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// ...
}
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
DB::listen(function (QueryExecuted $query) {
// $query->sql;
// $query->bindings;
// $query->time;
// $query->toRawSql();
});
}
}

监控累积查询时间

现代 Web 应用程序的一个常见性能瓶颈是它们在查询数据库上花费的时间。幸运的是,当 Laravel 在单个请求期间花费过多的时间查询数据库时,它可以调用您选择的闭包或回调。要开始使用,请为 whenQueryingForLongerThan 方法提供一个查询时间阈值(以毫秒为单位)和闭包。您可以在服务提供者boot 方法中调用此方法。

<?php
 
namespace App\Providers;
 
use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Events\QueryExecuted;
 
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// ...
}
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
DB::whenQueryingForLongerThan(500, function (Connection $connection, QueryExecuted $event) {
// Notify development team...
});
}
}

数据库事务

您可以使用 DB 门面提供的 transaction 方法在数据库事务中运行一组操作。如果在事务闭包中抛出异常,则事务将自动回滚,并且该异常将被重新抛出。如果闭包执行成功,则事务将自动提交。使用 transaction 方法时,您无需担心手动回滚或提交。

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

处理死锁

transaction 方法接受一个可选的第二个参数,该参数定义在发生死锁时应重试事务的次数。一旦这些尝试耗尽,将抛出异常。

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

手动使用事务

如果您想手动启动事务并完全控制回滚和提交,您可以使用 DB 门面提供的 beginTransaction 方法。

use Illuminate\Support\Facades\DB;
 
DB::beginTransaction();

您可以通过 rollBack 方法回滚事务。

DB::rollBack();

最后,您可以通过 commit 方法提交事务。

DB::commit();
lightbulb

DB 门面的事务方法控制 查询构建器Eloquent ORM 的事务。

连接到数据库 CLI

如果您想连接到数据库的 CLI,您可以使用 db Artisan 命令。

php artisan db

如果需要,您可以指定一个数据库连接名称来连接到不是默认连接的数据库连接。

php artisan db mysql

检查你的数据库

使用 db:showdb:table Artisan 命令,您可以深入了解您的数据库及其关联的表。要查看数据库的概述,包括其大小、类型、打开的连接数以及其表的摘要,您可以使用 db:show 命令。

php artisan db:show

您可以通过 --database 选项向该命令提供数据库连接名称,以指定应检查哪个数据库连接。

php artisan db:show --database=pgsql

如果您想在命令的输出中包含表行计数和数据库视图详细信息,您可以分别提供 --counts--views 选项。在大型数据库上,检索行计数和视图详细信息可能会很慢。

php artisan db:show --counts --views

此外,您可以使用以下 Schema 方法来检查您的数据库。

use Illuminate\Support\Facades\Schema;
 
$tables = Schema::getTables();
$views = Schema::getViews();
$columns = Schema::getColumns('users');
$indexes = Schema::getIndexes('users');
$foreignKeys = Schema::getForeignKeys('users');

如果您想检查不是应用程序默认连接的数据库连接,您可以使用 connection 方法。

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

表概述

如果您想了解数据库中单个表的概述,您可以执行 db:table Artisan 命令。此命令提供数据库表的常规概述,包括其列、类型、属性、键和索引。

php artisan db:table users

监控你的数据库

使用 db:monitor Artisan 命令,您可以指示 Laravel 在数据库管理的打开连接数超过指定数量时调度 Illuminate\Database\Events\DatabaseBusy 事件。

要开始使用,您应该计划每分钟运行一次 db:monitor 命令。该命令接受您希望监视的数据库连接配置的名称以及在调度事件之前应容忍的最大打开连接数。

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

仅计划此命令不足以触发通知,提醒您打开的连接数。当命令遇到打开的连接数超过阈值的数据库时,将调度 DatabaseBusy 事件。您应该在应用程序的 AppServiceProvider 中监听此事件,以便向您或您的开发团队发送通知。

use App\Notifications\DatabaseApproachingMaxConnections;
use Illuminate\Database\Events\DatabaseBusy;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Notification;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Event::listen(function (DatabaseBusy $event) {
Notification::route('mail', '[email protected]')
->notify(new DatabaseApproachingMaxConnections(
$event->connectionName,
$event->connections
));
});
}