”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 在 Laravel 中使用 Redis 进行缓存:分步指南

在 Laravel 中使用 Redis 进行缓存:分步指南

发布于2024-11-08
浏览:830

Using Redis for Caching in Laravel: A Step-by-Step Guide

Introduction

Laravel is, without fear of contradiction, the most popular PHP framework, and among the most popular within web development. Redis is, for its part, an in-memory database widely used for caching storage due to the fact that data is stored in key-value pairs, which makes its management easier. In this article we'll see how to set up Redis in a Laravel application and what is to be considered when handling cache keys.

Prerequisites

Make sure you have Redis installed on your computer. You can use Docker's official image, or install it directly on Windows, MacOS or Linux. In the case of Linux, it varies from distribution to distribution, however, the common thing is that it's available through the package manager of the operating system, usually under the name of redis or redis-cli. Once you confirm that it's installed and the connection works, let's see how to set it up in Laravel.

Installing Redis in Laravel

So far we have simply installed the Redis server in our computer, but we need to have a way to connect from a PHP application. For this there are two options, using phpredis, a PHP extension, or predis, a library. None is better than the other, they just have different objectives and scopes. phpredis has the advantage that it allows you to connect to Redis from any PHP project that is executed in that computer, whereas phpredis, being a library, only allows to connect from the projects where it's installed. In my case, I have experience with phpredis, so let's see how to install it.

Installing phpredis

The installation guide for several operating systems can be found here. Again, in Linux, if your distribution is not listed in the guide, my recommendation is to look in your distribution's documentation for the installation process. This process is often to install the package using the package manager and uncomment the extension(s) in the PHP configuration files.

Laravel configuration

The first thing we need to do is make sure that Redis configuration in config/database.php is the right one. It must look like this:

// config/database.php
'redis' => [
  'client' => env('REDIS_CLIENT', 'phpredis'),

  'options' => [
      'cluster' => env('REDIS_CLUSTER', 'redis'),
      'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
  ],

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

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

Then we must set the necessary environment variables to use Redis as a cache and connect. Here it is assumed that phpredis is used, that Redis is installed locally and not in a Docker container, that you left the default port when installing Redis, and that you didn't set up any password:

# .env
CACHE_STORE=redis
CACHE_PREFIX=laravel_cache

REDIS_CLIENT=phpredis
# For Docker, use the name of the service
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

REDIS_DB=0
REDIS_CACHE_DB=1

REDIS_URL is used when you want to specify the connection with a single URL, for example, tcp://127.0.0.1:6379?database=0. Otherwise it's needed to specify the rest of the fields so that Laravel forms this URL at the end and connects. On the other hand, the cache prefix is important because we'll have to use it when searching keys. In the case of REDIS_DB and REDIS_CACHE_DB, it's just a way to separate distinct databases depending on the purpose. The first one is the default one if Redis is used without specifying the database, while the second one only takes care of the cache.

Using Redis in Laravel

Once the configuration is ready, it's time to use Redis in our application. Laravel provides us a Illuminate\Support\Facades\Cache class as part of its facades to make all sorts of operations (if you didn't know, facade is a design pattern). It's important to note that when using these methods, it's not necessary to use the Redis prefix, since Laravel adds it automatically. The basic methods are presented below:

Get an element

use Illuminate\Support\Facades\Cache;

Cache::get('key'); // value or null

Check if a key exists

use Illuminate\Support\Facades\Cache;

if (Cache::has('key')) {
  // do something
}

Add or overwrite an element

The put method adds or overwrites a cache element, that is, if it doesn't exist it creates it, and if it exists it updates it.

use Illuminate\Support\Facades\Cache;

$minutes = 60; // 1 hour
Cache::put('key', 'value', $minutes);

As seen above, the time in minutes can be specified. If not specified, the element will persist until it is explicitly deleted.

Add several elements

use Illuminate\Support\Facades\Cache;

$minutes = 60; // 1 hour
Cache::putMany([
    'key1' => 'value1',
    'key2' => 'value2'
], $minutes);

Similar to put, with the difference that multiple elements are stored at once.

Add an element if it doesn't exist

use Illuminate\Support\Facades\Cache;

$minutes = 60; // 1 hour
Cache::add('key', 'value', $minutes);

Similar to put, with the difference that add only adds an element if it doesn't exist, so it doesn't overwrite the previous one if there is one.

Delete an element

