使用AWS Elasticache Redis集群和Predis时需要注意的事项
这是关于Redis的随笔。最近我在接触它,如果有任何错误,请告诉我。
内容太长,不过只要一种选项
-
- 使用 AWS Elasticache 来构建 Redis 集群
-
- PHP 中有两个著名的 Redis 客户端实现
-
- 集群有一些注意事项
-
- 集群和数据库编号有限制
-
- 在集群中无法执行 SCAN 操作,感到困扰
- 使用 Predis 时遇到 MOVED 1234 123.456.789:1111 Predis\Response\ServerException,感到困扰
亚马逊云计算(AWS)弹性缓存
在AWS Elasticache上创建Redis实例时,可以选择启用集群选项。
如果不启用集群,则主要端点和领导者端点将被激活。
如果启用了集群,则会启用“设置终节点”。
从外观上看,主要的终点是数据库里的主节点,而读取终点类似于从节点。在写入和读取操作中使用不同的终点。
在这方面,设定终端是可以方便地进行读写操作的,因为服务器端已经进行了集群化设置,所以客户端无需特别关注。
PHP有两个Redis客户端的实现。
在处理Redis时,使用PHP的情况下,一般常见的选择是使用phpredis或Predis中的一种。
由于phpredis是一个用C实现的PHP扩展,因此可以期望它具有较快的速度。
在一方面,Predis是一个PHP的实现,所以速度可能不如本地代码,但它不需要安装扩展,因此更加便携。
Laravel 的 Redis 门面似乎默认使用 Predis 实现。但是,根据配置可以更改为 phpredis 实现。
集群的注意事项
我在聚类中遇到了困难。
如果要组成集群,与普通的独立实例(Standalone)相比,可能存在一些不同之处。
顺便说一下,这些是我在使用Predis时遇到的问题。
数据库编号
1 つ目は データベースを変更できない こと.
Redis はデータベース番号というものを持っていて,0〜15 の 16 個のデータベースを切り替えられる.
MySQL でいうとデータベースともいえそうだが,テーブルのほうが単位としては近いかもしれない.
で,クラスタを組むとデータベース番号を変更すること (SELECT) ができない.SELECT するとエラー (Predis だと例外) が出てしまい,クラスタモードだとデータベース指定ができないと言われてしまう.
未捕获的Predis\Connection\ConnectionException: SELECT操作失败: ERR SELECT在集群模式下不允许使用
因此,DB编号将被固定为0。
以下是一个例子:
参考:
Redis 集群不支持像独立版Redis那样的多个数据库。 只有一个数据库0,不允许使用SELECT命令。
Redis集群规范——https://redis.io/topics/cluster-spec#implemented-subset
扫描
这是一个可疑的问题,即使用某个条件来缩小范围并获取键值的 SCAN 功能无法使用。
不支持预设异常:无法在redis-cluster中使用“SCAN”
为什么会有疑问,因为在GUI客户端中,通常可以轻松地使用SCAN功能,无法使用SCAN功能实在是不方便且难以接受。
所以,有很大可能我的实现有问题。看来还需要再深入调查一下。
使用`scan`命令时,它仅适用于单个Redis节点。如果想在集群中使用该命令,首先获取集群中的节点列表,然后对每个节点运行`scan`命令。
在这个StackOverflow的帖子中,SCAN是针对一个节点的操作,因此需要扫描整个集群内的所有节点。
如果在服务器集群中没有人来照顾,那么是否需要获取节点列表呢……需要进行调查。
顺便提一下,Predis 的 Predis\ClientInterface 接口有一个 scan 方法,可以用该方法发出 SCAN 命令。但是,需要管理游标并循环遍历,所以在获取一定量的数据时使用迭代器会很方便(例如Predis\Collection\Iterator\KeySpace)。
→ 使用示例: https://github.com/nrk/predis/blob/v1.1.1/examples/redis_collections_iterators.php#L43-L47
当出现MOVED <端口号> <IP地址>:<端口> 的异常时
在操作时指定了集群的配置终端点,偶尔会出现 Predis\Response\ServerException 的异常,例如 MOVED 1234 123.456.789:1111。 “偶尔” 指的是每3次中有2次会出现错误,而1次成功是一种大致的频率。
总的来说,这是因为我在 Predis 的集群模式下错误地指定了连接地址。
通常情况下,当创建 Predis\Client 实例时,可以按照以下方式使用:
<?php
use Predis\Client;
function provideClient(): Client
{
$parameters = [
'host' => '<エンドポイント>',
'port' => '<ポート番号>',
'database' => 0,
];
return new Client($parameters);
}
如果在独立模式下指定一个节点,则可以使用这个,但是如果在集群模式下连接,必须在构造函数的第二个参数中指定选项关联数组。
<?php
use Predis\Client;
function provideClient(): Client
{
$parameters = [
'host' => '<エンドポイント>',
'port' => '<ポート番号>',
'database' => 0,
];
$options = [
'cluster' => 'redis',
];
return new Client($parameters, $options);
}
我原以为可以用这个来指定服务器端的集群,但事实证明这是不行的。
实际上,在集群的情况下,参数$parameters需要指定一个包含多个连接目标的数组。无论是AWS Elasticache的Redis集群还是服务器端的一个托管的端点。
<?php
use Predis\Client;
function provideClient(): Client
{
$parameters = [
[
'host' => '<エンドポイント>',
'port' => '<ポート番号>',
'database' => 0,
]
];
$options = [
'cluster' => 'redis',
];
return new Client($parameters, $options);
}
こうすることで,MOVED … もクライアント側で正しく処理されてエラーが出なくなった.
ちなみにこの原因には Laravel の Redis ファサードの実装を GitHub で確認して気づいた.Laravel さまさまである.
→ https://github.com/illuminate/redis/blob/5.1/Database.php#L31-L35
→ https://github.com/illuminate/redis/blob/5.1/Database.php的31-35行