首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Laravel 数据库连接配置和读写分离

Laravel 数据库连接配置和读写分离

作者头像
学院君
发布于 2021-01-08 07:53:54
发布于 2021-01-08 07:53:54
6.1K00
代码可运行
举报
文章被收录于专栏:学院君的专栏学院君的专栏
运行总次数:0
代码可运行

今天开始讲如何在 Laravel 中操作数据库,Laravel 为我们提供了多种工具实现对数据库的增删改查,在我们使用 Laravel 提供的这些数据库工具之前,首先要连接到数据库。

数据库的连接配置文件位于 config/database.php,和很多其他 Laravel 配置一样,你可以为数据库配置多个「连接」,然后决定将哪个「连接」作为默认连接。

基本配置

默认情况下,Laravel 为支持的每一种数据库定义了一个连接配置项:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
'connections' => [

    'sqlite' => [
        'driver' => 'sqlite',
        'database' => env('DB_DATABASE', database_path('database.sqlite')),
        'prefix' => '',
    ],

    'mysql' => [
        'driver' => 'mysql',
        'host' => env('DB_HOST', '127.0.0.1'),
        'port' => env('DB_PORT', '3306'),
        'database' => env('DB_DATABASE', 'forge'),
        'username' => env('DB_USERNAME', 'forge'),
        'password' => env('DB_PASSWORD', ''),
        'unix_socket' => env('DB_SOCKET', ''),
        'charset' => 'utf8mb4',
        'collation' => 'utf8mb4_unicode_ci',
        'prefix' => '',
        'strict' => true,
        'engine' => null,
    ],

    'pgsql' => [
        'driver' => 'pgsql',
        'host' => env('DB_HOST', '127.0.0.1'),
        'port' => env('DB_PORT', '5432'),
        'database' => env('DB_DATABASE', 'forge'),
        'username' => env('DB_USERNAME', 'forge'),
        'password' => env('DB_PASSWORD', ''),
        'charset' => 'utf8',
        'prefix' => '',
        'schema' => 'public',
        'sslmode' => 'prefer',
    ],

    'sqlsrv' => [
        'driver' => 'sqlsrv',
        'host' => env('DB_HOST', 'localhost'),
        'port' => env('DB_PORT', '1433'),
        'database' => env('DB_DATABASE', 'forge'),
        'username' => env('DB_USERNAME', 'forge'),
        'password' => env('DB_PASSWORD', ''),
        'charset' => 'utf8',
        'prefix' => '',
    ],

],

包括 SQLiteMySQL、PostgresSQL、SQL Server,一般我们默认使用的都是 MySQL:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
'default' => env('DB_CONNECTION', 'mysql'),

当然,默认数据库连接、数据库名称以及数据库用户名和密码等敏感信息都保存到 .env 文件中了,然后通过 env 辅助函数读取:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

我们平时修改数据库连接信息的话修改这里就好了,默认配置值是针对 Homestead 开发环境配置的,如果你使用的是 Homestead 作为开发环境的话,开箱即用,不用做任何修改,如果不是的话则需要根据自己的环境做修改,比如学院君使用的是 Laradock,配置信息如下(数据库名称、用户名、密码以自己的环境为准,不要照搬):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel57
DB_USERNAME=root
DB_PASSWORD=root

做好以上配置后,你就可以在 Laravel 项目中连接上 MySQL 数据库了。

配置多个数据库连接

有时候,我们的应用用到的不止一个数据库,或者做项目迁移的时候要做新老数据库之间的数据迁移,这个时候我们就可以配置多个数据库连接,如果我们的新老数据库使用的都是 MySQL 的话,可以在 config/database.phpconnections 配置项中新增一个 MySQL 连接:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
'mysql_old' => [
    'driver' => 'mysql',
    'host' => env('DB_HOST_OLD', '127.0.0.1'),
    'port' => env('DB_PORT_OLD', '3306'),
    'database' => env('DB_DATABASE_OLD', 'forge'),
    'username' => env('DB_USERNAME_OLD', 'forge'),
    'password' => env('DB_PASSWORD_OLD', ''),
    'unix_socket' => env('DB_SOCKET_OLD', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'strict' => true,
    'engine' => null,
],

然后在 .env 中新增对应配置项:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DB_CONNECTION_OLD=mysql
DB_HOST_OLD=mysql
DB_PORT_OLD=3306
DB_DATABASE_OLD=laravel56
DB_USERNAME_OLD=root
DB_PASSWORD_OLD=root

接下来,我们要怎样连上这个实例呢?默认情况下,我们在通过 Laravel 提供的数据库工具(DB 门面、查询构建器、Eloquent模型)连接数据库的时候,都没有显式指定连接,因为我们在配置文件中指定了默认的连接 mysql。所以要连接上其它连接很简单,在查询的时候指定这个新的连接就好了,如果你使用的是 DB 门面执行原生 SQL 查询,可以这么连接老的数据库:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$users = DB::connection('mysql_old')->select(...);
DB::connection('mysql_old')->insert(...);

如果你使用的是查询构建器进行数据库操作,可以这么指定(和原生操作一样):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$users = DB::connection('mysql_old')->table('users')->where(...)->get();
DB::connection('mysql_old')->table('users')->insert(...);

如果你使用的 Eloquent 模型类,可以在对应模型类中设置 $connection 属性:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
protected $connection = 'mysql_old';

这样,在模型类上执行查询、插入等操作时都会使用这个 mysql_old 数据库连接。

配置数据库读写分离连接

理论上来说,配置数据库读写分离连接也属于配置多个数据库连接的范畴,但是由于是一个比较特殊又很常见的使用场景,所以我们单独来讨论,Laravel 也对此进行了单独支持。

随着应用访问量的增长,对数据库进行读写分离可以有效的提升应用整体性能,关于数据库层面的读写分离配置不属于本教程讨论范畴,我们这里只讨论从应用层面如何在 Laravel 项目中配置读写分离连接。Laravel 框架数据库底层代码对数据库读写分离进行了支持,所以我们需要遵循底层实现进行读写分离配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
'mysql' => [
    'driver' => 'mysql',
    'read' => [
        'host' => env('DB_HOST_READ', '127.0.0.1'),
    ],
    'write' => [
        'host' => env('DB_HOST_WRITE', '127.0.0.1')
    ],
    'port' => env('DB_PORT', '3306'),
    'database' => env('DB_DATABASE', 'forge'),
    'username' => env('DB_USERNAME', 'forge'),
    'password' => env('DB_PASSWORD', ''),
    'unix_socket' => env('DB_SOCKET', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'strict' => true,
    'engine' => null,
],

read 项配置的是「读」连接,write 项配置的是「写」连接。然后在 .env 中新增 DB_HOST_READDB_HOST_WRITE 配置项。当然,对于 Web 应用而言,大多是读多写少,所以你还可以配置多个 read 主机,Laravel 底层的负载均衡机制是随机从配置的 IP 中挑一个连接:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
'read' => [
    'host' => [env('DB_HOST_READ_1'), env('DB_HOST_READ_2')],
],

有的人会问,如果我们读写数据库的用户名、密码不一样咋办?好办,上面这种配置默认读写连接使用的用户名密码一样,那我们在读写配置项中分别独立配置就好了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
'mysql' => [
    'driver' => 'mysql',
    'read' => [
        'host' => env('DB_HOST_READ', '127.0.0.1'),
        'username' => env('DB_USERNAME_READ', 'forge'),
        'password' => env('DB_PASSWORD_READ', ''),
    ],
    'write' => [
        'host' => env('DB_HOST_WRITE', '127.0.0.1'),
        'username' => env('DB_USERNAME_WRITE', 'forge'),
        'password' => env('DB_PASSWORD_WRITE', ''),
    ],
    'port' => env('DB_PORT', '3306'),
    'database' => env('DB_DATABASE', 'forge'),
    'unix_socket' => env('DB_SOCKET', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'strict' => true,
    'engine' => null,
],

其他公共配置项也是同理,不再赘述。

针对读写分离数据库的连接,Laravel 数据库底层会自动判断,如果是查询语句会使用读连接,如果是数据库插入、更新、删除等操作会使用写连接。

读写分离本地模拟测试

我们可以在本地简单模拟测试下读写分离配置,我们使用同一个数据库主机,不同的数据库来进行读写分离,在数据库中创建一个新的数据库用作写数据库,并将其配置到 config/database.php

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
'mysql' => [
   'driver' => 'mysql',
   'read' => [
       'host' => env('DB_HOST_READ', '127.0.0.1'),
       'database' => env('DB_DATABASE', 'db_read'),
   ],
   'write' => [
       'host' => env('DB_HOST_WRITE', '127.0.0.1'),
       'database' => env('DB_DATABASE_WRITE', 'db_write'),
   ],
   'port' => env('DB_PORT', '3306'),
   'username' => env('DB_USERNAME', 'root'),
   'password' => env('DB_PASSWORD', ''),
   'unix_socket' => env('DB_SOCKET', ''),
   'charset' => 'utf8mb4',
   'collation' => 'utf8mb4_unicode_ci',
   'prefix' => '',
   'strict' => true,
   'engine' => null,
],

然后我们在命令行运行 php artisan migrate,就会将数据库迁移应用到写数据库,因为数据库更新也属于写操作,所以此时自动判读使用写连接。

然后我们通过 Tinker 插入一条记录(插入属于写操作,自动使用写连接):

然后你会在写数据库中看到这条记录,读数据库中没有,接下来,我们运行一条查询语句(查询属于读操作,自动使用读连接):

此时,由于我们并没有配置读写数据库之间的数据同步,所以只能查出来我们在上一篇教程中在读数据库中插入的记录。所以在 Laravel 中实现读写分离还是很方便的,我们只需要做好配置就好了,剩下的框架帮我们完成。

当然,和多个数据库连接类似,你也可以在使用时显式进行指定,以查询构建器为例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DB::connection('read')->table('users')->where(...)->get();
DB::connection('write')->table('users')->insert(...);

如果通过 Eloquent 模型类调用的话,还可以这么指定:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
User::on('read')->where(...)->get();
User::onWriteConnection()->save();

注:关于数据库的各种增删改查操作,后面会一一介绍。

读写分离配置中的 `sticky` 配置项

在读写分离配置中,我们注意到新增了一个 sticky 配置项,这个是用来干嘛的呢?

我们配置数据库读写分离的时候,会配置读数据库(从库)从写数据库(主库)同步数据,由于不同主机之间数据同步是需要时间的,虽然这个时间很短,但是对于并发量很大的应用,还是可能出现写入写数据库的数据不能立即从读数据库读取到的情况,sticky 配置项在这个时候就派上用场了。如果该配置项设置为 true 的话,在同一个请求生命周期中,写入的数据会被立刻读取到,底层原理其实就是读操作也从写数据库读取,因为写数据库始终是最新数据,从而避免主从同步延迟导致的数据不一致。

其它配置项

除了上面提到的数据库连接配置外,config/database.php 配置文件中还有一些其它配置项,你可以通过 migrations 配置项自定义数据库迁移表的名称,默认是 migrations

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
'migrations' => 'migrations',

还可以通过 redis 配置项配置 Redis 作为 NoSQL 数据库的连接配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
'redis' => [

    'client' => 'predis',

    'default' => [
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD', null),
        'port' => env('REDIS_PORT', 6379),
        'database' => env('REDIS_DB', 0),
    ],

    'cache' => [
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD', null),
        'port' => env('REDIS_PORT', 6379),
        'database' => env('REDIS_CACHE_DB', 1),
    ],

],

你可以看到 Redis 也支持多个连接,一个默认连接和一个用作缓存的 cache 连接。这一思想在 Laravel 配置中无处不在,很多服务都支持配置多个连接提供不同的驱动,比如 Session 支持文件、数据表等连接,缓存支持 Memcached、Redis 等连接,队列支持数据库、Beanstalkd 等连接。你可以为它们定义多个连接,然后指定一个默认连接,这样做的好处是,当某个连接出现问题,或者你想切换到其它实现,只需动动手指头修改下配置文件中的默认配置项就好了,极大的提高了系统的可维护性。

注:关于 Redis 我们会在后面单独讲,这里不做深入讨论。

本系列教程首发在学院君网站(xueyuanjun.com),你可以点击页面左下角阅读原文链接查看最新更新的教程。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 极客书房 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
一致性无锁读与MVCC、undo-log、Read-View
一致性读,即快照读。在InnoDB中,事务中的查询会基于某个时间点创建的快照返回结果集,而非查询数据库表空间中的当前数据。一致性读(MySQL官方文档)。
windealli
2022/09/21
1.4K1
一致性无锁读与MVCC、undo-log、Read-View
✅浅聊MVCC?
MVCC,即多版本并发控制(Multiversion Concurrency Control),类似于数据库锁,是一种优雅的并发控制方案。
@派大星
2024/05/02
2340
MySQL MVCC详解
有没有一种方式,可以不采用锁机制,而是通过乐观锁的方式来解决不可重复读和幻读问题呢?实际上 MVCC 机制的设计,就是用来解决这个问题的,它可以在大多数情况下替代行级锁,降低系统的开销。
Michel_Rolle
2023/11/02
3.1K0
MVCC 原理
一个事务读取了另外一个事务修改后记录 强调的是 update 和delete ,只需要锁住满足条件的记录即可
王小明_HIT
2020/06/16
7821
MVCC 原理
MySQL八:读懂MVCC多版本并发控制
mysql在并发的情况下,会引起脏读,幻读,不可重复读等一系列的问题,为解决这些问题,引入了mvcc的机制。本文就详细看看mvcc是怎么解决脏读,幻读等问题的。
云扬四海
2022/09/26
8170
MVCC实现原理之ReadView(一步到位)
使用 READ UNCOMMITTED 隔离级别的事务,由于可以读到未提交事务修改过的记录,所以直接读取记录 的最新版本就好了。
一个风轻云淡
2022/11/15
1.3K0
MVCC实现原理之ReadView(一步到位)
MySQL MVCC实现原理
MVCC (Multiversion Concurrency Control),多版本并发控制。顾名思义,MVCC是通过数据行的多个版本管理实现数据库的并发控制。这项技术使得在InnoDB的事务隔离级别下执行一致性读操作有了保证。换言之,就是为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值,这样在做查询的时候就不用等待另一个事务释放锁。
得物技术
2023/03/21
8410
MySQL  MVCC实现原理
大厂最爱问的MVCC,到底是个啥?
多版本并发控制(MVCC)是一种用于提高数据库并发性能的技术,尤其在处理高并发读写操作时极为有效。MVCC通过维护数据的多个版本来避免读写冲突,使得读操作无需阻塞写操作,写操作也不会影响读操作。下面,我们具体讲解MySQL中InnoDB存储引擎对MVCC的实现原理。
不惑
2024/09/04
2.4K0
大厂最爱问的MVCC,到底是个啥?
通俗易懂数据库MVCC讲解,后悔没早点学
全称Multi-Version Concurrency Control,即多版本并发控制,主要是为了提高数据库的并发性能。以下文章都是围绕InnoDB引擎来讲,因为myIsam不支持事务。
公众号 IT老哥
2020/09/16
4.7K1
通俗易懂数据库MVCC讲解,后悔没早点学
【MySQL】MySQL中MVCC多版本并发控制的概念
锁相关的知识我们已经学习完了,在其中我们提到过一个概念,那就是 MVCC 。这又是个什么东西呢?今天我们就来好好看看 MVCC 到底是干嘛的。
硬核项目经理
2024/05/02
2590
【MySQL】MySQL中MVCC多版本并发控制的概念
初识MySQL · 事务 · 下
前文我们介绍了MySQL的事务的四大特点,分别是原子性,隔离性,一致性,持久性,其中以上四个特点统称为ACID,不过其实我们学完了事务之后,我们知道所谓的ACID最后不过是通过AID保证C的。
_lazy
2025/06/13
830
初识MySQL · 事务 · 下
【MySQL笔记】正确的理解MySQL的MVCC及实现原理
MVCC 在 MySQL InnoDB 中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读
全栈程序员站长
2022/08/12
8520
【MySQL笔记】正确的理解MySQL的MVCC及实现原理
【MySql】多版本并发控制MVCC前置知识——隐藏字段、undo日志与Read View
1.每个事务都要有自己的事务ID,可以根据事务ID的大小,来决定事务到来的先后顺序
平凡的人1
2023/10/15
5200
【MySql】多版本并发控制MVCC前置知识——隐藏字段、undo日志与Read View
MySQL探秘(六):InnoDB一致性非锁定读
 一致性非锁定读(consistent nonlocking read)是指InnoDB存储引擎通过多版本控制(MVVC)读取当前数据库中行数据的方式。如果读取的行正在执行DELETE或UPDATE操作,这时读取操作不会因此去等待行上锁的释放。相反地,InnoDB会去读取行的一个快照。
程序员历小冰
2018/11/26
1.1K0
MySQL探秘(六):InnoDB一致性非锁定读
MySQL多版本并发控制机制(MVCC)-源码浅析
作为一个数据库爱好者,自己动手写过简单的SQL解析器以及存储引擎,但感觉还是不够过瘾。<<事务处理-概念与技术>>诚然讲的非常透彻,但只能提纲挈领,不能让你玩转某个真正的数据库。感谢cmake,能够让我在mac上用xcode去debug MySQL,从而能去领略它的各种实现细节。 笔者一直对数据库的隔离性很好奇,此篇博客就是我debug MySQL过程中的偶有所得。 (注:本文的MySQL采用的是MySQL-5.6.35版本)
无毁的湖光-Al
2018/08/27
1.7K0
MySQL多版本并发控制机制(MVCC)-源码浅析
第16章_多版本并发控制
🧑个人简介:大家好,我是 shark-Gao,一个想要与大家共同进步的男人😉😉
程序员Leo
2023/08/02
1970
第16章_多版本并发控制
MySQL数据库的事务隔离和MVCC
事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity)、一致性(consistency)、隔离性(isolation)、持久性(durability)的缩写,这四种状态的意思是:
数据和云
2019/05/13
1.2K0
MySQL数据库的事务隔离和MVCC
面试题:MySQL事务的ACID如何实现?
事务(Transaction)是并发控制的基本单位。所谓的事务呢,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。
码老思
2023/10/29
3650
面试题:MySQL事务的ACID如何实现?
Mysql高可用高性能存储应用系列2 - 深入理解锁和Mvcc
Mysql数据库在处理并发中下了很多功夫,锁是为了更好的保护数据的正确和可靠,Mvcc是维持一个数据的多个版本,使得读写操作没有冲突的解决并发的数据库方案。
stark张宇
2023/03/24
4590
Mysql高可用高性能存储应用系列2 - 深入理解锁和Mvcc
一文读懂Innodb MVCC实现原理
• 不可重复读:事物A同样的查询条件,查询多次,读出的数据不一样,不一样的侧重点在于 update和delete
公众号-Java编程大本营
2021/01/24
8100
推荐阅读
相关推荐
一致性无锁读与MVCC、undo-log、Read-View
更多 >
交个朋友
加入前端学习入门群
前端基础系统教学 经验分享避坑指南
加入前端工作实战群
前端工程化实践 组件库开发经验分享
加入前端趋势交流群
追踪前端新趋势 交流学习心得
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档