use Illuminate\Support\Facades\Cache;

Cache::forget('key');

Clear the cache

use Illuminate\Support\Facades\Cache;

Cache::flush();

This method deletes all the cache elements, so it must be used with caution.

Get or add an element for a fixed time if it doesn't exist

The remember method is useful when getting or adding an element to the cache for a fixed time is needed if it doesn't exist yet, i. e., it doesn't overwrite and works like add. Besides this, its purpose is that by using a closure, complex operations can be done inside the function to determine the value to be obtained or added.

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

$minutes = 60; // 1 hour
$value = Cache::remember('key', $minutes, function () {
    return DB::table('users')->get();
});

Get or add an element if it doesn't exist

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

$value = Cache::rememberForever('key', function () {
    return DB::table('users')->get();
});

Similar to remember, with the difference that here no time is specified and the element persists until it is explicitly deleted.

Get and element and then delete it

use Illuminate\Support\Facades\Cache;

$value = Cache::pull('key');

This method obtains the value associated to the key, stores it on the $value variable and removes the element from the cache. Clearly, it must be used with care and always assigned to a variable so as not to lose the information. You can find more methods in the official docs.

Example of a pattern search

A common situation is that we need to search for all the keys that match a pattern, that contain a certain expression. To do this, the Cache facade is no longer used, but we use the Redis one directly, which provides more advanced methods as the ones that exist when interacting directly with the Redis server. Under the Redis syntax, an asterisk * means all. So, for example, if we want to obtain all the keys, the pattern would simply be *. Some bad examples would recommend to do the following:

use Illuminate\Support\Facades\Redis;

$found_keys = Redis::keys('*');

This would return us an array with all the keys that were found. If we wanted to get the values we would have to loop through this array and get every value using the same Redis facade, as following:

use Illuminate\Support\Facades\Redis;

$found_keys = Redis::keys('*');

// Get the values
foreach ($found_keys as $found_key) {
  $value = Redis::get($found_key);
  // Do something with the value
}

By this point you should have two questions: why is it bad to do this and why is Redis used instead of Cache to get the values. The latter is easier to answer, so I'll start there. Do you remember that I said that when using Cache Laravel handles the prefix automatically? Well, when the keys are obtained using Redis, these contain the prefix, so if we tried to find their value using Cache, it wouldn't work, since Laravel adds the prefix, but it doesn't detect if it already exists, so the result would be a key with the prefix duplicated.

Moving on to the other question, the issue with using the keys method is that it blocks the execution of the code while it searches for the keys, why? Because it gets all the keys at once, and if there are a lot, well, let's wait for it to end. So what you're doing is using PHP to operate over Redis records, that is, you're using a programming language to search between all the records of a database, instead of using the database itself to do this process. That will clearly be slower, and even more if we remember that PHP isn't a language designed for high performance applications and large volumes of data. So, what's the right approach?

In order to get Redis to do all the job instead of PHP, we have to use the scan method, which as its name mentions, scans the records and uses a cursor to have a reference of the last position where it searched for and that way it goes little by little, incrementally, moving between the keys. In other words, instead of obtaining all the keys at once, it gets a few of them and keeps looking. The same example, using scan, would look like this:

use Illuminate\Support\Facades\Redis;

$redis = Redis::connection('cache'); // Store the connection in a variable so as not to open multiple connections
$prefix = config('database.redis.options.prefix');

$cursor = null; // This is fundamental so as the scan method can loop through the records at least once
$pattern = "{$prefix}*"; // Important to include the prefix

$found_keys = [];

do {
    $response = $redis->scan($cursor, [
        'match' => $pattern,
        'count' => 300 // Adjust according to the maximum size of keys needed
    ]);

    if ($response === false) {
        break;
    }

    $cursor = $response[0];
    $results = $response[1];

    $found_keys = array_merge($found_keys, $results);
} while ($cursor !== 0); // The rule of the scan method is that it returns false once the cursor is 0

// Get the values
foreach ($found_keys as $found_key) {
  $value = $redis->get($found_key);
  // Do something with the value
}

As a bonus, let's say that we want to look for all the keys that start with test. For this, the pattern would simply be:

$pattern = "{$prefix}test*";

It's important to note the use of the asterisk because otherwise we would be searching for test exactly, and if it wasn't clear already, we must never use the keys method in production environments, we must always use scan. You can, of course, use keys locally, since it's easier to implement and you don't have much data nor a server on which the users depend, but when in production, the use of scan is mandatory.

Delete the keys using the scan method

Another common situation is that we need to search for the keys that match a pattern and then delete them. For this, the del method is available, which allows us to delete several keys at once, that is, the direct result of the scan method after each iteration. But here's a little detail, which took me hours to figure out at its time and this is my chance to say "You're welcome" so you don't lose your time too. For a reason unknown to me, the del method doesn't work if the keys include the prefix, so it must be deleted. By adjusting the previous example to remove the keys on each iteration, we have the following:

use Illuminate\Support\Facades\Redis;

$redis = Redis::connection('cache');
$prefix = config('database.redis.options.prefix');

$cursor = null;
$pattern = "{$prefix}*";

$found_keys = [];

do {
    $response = $redis->scan($cursor, [
        'match' => $pattern,
        'count' => 300
    ]);

    if ($response === false) {
        break;
    }

    $cursor = $response[0];
    $results = $response[1];

    // Delete the keys prefix (otherwise, they won't get deleted)
    $results = array_map(function ($key) use ($prefix) {
        return str_replace($prefix, '', $key);
    }, $results);

    if (count($results) > 0) {
        // Delete the keys found
        $redis->del($results);
    }
} while ($cursor !== 0);

Conclusion

Redis is a highly powerful in-memory database that can help us to implement caching mechanisms in our applications. By using it the right way, it can help to reduce response times by providing cached content instead of having to look for it in another database.

I hope you find this article useful and if you have questions or want to share something, leave it in the comments :)

