数据库入门
数据库:入门
简介
几乎所有现代 Web 应用都会与数据库交互。Laravel 支持多种数据库,让你能够通过原生 SQL、流式查询构建器和 Eloquent ORM 轻松操作数据库。目前,Laravel 官方支持以下五种数据库:
此外,你可以通过 MongoDB 官方维护的 mongodb/laravel-mongodb 包使用 MongoDB。更多信息请参阅 Laravel MongoDB 文档。
配置
Laravel 数据库服务的配置位于应用的 config/database.php 配置文件中。你可以在该文件中定义所有数据库连接,并指定默认使用的连接。文件中的大多数配置选项取自应用的环境变量,其中还提供了 Laravel 支持的大多数数据库系统的配置示例。
默认情况下,Laravel 的示例环境配置可直接与 Laravel Sail 配合使用。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
[!NOTE]
如果你使用 Laravel 安装器创建应用,并选择 SQLite 作为数据库,Laravel 会自动创建database/database.sqlite文件,并为你运行默认的数据库迁移。
Microsoft SQL Server 配置
要使用 Microsoft SQL Server 数据库,请确保已安装 sqlsrv 和 pdo_sqlsrv PHP 扩展,以及这些扩展所需的依赖,例如 Microsoft SQL ODBC 驱动。
使用 URL 进行配置
通常,数据库连接需要通过 host、database、username、password 等多个配置值进行配置。每个配置值都有对应的环境变量。这意味着,在生产服务器上配置数据库连接信息时,你需要管理多个环境变量。
AWS、Heroku 等托管数据库服务提供商会提供一个数据库「URL」,将所有数据库连接信息包含在一个字符串中。数据库 URL 的示例如下:
mysql://root:password@127.0.0.1/forge?charset=UTF-8
这些 URL 通常遵循以下标准格式:
driver://username:password@host:port/database?options
为方便使用,Laravel 支持通过这些 URL 配置数据库,以替代多个独立配置项。如果存在 url 配置选项(或对应的 DB_URL 环境变量),Laravel 就会从中提取数据库连接信息和凭据。
读写连接
有时,你可能希望对 SELECT 语句使用一个数据库连接,对 INSERT、UPDATE 和 DELETE 语句使用另一个连接。Laravel 让这种配置十分简单,无论你使用原生查询、查询构建器还是 Eloquent ORM,都会使用正确的连接。
下面的示例展示了如何配置读写连接:
'mysql' => [
'driver' => 'mysql',
'read' => [
'host' => [
'192.168.1.1',
'196.168.1.2',
],
],
'write' => [
'host' => [
'192.168.1.3',
],
],
'sticky' => true,
'port' => env('DB_PORT', '3306'),
'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([
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
请注意,配置数组中添加了三个键:read、write 和 sticky。read 和 write 的值都是仅包含 host 键的数组。read 和 write 连接的其余数据库选项会从主 mysql 配置数组中合并而来。
只有需要覆盖主 mysql 数组中的值时,才需要在 read 和 write 数组中添加对应配置。因此,在本例中,「读」连接使用 192.168.1.1 作为主机,「写」连接使用 192.168.1.3。两个连接共享主 mysql 数组中的数据库凭据、前缀、字符集及所有其他选项。当 host 配置数组包含多个值时,每次请求都会随机选择一个数据库主机。
sticky 选项
sticky 是一个可选配置项,用于在当前请求周期内立即读取刚写入数据库的记录。如果启用了 sticky 选项,且当前请求周期内已执行过「写」操作,后续所有「读」操作都会使用「写」连接。这确保了本次请求中写入的数据可以在同一次请求中立即读回。你可以自行决定应用是否需要这种行为。
PostgreSQL 连接池
许多 PostgreSQL 托管服务提供商通过 PgBouncer 或连接代理等服务提供事务模式的连接池。这类连接池很适合应用查询,但部分数据库结构操作、迁移和维护命令需要直接连接数据库。
要在 PostgreSQL 中使用事务连接池,请照常配置池化连接,并通过 direct 配置选项提供直连信息:
'pgsql' => [
'driver' => 'pgsql',
// ...
'pooled' => env('DB_POOLED', false),
'direct' => array_filter([
'host' => env('DB_DIRECT_HOST'),
'port' => env('DB_DIRECT_PORT'),
'username' => env('DB_DIRECT_USERNAME'),
'password' => env('DB_DIRECT_PASSWORD'),
'sslmode' => env('DB_DIRECT_SSLMODE'),
]),
],
将 PostgreSQL 连接配置为池化连接后,Laravel 会自动为该连接启用模拟预处理。直连会继承所有未在 direct 配置中明确指定的选项,并默认使用原生预处理。
Laravel 会自动对迁移、数据库结构的导出与恢复,以及 db:wipe、db:show 和 db:table 使用直连。启用池化模式并配置直连后,db 命令默认也使用直连;你可以传入 --pooled 选项,改用池化连接:
php artisan db --pooled
如果需要在应用中显式使用直连,请在连接名称后添加 ::direct 后缀:
DB::connection('pgsql::direct')->statement('create extension if not exists "uuid-ossp"');
运行 SQL 查询
配置好数据库连接后,就可以使用 DB 门面运行查询。DB 门面为各类查询提供了对应的方法:select、update、insert、delete 和 statement。
运行 Select 查询
要运行基本的 SELECT 查询,可以使用 DB 门面的 select 方法:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* 显示应用中所有用户的列表。
*/
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 语句
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"');
[!WARNING]
由于未预处理语句不绑定参数,可能受到 SQL 注入攻击。切勿在未预处理语句中使用用户可控的值。
隐式提交
在事务中使用 DB 门面的 statement 和 unprepared 方法时,务必避开会导致隐式提交的语句。这些语句会使数据库引擎间接提交整个事务,导致 Laravel 无法获知数据库当前的事务层级。创建数据库表就是一例:
DB::unprepared('create table a (col varchar(1) null)');
所有会触发隐式提交的语句,请参阅 MySQL 手册中的完整列表。
使用多个数据库连接
如果应用在 config/database.php 配置文件中定义了多个连接,你可以通过 DB 门面的 connection 方法访问各个连接。传给该方法的连接名称应对应 config/database.php 中列出的某个连接,或通过 config 辅助函数在运行时配置的连接:
use Illuminate\Support\Facades\DB;
$users = DB::connection('sqlite')->select(/* ... */);
你可以通过连接实例的 getPdo 方法访问该连接底层的原始 PDO 实例:
$pdo = DB::connection()->getPdo();
在 Vitess 上运行分析查询
使用 PlanetScale 等 Vitess 数据库时,你可以通过 olap 方法,以 OLAP 工作负载执行分析查询:
use Illuminate\Support\Facades\DB;
$totals = DB::connection('mysql')->olap(fn ($connection) => $connection->table('orders')
->selectRaw('customer_id, sum(total) as total')
->groupBy('customer_id')
->get()
);
Laravel 会同时设置读写连接的工作负载,并在回调结束后将其重置为 OLTP,即使回调抛出异常也会如此。olap 方法会返回回调的结果。对于未使用 Vitess 的 MySQL 连接,Laravel 会直接执行回调,不改变工作负载。
你可以通过 isVitess 方法判断 MySQL 连接是否使用了 Vitess:
$isVitess = DB::connection('mysql')->isVitess();
监听查询事件
如果希望应用每执行一条 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
{
/**
* 注册应用服务。
*/
public function register(): void
{
// ...
}
/**
* 启动应用服务。
*/
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
{
/**
* 注册应用服务。
*/
public function register(): void
{
// ...
}
/**
* 启动应用服务。
*/
public function boot(): void
{
DB::whenQueryingForLongerThan(500, function (Connection $connection, QueryExecuted $event) {
// 通知开发团队……
});
}
}
数据库事务
你可以使用 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');
}, attempts: 5);
手动使用事务
如果希望手动开启事务,并完全控制回滚与提交,可以使用 DB 门面的 beginTransaction 方法:
use Illuminate\Support\Facades\DB;
DB::beginTransaction();
你可以通过 rollBack 方法回滚事务:
DB::rollBack();
最后,可以通过 commit 方法提交事务:
DB::commit();
[!NOTE]
DB门面的事务方法同时控制查询构建器和 Eloquent ORM 的事务。
连接数据库 CLI
要连接数据库的命令行界面(CLI),可以使用 db Artisan 命令:
php artisan db
如有需要,你可以指定数据库连接名称,以连接到默认连接以外的数据库连接:
php artisan db mysql
检查数据库
通过 db:show 和 db: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;
/**
* 启动应用服务。
*/
public function boot(): void
{
Event::listen(function (DatabaseBusy $event) {
Notification::route('mail', 'dev@example.com')
->notify(new DatabaseApproachingMaxConnections(
$event->connectionName,
$event->connections
));
});
}
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
Laravel 13 中文文档
关于 LearnKu
推荐文章: