使用 Relay 解決 Redis 快取重複取用的效能問題

前言

在 PHP 專案中使用 Redis 快取時,可能會面臨 I/O 次數過多而導致效能降低的問題。接下來會介紹如何透過 Relay 機制有效地解決這個問題。

為什麼使用 Relay?

當我們直接使用 Redis 存取資料時,每次查詢都需要透過網路進行 I/O 操作。如果查詢量很大且重複性高,Redis 的 I/O 次數將會暴增,反而降低整體效能。

Relay 會將 Redis 的查詢結果保存在本地記憶體中,可以有效避免重複的網路 I/O 存取 Redis,進而提升系統效能。

Relay 的運作原理

Relay 作為一個中介層,當第一次查詢時會將結果保存在本地記憶體,後續的相同查詢會直接從本地取得資料,避免重複 I/O 操作。Relay 還能即時同步 Redis 中的資料變化,確保資料一致。

環境建立

Dockerfile
https://github.com/cachewerk/relay/blob/main/docker/ubuntu/ubuntu24.Dockerfile

安裝套件

使用 Composer 安裝套件:

1
2
3
composer init
composer require cachewerk/relay
composer require predis/predis

PHP 程式碼範例

直接使用 Redis 存取(未使用 Relay)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
require 'vendor/autoload.php';

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$key = 'users:count';

for ($i = 0; $i < 1000; $i++) {
$value = $redis->get($key);

if (!$value) {
$value = 10;
$redis->set($key, $value, 60);
}
}

echo $value;

使用 Relay 存取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
require 'vendor/autoload.php';

$relay = new Relay\Relay('127.0.0.1', 6379);

$key = 'users:count';

for ($i = 0; $i < 1000; $i++) {
$value = $relay->get($key);

if (!$value) {
$value = 10;
$relay->set($key, $value, 60);
}
}

echo $value;

透過 redis-cli 觀察 I/O 情況

使用以下指令來觀察 Redis 接收的指令數量:

1
redis-cli monitor

當你執行直接使用 Redis 的程式碼時,會看到 Redis 不斷接收大量的 GET 指令。然而,使用 Relay 後,Redis 僅會在首次查詢時接收到指令,後續查詢則幾乎沒有額外指令送達 Redis,證明 Relay 能有效降低 Redis 的 I/O 操作。

透過以上範例可以看出,Relay 會將 Redis 查詢結果保存在本地記憶體中,減少了大量重複 I/O 次數。

Relay 的資料更新機制

Relay 透過訂閱 Redis 中 key 的更新,當 Redis 資料變更時,Relay 會即時更新本地快取內容,確保資料即時一致。

1
2
3
4
5
6
7
8
9
10
$redis = new Relay('127.0.0.1', 6379);

// 從 Relay 本地記憶體取得資料
$users = $redis->get('users:count'); // 10

// 透過指令直接更新 Redis 的值,Relay 會立刻刪除本地記憶體中的快取
shell_exec('redis-cli SET users:count 50');

// 此次呼叫會直接從 Redis 取得最新值,而非從 Relay 記憶體
$users = $redis->get('users:count'); // 50

結論

Relay 能有效地解決 Redis 快取 I/O 過多的問題,並且可以自動更新快取的內容,適合有高頻率重複查詢需求的 PHP 專案。

Reference