Laravel数据查询优化最佳实践

本文涉及的产品
云解析DNS,个人版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 在 Laravel 中,数据库查询是一个常见的任务。为了提高查询的性能和可维护性,我们可以通过自定义查询构造器类来优化数据库查询。本文将详细解析使用 自定义ORM查询构造器类 `CacheBuilder` 和 改造 Laravel 中的 `DB` 类,以使用自定义的查询构造器类 `CacheBuilder` 缓存技巧来优化数据库查询。并详细解释每个方法的意义和改造的原因。

简介

在 Laravel 中,数据库查询是一个常见的任务。为了提高查询的性能和可维护性,我们可以通过自定义查询构造器类来优化数据库查询。本文将详细解析使用 自定义ORM查询构造器类 CacheBuilder 和 改造 Laravel 中的 DB 类,以使用自定义的查询构造器类 CacheBuilder 缓存技巧来优化数据库查询。并详细解释每个方法的意义和改造的原因。

优化方案

ORM 优化

查询构造器类是执行数据库查询的重要组件之一。通过自定义查询构造器类,我们可以扩展和优化查询功能,以满足特定的需求。在本文中,我们将详细解析自定义查询构造器类 CacheBuilder 中的每个方法,以及为什么要进行这样的改造。

当使用 Laravel 框架的 ORM 建造者模式时,你可以自定义一个新的查询方法 firstCache() 来实现从缓存中读取数据的功能。下面是一个详细的代码改造过程示例:

  1. 创建一个新的查询构造器类 CacheBuilder,继承自 Laravel 的原生查询构造器类 Illuminate\Database\Query\Builder。在这个类中,你可以添加自定义的方法 firstCache()
<?php

namespace App\Database\Query;

use Illuminate\Database\Query\Builder;

class CacheBuilder extends Builder
{
   
    /**
     * Execute the query and get the first result from the cache or the database.
     *
     * @param  array|string  $columns
     * @return mixed
     */
    public function firstCache($columns = ['*'])
    {
   
        // 1. 尝试从缓存中获取数据
        {
   mathJaxContainer[0]}this->getCacheKey();
        {
   mathJaxContainer[1]}cacheKey);

        if ($cachedData !== null) {
   
            // 如果缓存中存在数据,则直接返回
            return {
   mathJaxContainer[2]}this->model->newCollection([{
   mathJaxContainer[3]}columns)->first();
        }

        // 2. 从数据库中获取数据
        {
   mathJaxContainer[4]}this->first($columns);

        if ($result !== null) {
   
            // 如果数据库中存在数据,则将其缓存起来
            Cache::put({
   mathJaxContainer[5]}result->toArray(), $this->getCacheExpiration());
        }

        return $result;
    }

    /**
     * Get the cache key for the query.
     *
     * @return string
     */
    protected function getCacheKey()
    {
   
        // 这里可以根据你的需求生成一个唯一的缓存键
        return 'cache_key_' . md5({
   mathJaxContainer[6]}this->getBindings()));
    }

    /**
     * Get the cache expiration time in seconds.
     *
     * @return int
     */
    protected function getCacheExpiration()
    {
   
        // 这里可以根据你的需求设置缓存的过期时间
        return 3600; // 1 hour
    }
}

上述代码展示了 CacheBuilder 类的基本结构和示例的缓存优化方法。现在,让我们逐个解析每个方法的意义和改造原因:

  1. firstCache() 方法
    firstCache() 方法是我们自定义的方法,用于缓存查询结果并返回第一个结果。在该方法中,我们首先尝试从缓存中获取数据,如果缓存中存在数据,则直接返回缓存结果,避免了对数据库的额外查询操作。如果缓存中不存在数据,则从数据库中获取数据,并将结果缓存起来,以便下次查询时可以直接从缓存中读取。通过这样的改造,我们可以减少对数据库的访问次数,提高查询性能。
  2. getCacheKey() 方法
    getCacheKey() 方法用于生成缓存键名。在该方法中,我们根据查询的 SQL 语句和绑定的参数生成一个唯一的缓存键。通过自定义键名的生成规则,我们可以更灵活地控制缓存的存储和管理。
  3. getCacheExpiration() 方法
    getCacheExpiration() 方法用于设置缓存的过期时间。在该方法中,我们可以根据需求设置缓存的有效期,以确保缓存的数据在一定时间后会被更新。通过设置适当的缓存过期时间,我们可以在一定程度上保持数据的实时性。

