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

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

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

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]删除
最新教程 更多>
  • 如何在不离开当前页面的情况下打开下载窗口?
    如何在不离开当前页面的情况下打开下载窗口?
    在不离开当前页面的情况下打开下载窗口的最简单方法要在不离开当前页面或打开弹出窗口的情况下打开下载对话框,使用以下跨浏览器 JavaScript:window.location.assign(url);此方法不会打开新窗口或选项卡,即使在 Internet Explorer 6 中也能正常工作。通过将...
    编程 发布于2024-11-08
  • 如何在 C++ 中使用虚拟模板方法实现多态性?
    如何在 C++ 中使用虚拟模板方法实现多态性?
    C 虚拟模板方法在 C 中,将静态时间多态性(模板)与运行时多态性结合起来可能具有挑战性。这在以下抽象类中很明显:class AbstractComputation { public: template <class T> virtual void setData...
    编程 发布于2024-11-08
  • 多线程:工程师的关键概念 - 第 1 部分
    多线程:工程师的关键概念 - 第 1 部分
    了解关键的多线程概念对于软件开发人员至关重要,因为它不仅可以增强技能,还可以直接影响应用程序开发、可扩展性和软件解决方案的整体质量。 原子性 在多线程上下文中,原子操作确保一个线程可以执行一系列操作而不会被其他线程中断。多个线程可能会尝试同时读取或写入共享数据。如果没有原子性,并发...
    编程 发布于2024-11-08
  • 后端开发路线图
    后端开发路线图
    对于所有有抱负的后端开发人员,我知道导航您的学习道路是多么令人畏惧。这就是为什么我很高兴能够提供详细的路线图来帮助您掌握用于后端开发的 JavaScript!? 1️⃣ 从基础开始:熟悉 JavaScript 基础知识。理解变量、函数和控制结构至关重要。 2️⃣ 深入了解 Node.js:这个强大的...
    编程 发布于2024-11-08
  • 如何在Go中调用Linux共享库函数?
    如何在Go中调用Linux共享库函数?
    在 Go 中调用 Linux 共享库函数在此问题中,开发人员寻求从共享对象 (.so) 调用函数的帮助他们的 Go 代码中的文件。 Python 中的 ctypes 包可以访问 C 函数,可以作为所需的功能。使用 cgo 进行静态共享库加载静态调用函数在编译时已知共享库,可以使用 cgo 包。以下是...
    编程 发布于2024-11-08
  • Java 中的字符串文字是否会被垃圾收集?
    Java 中的字符串文字是否会被垃圾收集?
    字符串文字的垃圾收集Q1:如果一个字符串在编译时被定义为文字(例如,String str = "java"),它会被垃圾收集吗?通常不会。代码对象包含对文字 String 对象的引用,使它们保持可访问性。垃圾回收仅在代码对象变得不可访问时发生。Q2:如果使用 intern 方法(...
    编程 发布于2024-11-08
  • 跨平台使用 RSA 加密和解密保护数据
    跨平台使用 RSA 加密和解密保护数据
    RSA加密简介 在当今的数字环境中,保护敏感数据对于个人和组织都至关重要。 RSA (Rivest-Shamir-Adleman) 加密作为保护数据的强大解决方案脱颖而出。它是一种非对称加密算法,这意味着它使用一对密钥:用于加密的公钥和用于解密的私钥。 RSA 加密的主要好处之一是...
    编程 发布于2024-11-08
  • 如何利用导师在数字营销中实现职业发展
    如何利用导师在数字营销中实现职业发展
    在当今快速发展的数字营销环境中,保持领先趋势并提高您的技能至关重要。加速该领域职业发展的最有效方法之一是利用指导。无论您是刚刚起步还是希望晋升为领导角色,数字营销导师都可以提供宝贵的指导和见解,帮助您实现职业目标。本文探讨了指导如何塑造您的职业轨迹,并提供了充分利用指导经验的策略。 ...
    编程 发布于2024-11-08
  • 将 Cloudinary 集成到 Next.js 应用程序中
    将 Cloudinary 集成到 Next.js 应用程序中
    了解 Cloudinary 及其定价。 1. 创建一个Cloudinary账户 如果您没有帐户,请在 Cloudinary 注册并创建一个新帐户。 2.安装Cloudinary SDK 您可以使用npm或yarn安装Cloudinary SDK: npm instal...
    编程 发布于2024-11-08
  • 我们如何在 CSS 中对后代元素进行分组?
    我们如何在 CSS 中对后代元素进行分组?
    用于后代分组的难以捉摸的 CSS 选择器虽然 CSS 为样式元素提供了强大的选项,但一个值得注意的缺失是轻松对后代进行分组的能力。当对 HTML 表格等复杂元素进行样式设计时,这种限制变得很明显,其中为列标题和单元格分配相同的样式需要繁琐的选择器。后代样式设计的困境考虑下面的表示例:<tabl...
    编程 发布于2024-11-08
  • ## 如何在 PHP 中可靠地确定目录是否为空?
    ## 如何在 PHP 中可靠地确定目录是否为空?
    在 PHP 中验证目录是否为空确定目录是否为空可能是各种 Web 开发场景中的一项重要任务。但是,某些脚本可能会遇到问题,即输出错误地建议空或非空目录,尽管其中存在或不存在文件。原始脚本提供的脚本尝试使用以下代码检查目录是否为空:$q = (count(glob("$dir/*"...
    编程 发布于2024-11-08
  • `&deployment` 指针如何实现 Kubernetes 中的 `runtime.Object` 接口?
    `&deployment` 指针如何实现 Kubernetes 中的 `runtime.Object` 接口?
    &deployment指针如何满足运行时对象接口在Kubernetes代码中,kubectl/run.go中的Generate函数返回包含runtime.Object 和错误的结果列表。函数最后一行,return &deployment, nil,将局部变量deployment的地址赋值给r...
    编程 发布于2024-11-08
  • 用于带或不带协议的 URL 匹配的通用正则表达式
    用于带或不带协议的 URL 匹配的通用正则表达式
    匹配带或不带协议的 URL 的正则表达式对于可能包含或不包含“http://www”前缀的 URL ,可以使用正则表达式来执行匹配操作。下面提供了详细的正则表达式模式:((https?|ftp)://)?([a-z0-9 !*(),;?&amp=$_.-] (:[a-z0-9 !*(),;?...
    编程 发布于2024-11-08
  • 如何防止 Foreach 循环输出中出现尾随逗号?
    如何防止 Foreach 循环输出中出现尾随逗号?
    消除 Foreach 循环中的尾随逗号在编程中,通常需要迭代项目列表并单独输出每个项目通过分隔符,例如逗号。但是,当使用 foreach 循环执行此任务时,您可能会遇到最后一项后面出现尾随逗号的问题。考虑以下 PHP 代码:foreach ($this->sinonimo as $s){ ...
    编程 发布于2024-11-08
  • LZ C++ React Native 绑定,实现极快的压缩算法
    LZ C++ React Native 绑定,实现极快的压缩算法
    我最近开始涉足 JSI 和 C,因此,我必须构建一个名为react-native-lz4 的小包。它是一个在 React Native 中使用 C 编写的 LZ4 算法进行快速文件压缩的​​库。 它仍处于实验阶段,因为我仍在完善错误处理并扩展其 API,但它已经可以使用(谨慎!) 包:https:/...
    编程 发布于2024-11-08

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

Copyright© 2022 湘ICP备2022001581号-3