版本声明 本文转载于:https://dev.to/charliet1802/using-redis-for-caching-in-laravel-a-step-by-step-guide-3j4h?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • Bootstrap 4 Beta 中的列偏移发生了什么?
    Bootstrap 4 Beta 中的列偏移发生了什么?
    Bootstrap 4 Beta:列偏移的删除和恢复Bootstrap 4 在其 Beta 1 版本中引入了重大更改柱子偏移了。然而,随着 Beta 2 的后续发布,这些变化已经逆转。从 offset-md-* 到 ml-auto在 Bootstrap 4 Beta 1 中, offset-md-*...
    编程 发布于2024-12-23
  • 在 Go 中使用 WebSocket 进行实时通信
    在 Go 中使用 WebSocket 进行实时通信
    构建需要实时更新的应用程序(例如聊天应用程序、实时通知或协作工具)需要一种比传统 HTTP 更快、更具交互性的通信方法。这就是 WebSockets 发挥作用的地方!今天,我们将探讨如何在 Go 中使用 WebSocket,以便您可以向应用程序添加实时功能。 在这篇文章中,我们将介绍: WebSoc...
    编程 发布于2024-12-23
  • 大批
    大批
    方法是可以在对象上调用的 fns 数组是对象,因此它们在 JS 中也有方法。 slice(begin):将数组的一部分提取到新数组中,而不改变原始数组。 let arr = ['a','b','c','d','e']; // Usecase: Extract till index p...
    编程 发布于2024-12-23
  • 除了“if”语句之外:还有什么地方可以在不进行强制转换的情况下使用具有显式“bool”转换的类型?
    除了“if”语句之外:还有什么地方可以在不进行强制转换的情况下使用具有显式“bool”转换的类型?
    无需强制转换即可上下文转换为 bool您的类定义了对 bool 的显式转换,使您能够在条件语句中直接使用其实例“t”。然而,这种显式转换提出了一个问题:“t”在哪里可以在不进行强制转换的情况下用作 bool?上下文转换场景C 标准指定了四种值可以根据上下文转换为的主要场景bool:语句:if、whi...
    编程 发布于2024-12-23
  • 如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    MySQL配置不正确:相对路径的问题在Django中运行python manage.py runserver时,可能会遇到以下错误:ImproperlyConfigured: Error loading MySQLdb module: dlopen(/Library/Python/2.7/site-...
    编程 发布于2024-12-23
  • 尽管代码有效,为什么 POST 请求无法捕获 PHP 中的输入?
    尽管代码有效,为什么 POST 请求无法捕获 PHP 中的输入?
    解决 PHP 中的 POST 请求故障在提供的代码片段中:action=''而不是:action="<?php echo $_SERVER['PHP_SELF'];?>";?>"检查 $_POST数组:表单提交后使用 var_dump 检查 $_POST 数...
    编程 发布于2024-12-23
  • 如何在 PHP 中组合两个关联数组,同时保留唯一 ID 并处理重复名称?
    如何在 PHP 中组合两个关联数组,同时保留唯一 ID 并处理重复名称?
    在 PHP 中组合关联数组在 PHP 中,将两个关联数组组合成一个数组是一项常见任务。考虑以下请求:问题描述:提供的代码定义了两个关联数组,$array1 和 $array2。目标是创建一个新数组 $array3,它合并两个数组中的所有键值对。 此外,提供的数组具有唯一的 ID,而名称可能重合。要求...
    编程 发布于2024-12-23
  • 插入数据时如何修复“常规错误:2006 MySQL 服务器已消失”?
    插入数据时如何修复“常规错误:2006 MySQL 服务器已消失”?
    插入记录时如何解决“一般错误:2006 MySQL 服务器已消失”介绍:将数据插入 MySQL 数据库有时会导致错误“一般错误:2006 MySQL 服务器已消失”。当与服务器的连接丢失时会出现此错误,通常是由于 MySQL 配置中的两个变量之一所致。解决方案:解决此错误的关键是调整wait_tim...
    编程 发布于2024-12-23
  • 如何使用 MySQL 查找今天生日的用户?
    如何使用 MySQL 查找今天生日的用户?
    如何使用 MySQL 识别今天生日的用户使用 MySQL 确定今天是否是用户的生日涉及查找生日匹配的所有行今天的日期。这可以通过一个简单的 MySQL 查询来实现,该查询将存储为 UNIX 时间戳的生日与今天的日期进行比较。以下 SQL 查询将获取今天有生日的所有用户: FROM USERS ...
    编程 发布于2024-12-23
  • 为什么我的 Angular HTTP POST 值在 PHP 中未定义,如何修复它?
    为什么我的 Angular HTTP POST 值在 PHP 中未定义,如何修复它?
    Angular HTTP POST 到 PHP:处理未定义的 POST 值在 AngularJS 中,对 PHP 端点执行 HTTP POST 请求有时会导致未定义的值服务器端的 POST 值。当预期数据格式与 Angular 应用程序发送的实际数据不匹配时,就会发生这种情况。要解决此问题,确保正确...
    编程 发布于2024-12-23
  • Go可以访问初始标准输入流吗?
    Go可以访问初始标准输入流吗?
    在 Go 中,您可以访问初始标准输入吗?在 Go 中,使用 os.Stdin 从原始标准输入读取应该会产生所需的结果,如图所示通过这个代码片段:package main import "os" import "log" import "io"...
    编程 发布于2024-12-23
  • 极简密码管理器桌面应用程序:进军 Golang 的 Wails 框架(第 2 部分)
    极简密码管理器桌面应用程序:进军 Golang 的 Wails 框架(第 2 部分)
    Hi again, coders! In the first part of this short series we saw the creation and operation of a desktop application to store and encrypt our passwords...
    编程 发布于2024-12-23
  • ES6 React 组件:何时使用基于类与函数式?
    ES6 React 组件:何时使用基于类与函数式?
    在 ES6 基于类和函数式 ES6 React 组件之间做出选择使用 React 时,开发人员面临着使用 ES6 基于类的选择组件或功能 ES6 组件。了解每种类型的适当用例对于最佳应用程序开发至关重要。函数式 ES6 组件:无状态和简单函数式组件是无状态的,这意味着它们不维护任何内部状态。他们只是...
    编程 发布于2024-12-23
  • 如何在 PHP 中找到两个平面数组之间的唯一值?
    如何在 PHP 中找到两个平面数组之间的唯一值?
    在平面数组之间查找唯一值给定两个数组,任务是确定仅存在于其中一个数组中的值。此操作通常称为查找两个集合之间的差异。在 PHP 中,您可以利用 array_merge、array_diff 和 array_diff 函数来实现此操作。详细解法如下:$array1 = [64, 98, 112, 92,...
    编程 发布于2024-12-23
  • CSS 可以在内联块元素中本机插入换行符吗?
    CSS 可以在内联块元素中本机插入换行符吗?
    CSS 在行内块元素中插入换行符:理论探索在不断发展的 Web 开发领域,这种能力操纵内容流仍然是最重要的。经常出现的一个特殊挑战涉及在内联块元素中插入换行符。考虑以下 HTML 结构:<h3 id="features">Features</h3> <...
    编程 发布于2024-12-23

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3