通过以上的方法改造,我们实现了一个自定义的查询构造器类 CacheBuilder,它具备了缓存查询结果的能力。这样的改造使得我们可以更高效地执行数据库查询,减少对数据库的访问次数,从而提高查询性能和应用程序的响应速度。

接下来,让我们看一下如何在模型中使用自定义的查询构造器类 CacheBuilder

<?php

namespace App\Models;

use App\Database\Query\CacheBuilder;
use Illuminate\Database\Eloquent\Model;

class YourModel extends Model
{
   
    /**
     * Get a new query builder instance for the connection.
     *
     * @param  \Illuminate\Database\Query\Builder|null  $query
     * @return \Illuminate\Database\Query\Builder
     */
    protected function newBaseQueryBuilder($query = null)
    {
   
        {
   mathJaxContainer[7]}this->getConnection();

        // 使用自定义的查询构造器类 CacheBuilder
        return new CacheBuilder({
   mathJaxContainer[8]}connection->getQueryGrammar(), {
   mathJaxContainer[9]}query);
    }
}

现在你可以在使用模型查询时,使用新的方法 firstCache() 来从缓存中读取数据。例如:

$result = YourModel::where('column', 'value')->firstCache();

这将先尝试从缓存中获取数据,如果缓存中不存在,则从数据库中读取数据,并将结果缓存起来。

请注意,以上代码示例仅为演示目的,你可能需要根据你的实际需求进行适当的修改和调整。另外,确保你已经正确配置了缓存驱动程序和相关的缓存设置。

Query Builder 优化

在 Laravel 中,我们通常使用 DB 类来执行数据库查询操作。然而,有时候我们需要对查询进行优化,以提高性能和可维护性。这时,自定义查询构造器类就派上了用场。

继承ORM Builder实现

在本文中,我们将介绍如何改造 Laravel 的 DB 类,以使用自定义的查询构造器类 CacheBuilder。这个改造将为我们提供一个名为 firstCache() 的方法,用于缓存查询结果,从而进一步提高查询性能。

首先,我们需要创建一个新的类 CacheDB,它继承自 Laravel 的原生 DB 类,并重写了 connection 方法。这个方法用于获取数据库连接实例。

<?php

namespace App\Database;

use Illuminate\Support\Facades\DB as BaseDB;

class CacheDB extends BaseDB
{
   
    /**
     * Get a database connection instance.
     *
     * @param  string|null  $name
     * @return \Illuminate\Database\ConnectionInterface
     */
    public static function connection($name = null)
    {
   
        {
   mathJaxContainer[10]}name);

        // 使用自定义的查询构造器类 CacheBuilder
        {
   mathJaxContainer[11]}connection->getQueryGrammar());
        {
   mathJaxContainer[12]}connection->getPostProcessor());
        {
   mathJaxContainer[13]}connection, {
   mathJaxContainer[14]}connection->getPostProcessor()));

        return $connection;
    }
}

上述代码中的 CacheDB 类继承了原生的 DB 类,并重写了 connection 方法。这个方法在获取数据库连接实例时被调用。

在重写的 connection 方法中,我们首先调用了父类的 connection 方法,以获取原始的数据库连接实例。然后,我们使用自定义的查询构造器类 CacheBuilder 替换了原始连接实例的查询语法和后处理器,并设置了新的查询构造器。

现在,我们需要在 config/app.php 文件中进行一些配置更改,以使用我们的自定义 CacheDB 类。

'aliases' => [
    // ...
    'DB' => App\Database\CacheDB::class,
    // ...
],

在上述代码中,我们将原生的 DB 类别名替换为我们自定义的 CacheDB 类。这样,当我们使用 DB 类进行数据库查询时,实际上是使用了我们自定义的查询构造器类 CacheBuilder

现在,我们可以使用 firstCache() 方法来缓存查询结果,以提高查询性能。例如:

$result = DB::table('your_table')->where('column', 'value')->firstCache();

通过以上改造,我们实现了一个自定义的查询构造器类 CacheBuilder,并将其应用于 Laravel 的 DB 类。这样做的目的是为了提高数据库查询的性能和可维护性。

独立写Cache

当使用 Laravel 框架时,你可以通过自定义一个扩展类来实现对 DB 建造者模式的改造,以满足你的需求。下面是一个示例代码:

<?php

namespace App\Extensions;

use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\Cache;

class CustomQueryBuilder extends Builder
{
   
    public function firstCache({
   mathJaxContainer[15]}key = null)
    {
   
        {
   mathJaxContainer[16]}key ?: $this->getCacheKey();

        return Cache::remember({
   mathJaxContainer[17]}minutes, function () {
   
            return $this->first();
        });
    }

