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

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

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

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]删除
最新教程 更多>
  • Android如何向PHP服务器发送POST数据?
    Android如何向PHP服务器发送POST数据?
    在android apache httpclient(已弃用) httpclient httpclient = new defaulthttpclient(); httppost httppost = new httppost(“ http://www.yoursite.com/script.p...
    编程 发布于2025-07-20
  • 如何使用Python的请求和假用户代理绕过网站块?
    如何使用Python的请求和假用户代理绕过网站块?
    如何使用Python的请求模拟浏览器行为,以及伪造的用户代理提供了一个用户 - 代理标头一个有效方法是提供有效的用户式header,以提供有效的用户 - 设置,该标题可以通过browser和Acterner Systems the equestersystermery和操作系统。通过模仿像Chro...
    编程 发布于2025-07-20
  • 如何有效地转换PHP中的时区?
    如何有效地转换PHP中的时区?
    在PHP 利用dateTime对象和functions DateTime对象及其相应的功能别名为时区转换提供方便的方法。例如: //定义用户的时区 date_default_timezone_set('欧洲/伦敦'); //创建DateTime对象 $ dateTime = ne...
    编程 发布于2025-07-20
  • Java字符串非空且非null的有效检查方法
    Java字符串非空且非null的有效检查方法
    检查字符串是否不是null而不是空的if (str != null && !str.isEmpty())Option 2: str.length() == 0For Java versions prior to 1.6, str.length() == 0 can be二手: if(str!= n...
    编程 发布于2025-07-20
  • Go语言垃圾回收如何处理切片内存?
    Go语言垃圾回收如何处理切片内存?
    Garbage Collection in Go Slices: A Detailed AnalysisIn Go, a slice is a dynamic array that references an underlying array.使用切片时,了解垃圾收集行为至关重要,以避免潜在的内存泄...
    编程 发布于2025-07-20
  • 为什么我会收到MySQL错误#1089:错误的前缀密钥?
    为什么我会收到MySQL错误#1089:错误的前缀密钥?
    mySQL错误#1089:错误的前缀键错误descript [#1089-不正确的前缀键在尝试在表中创建一个prefix键时会出现。前缀键旨在索引字符串列的特定前缀长度长度,以便更快地搜索这些前缀。理解prefix keys `这将在整个Movie_ID列上创建标准主键。主密钥对于唯一识别...
    编程 发布于2025-07-20
  • 如何使用node-mysql在单个查询中执行多个SQL语句?
    如何使用node-mysql在单个查询中执行多个SQL语句?
    在node-mysql node-mysql文档最初出于安全原因最初禁用多个语句支持,因为它可能导致SQL注入攻击。要启用此功能,您需要在创建连接时将倍增设置设置为true: var connection = mysql.createconnection({{multipleStatement:...
    编程 发布于2025-07-20
  • 如何使用替换指令在GO MOD中解析模块路径差异?
    如何使用替换指令在GO MOD中解析模块路径差异?
    在使用GO MOD时,在GO MOD 中克服模块路径差异时,可能会遇到冲突,其中3个Party Package将另一个PAXPANCE带有导入式套件之间的另一个软件包,并在导入式套件之间导入另一个软件包。如回声消息所证明的那样: go.etcd.io/bbolt [&&&&&&&&&&&&&&&&...
    编程 发布于2025-07-20
  • MySQL中如何高效地根据两个条件INSERT或UPDATE行?
    MySQL中如何高效地根据两个条件INSERT或UPDATE行?
    在两个条件下插入或更新或更新 solution:的答案在于mysql的插入中...在重复键更新语法上。如果不存在匹配行或更新现有行,则此功能强大的功能可以通过插入新行来进行有效的数据操作。如果违反了唯一的密钥约束。实现所需的行为,该表必须具有唯一的键定义(在这种情况下为'名称'...
    编程 发布于2025-07-20
  • 在Java中使用for-to-loop和迭代器进行收集遍历之间是否存在性能差异?
    在Java中使用for-to-loop和迭代器进行收集遍历之间是否存在性能差异?
    For Each Loop vs. Iterator: Efficiency in Collection TraversalIntroductionWhen traversing a collection in Java, the choice arises between using a for-...
    编程 发布于2025-07-20
  • 解决MySQL插入Emoji时出现的\\"字符串值错误\\"异常
    解决MySQL插入Emoji时出现的\\"字符串值错误\\"异常
    Resolving Incorrect String Value Exception When Inserting EmojiWhen attempting to insert a string containing emoji characters into a MySQL database us...
    编程 发布于2025-07-20
  • PHP未来:适应与创新
    PHP未来:适应与创新
    PHP的未来将通过适应新技术趋势和引入创新特性来实现:1)适应云计算、容器化和微服务架构,支持Docker和Kubernetes;2)引入JIT编译器和枚举类型,提升性能和数据处理效率;3)持续优化性能和推广最佳实践。 引言在编程世界中,PHP一直是网页开发的中流砥柱。作为一个从1994年就开始发展...
    编程 发布于2025-07-20
  • 如何使用“ JSON”软件包解析JSON阵列?
    如何使用“ JSON”软件包解析JSON阵列?
    parsing JSON与JSON软件包 QUALDALS:考虑以下go代码:字符串 } func main(){ datajson:=`[“ 1”,“ 2”,“ 3”]`` arr:= jsontype {} 摘要:= = json.unmarshal([] byte(...
    编程 发布于2025-07-20
  • 为什么不````''{margin:0; }`始终删除CSS中的最高边距?
    为什么不````''{margin:0; }`始终删除CSS中的最高边距?
    在CSS 问题:不正确的代码: 全球范围将所有余量重置为零,如提供的代码所建议的,可能会导致意外的副作用。解决特定的保证金问题是更建议的。 例如,在提供的示例中,将以下代码添加到CSS中,将解决余量问题: body H1 { 保证金顶:-40px; } 此方法更精确,避免了由全局保证金重置引...
    编程 发布于2025-07-20

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

Copyright© 2022 湘ICP备2022001581号-3