본문으로 건너뛰기
버전: 9.x

레디스 (Redis)

소개

Redis는 오픈 소스의 고급 키-값 저장소입니다. 키에 문자열(string), 해시(hashes), 리스트(lists), 셋(sets), 정렬된 셋(sorted sets) 등 다양한 데이터 구조를 저장할 수 있어서 종종 데이터 구조 서버라고도 불립니다.

라라벨에서 Redis를 사용하려면 phpredis PHP 확장 프로그램을 PECL을 통해 설치해서 사용하는 것을 권장합니다. 이 확장 프로그램은 일반 사용자용 PHP 패키지에 비해 설치가 다소 복잡하지만, Redis를 많이 활용하는 애플리케이션에서는 더 나은 성능을 기대할 수 있습니다. 만약 Laravel Sail을 사용한다면, 해당 확장 프로그램이 이미 애플리케이션의 Docker 컨테이너에 설치되어 있습니다.

phpredis 확장 프로그램을 설치할 수 없는 경우, Composer를 통해 predis/predis 패키지를 설치하여 사용할 수 있습니다. Predis는 PHP로만 작성된 Redis 클라이언트로, 별도의 추가 확장 프로그램이 필요하지 않습니다:

composer require predis/predis

설정

애플리케이션의 Redis 설정은 config/database.php 설정 파일에서 관리할 수 있습니다. 이 파일에는 애플리케이션에서 사용할 Redis 서버 정보를 담고 있는 redis 배열이 있습니다:

'redis' => [

'client' => env('REDIS_CLIENT', 'phpredis'),

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

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

],

설정 파일에서 정의하는 각 Redis 서버에는 이름, 호스트, 포트 정보가 필요합니다. 대신, Redis 연결을 하나의 URL로 표현하고자 할 때는 해당 정보를 명시하지 않아도 됩니다:

'redis' => [

'client' => env('REDIS_CLIENT', 'phpredis'),

'default' => [
'url' => 'tcp://127.0.0.1:6379?database=0',
],

'cache' => [
'url' => 'tls://user:[email protected]:6380?database=1',
],

],

연결 방식(scheme) 설정하기

기본적으로 Redis 클라이언트는 Redis 서버와 연결할 때 tcp 방식을 사용합니다. 하지만, scheme 설정 옵션을 지정해서 TLS/SSL 암호화 연결도 사용할 수 있습니다. 이 옵션은 각 Redis 서버의 설정 배열에 포함시킵니다:

'redis' => [

'client' => env('REDIS_CLIENT', 'phpredis'),

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

],

클러스터

애플리케이션에서 여러 대의 Redis 서버로 클러스터를 구성하여 사용한다면, Redis 설정에서 clusters 키를 작성하여 클러스터를 정의해야 합니다. 이 설정 키는 기본적으로 존재하지 않으므로, 애플리케이션의 config/database.php 파일 내에 직접 추가해야 합니다:

'redis' => [

'client' => env('REDIS_CLIENT', 'phpredis'),

'clusters' => [
'default' => [
[
'host' => env('REDIS_HOST', 'localhost'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
],
],
],

],

기본적으로 클러스터는 클라이언트-사이드 샤딩(Client-side Sharding)을 이용해서 여러 노드에 데이터를 분산 저장하여, 많은 RAM을 사용할 수 있도록 해줍니다. 그러나 클라이언트-사이드 샤딩은 장애 발생 시 자동 복구(Failover)를 지원하지 않기 때문에, 다른 주 데이터 저장소와 연동되는 임시 캐시 데이터 저장용으로 주로 사용됩니다.

클라이언트-사이드 샤딩이 아닌, Redis의 네이티브 클러스터링 기능을 사용하려면, options.cluster 설정 값을 redis로 변경해야 합니다. 이 설정도 애플리케이션의 config/database.php 파일에 추가해야 합니다:

'redis' => [

'client' => env('REDIS_CLIENT', 'phpredis'),

'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
],

'clusters' => [
// ...
],

],

Predis

애플리케이션에서 Predis 패키지를 이용해 Redis와 상호작용하려면, REDIS_CLIENT 환경 변수 값에 predis를 설정해야 합니다:

'redis' => [

'client' => env('REDIS_CLIENT', 'predis'),

// ...
],

Predis는 기본 host, port, database, password 외에도, 각 Redis 서버별로 추가적인 연결 파라미터를 지원합니다. 이 추가 설정 옵션들은 애플리케이션의 config/database.php 파일에서 각 Redis 서버 설정에 추가할 수 있습니다:

'default' => [
'host' => env('REDIS_HOST', 'localhost'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
'read_write_timeout' => 60,
],

Redis 퍼사드(Facade) 별칭

라라벨의 config/app.php 설정 파일에는 프레임워크에서 등록할 클래스 별칭(alias)들이 정의된 aliases 배열이 있습니다. 기본적으로 Redis 별칭은 phpredis 확장 프로그램이 제공하는 Redis 클래스명과 충돌할 수 있어서 포함되어 있지 않습니다. Predis 클라이언트를 사용하고 별칭을 추가하고 싶다면, config/app.phpaliases 배열에 아래와 같이 추가하면 됩니다:

'aliases' => Facade::defaultAliases()->merge([
'Redis' => Illuminate\Support\Facades\Redis::class,
])->toArray(),

phpredis

기본적으로 라라벨은 phpredis 확장 프로그램을 통해 Redis와 통신합니다. 실제로 라라벨이 어떤 클라이언트로 Redis와 통신할지는 redis.client 설정 값에 따라 결정되며, 이 값은 보통 REDIS_CLIENT 환경 변수와 연동됩니다:

'redis' => [

'client' => env('REDIS_CLIENT', 'phpredis'),

// 나머지 Redis 설정...
],

phpredis는 기본 scheme, host, port, database, password 외에도 다음과 같은 추가 연결 파라미터를 지원합니다: name, persistent, persistent_id, prefix, read_timeout, retry_interval, timeout, context. 이 중 필요한 옵션을 config/database.php 설정 파일 내 Redis 서버 설정에 자유롭게 추가할 수 있습니다:

'default' => [
'host' => env('REDIS_HOST', 'localhost'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
'read_timeout' => 60,
'context' => [
// 'auth' => ['username', 'secret'],
// 'stream' => ['verify_peer' => false],
],
],

phpredis 직렬화 및 압축

phpredis 확장 프로그램은 다양한 직렬화(Serialization) 및 압축(Compression) 알고리즘을 지원하며, 이는 Redis 설정의 options 배열에서 지정할 수 있습니다:

'redis' => [

'client' => env('REDIS_CLIENT', 'phpredis'),

'options' => [
'serializer' => Redis::SERIALIZER_MSGPACK,
'compression' => Redis::COMPRESSION_LZ4,
],

// 나머지 Redis 설정...
],

지원되는 직렬화 알고리즘은 다음과 같습니다: Redis::SERIALIZER_NONE(기본값), Redis::SERIALIZER_PHP, Redis::SERIALIZER_JSON, Redis::SERIALIZER_IGBINARY, Redis::SERIALIZER_MSGPACK.

지원되는 압축 알고리즘은 다음과 같습니다: Redis::COMPRESSION_NONE(기본값), Redis::COMPRESSION_LZF, Redis::COMPRESSION_ZSTD, Redis::COMPRESSION_LZ4.

Redis와 상호작용하기

퍼사드Redis를 통해 다양한 Redis 메서드를 호출하여 Redis와 직접 상호작용할 수 있습니다. Redis 퍼사드는 다이나믹 메서드를 지원하므로, 어떤 Redis 명령어든 그대로 퍼사드에 호출하면 해당 명령어가 Redis 서버로 전달됩니다. 아래 예시에서는 Redis의 GET 명령어를 Redis 퍼사드의 get 메서드로 호출하는 방법을 보여줍니다:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Redis;

class UserController extends Controller
{
/**
* Show the profile for the given user.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
return view('user.profile', [
'user' => Redis::get('user:profile:'.$id)
]);
}
}

위에서 설명한 것처럼, Redis 퍼사드를 통해 어떤 Redis 명령어도 호출할 수 있습니다. 라라벨은 매직 메서드(magic method)를 사용해서 명령어를 Redis 서버로 전달합니다. 만약 Redis 명령어에 인수가 필요한 경우, 해당 인수들을 퍼사드의 해당 메서드에 그대로 전달하면 됩니다:

use Illuminate\Support\Facades\Redis;

Redis::set('name', 'Taylor');

$values = Redis::lrange('names', 5, 10);

또 다른 방법으로, Redis 퍼사드의 command 메서드를 활용해 서버로 명령어를 직접 전달할 수도 있습니다. 이 방법은 첫 번째 인자로 명령어 이름, 두 번째 인자로 값들의 배열을 받습니다:

$values = Redis::command('lrange', ['name', 5, 10]);

여러 Redis 연결 사용하기

애플리케이션의 config/database.php 설정 파일에서는 여러 개의 Redis 연결(서버)을 정의할 수 있습니다. 특정 Redis 연결을 사용하려면, Redis 퍼사드의 connection 메서드를 이용하면 됩니다:

$redis = Redis::connection('connection-name');

기본 Redis 연결 인스턴스를 얻고 싶다면 인자 없이 connection 메서드를 호출하면 됩니다:

$redis = Redis::connection();

트랜잭션

Redis 퍼사드의 transaction 메서드는 Redis의 기본 MULTIEXEC 명령어를 래핑해서 편리하게 트랜잭션을 사용할 수 있도록 지원합니다. transaction 메서드는 클로저(closure)를 인자로 받는데, 이 클로저는 Redis 연결 인스턴스를 전달받으며, 이 인스턴스에 원하는 Redis 명령어를 자유롭게 사용할 수 있습니다. 클로저 내에서 실행된 모든 Redis 명령어는 하나의 원자적(atomic) 트랜잭션으로 실행됩니다.

use Illuminate\Support\Facades\Redis;

Redis::transaction(function ($redis) {
$redis->incr('user_visits', 1);
$redis->incr('total_visits', 1);
});

[!WARNING] Redis 트랜잭션을 정의할 때는 명령어 실행 중간에 Redis로부터 값을 조회할 수 없습니다. 트랜잭션은 하나의 원자적 연산으로 처리되므로, 클로저가 모든 명령어를 실행한 뒤에야 실제로 실행됩니다.

Lua 스크립트

eval 메서드를 사용하면 여러 Redis 명령어를 하나의 원자적 작업으로 처리할 수 있는 또 다른 방법을 제공합니다. 특히, eval 메서드는 해당 작업 중에 Redis의 키 값을 조회하거나 조작할 수 있는 장점이 있습니다. Redis 스크립트는 Lua 프로그래밍 언어로 작성합니다.

eval 메서드는 여러 인수를 받습니다. 먼저, 실행할 Lua 스크립트 문자열을 전달해야 합니다. 두 번째는 스크립트가 참조하는 키의 개수(정수)를 지정합니다. 세 번째로는 그 키 이름(들)을 나열합니다. 추가적으로 스크립트 내부에서 사용할 수 있는 기타 인수들도 뒤에 전달할 수 있습니다.

아래 예시는 카운터 값을 증가시키고, 그 값이 5보다 크면 두 번째 카운터를 추가로 증가시키며, 마지막으로 첫 번째 카운터 값을 반환합니다:

$value = Redis::eval(<<<'LUA'
local counter = redis.call("incr", KEYS[1])

if counter > 5 then
redis.call("incr", KEYS[2])
end

return counter
LUA, 2, 'first-counter', 'second-counter');

[!WARNING] Redis 스크립팅에 관한 더 자세한 내용은 Redis 공식 문서를 참고하시기 바랍니다.

파이프라인 명령어 실행

한 번에 수십 개의 Redis 명령어를 실행해야 할 때, 각 명령어마다 서버에 네트워크 요청을 반복적으로 보내는 대신 pipeline 메서드를 사용하면 효율적으로 처리할 수 있습니다. pipeline은 하나의 인자(클로저)를 받으며, 이 클로저는 Redis 인스턴스를 전달받습니다. 모든 명령어는 이 인스턴스에 대해 실행되고, 명령어들은 한꺼번에 Redis 서버로 전송되어 반복적인 네트워크 왕복을 줄일 수 있습니다. 명령어는 발행된 순서대로 실행됩니다:

use Illuminate\Support\Facades\Redis;

Redis::pipeline(function ($pipe) {
for ($i = 0; $i < 1000; $i++) {
$pipe->set("key:$i", $i);
}
});

Pub / Sub

라라벨은 Redis의 publishsubscribe 명령어를 손쉽게 사용할 수 있는 인터페이스를 제공합니다. 이 명령어들을 사용하면 특정 "채널"에서 메시지를 실시간으로 주고받을 수 있습니다. 다른 애플리케이션이나 심지어 다른 프로그래밍 언어로도 자유롭게 채널에 메시지를 발행(publish)할 수 있기 때문에, 여러 애플리케이션과 프로세스 간의 통신이 매우 쉽습니다.

먼저, subscribe 메서드를 활용해 채널 리스너(listener)를 설정해봅니다. subscribe 메서드는 프로그램이 계속 실행되는 장기 실행 프로세스이기 때문에 Artisan 명령어 안에 구현하는 것이 보통입니다:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;

class RedisSubscribe extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'redis:subscribe';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Subscribe to a Redis channel';

/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
Redis::subscribe(['test-channel'], function ($message) {
echo $message;
});
}
}

이제 publish 메서드를 사용해서 해당 채널에 메시지를 발행할 수 있습니다:

use Illuminate\Support\Facades\Redis;

Route::get('/publish', function () {
// ...

Redis::publish('test-channel', json_encode([
'name' => 'Adam Wathan'
]));
});

와일드카드(전체 수신) 구독

psubscribe 메서드를 이용하면 와일드카드 패턴을 사용해 여러 채널의 모든 메시지를 구독할 수 있습니다. 이렇게 하면 모든 채널, 혹은 특정 패턴의 채널 메시지를 한 번에 수신할 수 있습니다. 클로저에는 두 번째 인자로 채널 이름이 전달됩니다.

Redis::psubscribe(['*'], function ($message, $channel) {
echo $message;
});

Redis::psubscribe(['users.*'], function ($message, $channel) {
echo $message;
});