    public function getCache({
   mathJaxContainer[18]}key = null)
    {
   
        {
   mathJaxContainer[19]}key ?: $this->getCacheKey();

        return Cache::remember({
   mathJaxContainer[20]}minutes, function () {
   
            return $this->get();
        });
    }

    protected function getCacheKey()
    {
   
        return 'query_cache_' . sha1({
   mathJaxContainer[21]}this->getBindings()));
    }
}

在上述代码中,我们自定义了一个名为 CustomQueryBuilder 的类,它继承自 Laravel 框架的 Builder 类,也就是 DB 建造者类。这个自定义类添加了 firstCache()getCache() 两个方法。

  • firstCache() 方法用于获取第一条记录并使用缓存。它接受两个参数:缓存的分钟数和可选的缓存键。如果缓存存在,将直接从缓存中获取数据;否则,将从数据库获取数据并生成缓存。最后,返回获取到的第一条记录。
  • getCache() 方法用于获取多条记录并使用缓存。它接受两个参数:缓存的分钟数和可选的缓存键。与 firstCache() 方法类似,它也会先检查缓存是否存在,然后决定是直接从缓存中获取数据还是从数据库中获取数据并生成缓存。最后,返回获取到的多条记录。

这两个方法内部使用了 Laravel 框架的 Cache 类来进行缓存操作。缓存键的生成使用了查询的 SQL 语句和绑定参数,以保证每个查询的唯一性。

为了使用这个自定义的 CustomQueryBuilder 类,你需要在 Laravel 项目中注册这个自定义类作为 DB 建造者的默认类。可以在 AppServiceProvider 或其他合适的服务提供者中添加以下代码:

use Illuminate\Database\ConnectionInterface;
use App\Extensions\CustomQueryBuilder;

public function register()
{
   
    {
   mathJaxContainer[22]}app) {
   
        return new CustomQueryBuilder(
            $app['db']->connection(),
            $app['db']->getQueryGrammar(),
            $app['db']->getPostProcessor()
        );
    });
}

这段代码将绑定 ConnectionInterface 接口到 CustomQueryBuilder 类,以便在使用 DB 门面时默认使用你的自定义类。

这样,当你使用 DB 门面的 table() 方法时,将使用你自定义的 CustomQueryBuilder 类,从而具备了 firstCache()getCache() 方法的功能。

使用封装的方法

现在你可以在项目中使用 firstCache()getCache() 方法来获取缓存数据。以下是示例用法:

$users = DB::table('users')->firstCache(60); // 获取第一条用户数据并使用缓存(缓存有效期为60分钟)

在上述示例中,我们使用 DB::table('users') 获取了一个查询构建器,然后通过调用 firstCache(60) 方法来获取第一条用户数据。如果缓存存在,将直接从缓存中获取数据,否则将从数据库中查询并生成缓存。

$users = DB::table('users')->getCache(60); // 获取所有用户数据并使用缓存(缓存有效期为60分钟)

这个示例展示了如何使用 getCache(60) 方法来获取所有的用户数据,并使用缓存。同样地,如果缓存存在,数据将直接从缓存中获取,否则将从数据库中查询并生成缓存。

总结:

通过自定义查询构造器类的改造,我们可以优化 Laravel 的数据库查询。在本文中,我们介绍了如何改造 DB 类,使用自定义的查询构造器类 CacheBuilder。这个改造允许我们使用新的方法 firstCache() 来缓存查询结果,从而提高查询性能。通过重写 connection 方法,我们成功地将自定义的查询构造器类应用于 DB 类,并在配置文件中进行了相应的修改。这样的改造提供了更高效和可扩展的数据库查询功能,使得我们能够更好地优化和管理我们的应用程序。无论是对于初学者还是有经验的开发者来说,这种改造都是非常有益的。

Laravel其他优化方法

除了对 Laravel 的建造者模式进行缓存优化外,还有许多其他的优化方式可以提升 Laravel 框架的性能和效率。以下是一些常见的优化方式:

  1. 缓存配置和路由:
    • 使用缓存来存储配置文件,以减少每次请求时重新加载配置的开销。
    • 缓存路由信息,避免在每次请求时重新解析路由。
  2. 使用缓存:
    • 使用缓存来存储经常访问的数据,如数据库查询结果、API 响应等。
    • 使用适当的缓存驱动(如 Memcached、Redis)来提高缓存性能。
  3. 数据库优化:
    • 使用适当的索引来加速数据库查询。
    • 避免在循环中执行数据库查询,尽量使用批量操作。
    • 使用延迟加载(Lazy Loading)来减少关联模型的查询次数。
  4. 代码优化:
    • 避免在视图中执行复杂的逻辑操作,尽量将逻辑放在控制器或服务层中处理。
    • 使用 Eager Loading 来预加载关联模型,减少查询次数。
    • 使用合适的数据结构和算法来提高代码的效率。
  5. 使用队列:
    • 将耗时的任务放入队列中异步处理,提高应用程序的响应速度。
    • 使用适当的队列驱动(如 Redis、Beanstalkd)来提高队列的处理性能。
  6. 优化自动加载:
    • 使用 Composer 的 classmap 自动加载优化,将类映射到文件路径,减少自动加载的开销。
    • 避免加载不必要的类和文件。
  7. 使用缓存视图:
    • 将编译后的视图缓存起来,减少视图编译的开销。
  8. 使用 HTTP 缓存:
    • 设置适当的缓存头,利用浏览器缓存和 CDN 缓存来减少重复请求。
  9. 使用性能分析工具:
    • 使用工具如 Laravel Debugbar、Blackfire 等来分析应用程序的性能瓶颈,并进行相应的优化。

这些优化方式可以根据具体的应用场景和需求进行选择和实施。通过综合应用这些优化策略,可以显著提升 Laravel 应用程序的性能和效率。

相关文章
|
1月前
|
SQL 关系型数据库 MySQL
轻松入门MySQL:视图之美,简化查询、提升效率的数据库利器(14)
轻松入门MySQL:视图之美,简化查询、提升效率的数据库利器(14)
|
1月前
|
NoSQL 大数据 数据处理
MongoDB聚合框架与复杂查询优化:技术深度解析
【4月更文挑战第30天】本文深入探讨了MongoDB的聚合框架和复杂查询优化技术。聚合框架包含$match、$group、$sort和$project阶段,用于数据处理和分析,提供灵活性和高性能。优化查询涉及创建合适索引、使用聚合框架、简化查询语句、限制返回结果数、避免跨分片查询、只查询所需字段及使用$inc操作符。理解这些技术有助于提升MongoDB在大数据和复杂查询场景下的性能。
|
1月前
|
存储 监控 负载均衡
InfluxDB最佳实践:数据模型设计与查询优化
【4月更文挑战第30天】本文探讨了InfluxDB的最佳实践,重点在于数据模型设计和查询优化。合理选择字段类型,根据业务逻辑划分Measurement,利用Tags进行索引优化,以及适时数据归档和清理,能有效提升性能。查询优化包括使用索引、精简查询语句、应用聚合函数及限制返回结果。分布式查询和分片适用于大规模数据集,以实现并行查询和负载均衡。这些策略旨在帮助用户优化InfluxDB的性能,进行高效时序数据分析。
|
1月前
|
SQL 监控 测试技术
SQL语法优化与最佳实践
【2月更文挑战第28天】本章将深入探讨SQL语法优化的重要性以及具体的优化策略和最佳实践。通过掌握和理解这些优化技巧,读者将能够编写出更高效、更稳定的SQL查询,提升数据库性能,降低系统资源消耗。
|
11月前
|
存储 SQL 缓存
MySQL高级第二篇(共四篇)之体系结构、存储引擎、优化SQL步骤、索引使用、SQL优化
最上层是一些客户端和链接服务,包含本地sock 通信和大多数基于客户端/服务端工具实现的类似于 TCP/IP的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安 全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
391 1
|
11月前
|
存储 监控 关系型数据库
MySQL数据库查询性能优化的技巧和经验分享
MySQL数据库查询性能优化的技巧和经验分享
|
消息中间件 缓存 NoSQL
Django开发-优化数据库实战解决方案(异步高效处理)
Django开发-优化数据库实战解决方案(异步高效处理)
159 0
|
NoSQL MongoDB 索引
软件开发入门教程网 之MongoDB 覆盖索引查询
【摘要】 本章将会讲解由于所有出现在查询中的字段是索引的一部分, MongoDB 无需在整个数据文档中检索匹配查询条件和返回使用相同索引的查询结果。 因为索引存在于RAM中,从索引中获取数据比通过扫描文档读取数据要快得多。
|
10月前
|
缓存 前端开发 PHP
PHP - Laravel The stream or file “/storage/logs/.“ could not be opened in append
PHP - Laravel The stream or file “/storage/logs/.“ could not be opened in append
432 0
|
SQL 存储 运维
【笔记】最佳实践—如何优化数据导入导出
数据库实际应用场景中经常需要进行数据导入导出,本文将介绍如何使用数据导入导出工具。
158 0