一. 概述
目前我们用到的 php 的 redis 扩展 主要有2个,
- phpredis, 它是用c写的php的高效扩展:https://github.com/phpredis/phpredis,
- predis, 它是用php代码写的,也用的蛮多的:https://github.com/nrk/predis。
二. phpredis(PHP扩展)方式
1. phpredis单机方式
<?php
$client = new Redis();
$client->connect('10.30.5.163', '7000');
echo $client->get('new_item_key:d89b561fb759fd533a8c2781ef15dd5f');
2. phpredis集群使用
https://github.com/phpredis/phpredis/blob/feature/redis_cluster/cluster.markdown
<?php
$redis_list = ['10.30.5.162:7000','10.30.5.163:7000','10.30.5.163:7001'];
$client = new RedisCluster(NUll,$redis_list);
echo $client->get('new_item_key:d89b561fb759fd533a8c2781ef15dd5f');
- 代码说明
第一个参数传NULL 别问我,我也不知道为啥。反正文档没找到,这篇也没看懂。
第二个参数是我们需要连接的redis cluster的master服务器列表。我们有3个master,就填3个, 填一个主节点也行, 甚至填一个从节点也行, 但是性能有差异, 见第四部分
3. 集群原理
为甚么填入任何一个节点地址都可以操作redisCluster呢?
-
在集群模式下,Redis接收任何键相关命令时首先计算键对应的槽,
假如初始化的是从节点, 首先会向从节点发送redis命令,
从节点根据槽找出所对应的节点,如果节点是自身,则处理键命令;
如果不是自身, 则MOVED重定向错误,通知客户端请求正确的节点。这个过程称为MOVED重定向
redis开发和运维.png
- 重定向信息包含了键所对应的槽以及负责该槽的节点地址,根据这些信
息客户端就可以向正确的节点发起请求 - phpredis客户端可以根据重定向信息直接再次向键所在节点发起请求, 从而获取数据
图片来源: <<redis开发和运维>>
4. 设定超时
<?php
$redis_list = ['10.30.5.162:7000','10.30.5.163:7000','10.30.5.163:7001', 1.5, 1.5];
$client = new RedisCluster(NUll,$redis_list);
echo $client->get('new_item_key:d89b561fb759fd533a8c2781ef15dd5f');
timeout和read_timeout功能。就是加到master列表的后面。
timeout表示连接redis的最长时间,这里设为1.5秒,表示超过1.5秒要是还没连接成功就返回false 。
read_timeout表示连接redis成功后,读取一个key的超时时间,有时候读取一个key 可能value比较大,读取需要很长时间,这里设置1.5秒,表示要是过了1.5秒还没读取到数据就返回false。
三. PRedis方式
predis是一套用php代码写的php连接redis的扩展
<?php
use Predis\Client;
require __DIR__ . '/../vendor/autoload.php';
// 写一个节点也可以
$redis_list = [
'tcp://10.30.5.163:7000',
'tcp://10.30.5.163:7001',
'tcp://10.30.5.162:7000'
];
$redis = new Client($redis_list, ['cluster'=>'redis']);
echo $redis->get('new_item_key:d89b561fb759fd533a8c2781ef15dd5f');
四. phpredis和Predis性能对比
-
使用ab压测, 获取key
/usr/local/apache2/bin/ab -n10000 -c100 http://10.30.5.162/redis.php
-
key:
new_item_key:d89b561fb759fd533a8c2781ef15dd5f
分布在10.30.5.163:7000节点 -
php.ini开启opcache
realpath_cache_size = 2M
[opcache]
zend_extension=opcache.so
opcache.enable=1
opcache.enable_cli=1
opcache.optimization_level=-1
opcache.fast_shutdown=1
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.use_cwd=1
opcache.max_accelerated_files=100000
opcache.max_wasted_percentage=5
opcache.memory_consumption=128
opcache.consistency_checks=0
opcache.huge_code_pages=1
1. phpredis填入三个主节点
<?php
$redis_list = ['10.30.5.162:7000','10.30.5.163:7000','10.30.5.163:7001',1.5,1.5];
$client = new RedisCluster(NUll,$redis_list);
echo $client->get('new_item_key:d89b561fb759fd533a8c2781ef15dd5f');
测试结果
Concurrency Level: 100
Time taken for tests: 2.203 seconds
Complete requests: 10000
Failed requests: 12
(Connect: 0, Receive: 0, Length: 12, Exceptions: 0)
Total transferred: 2506988 bytes
HTML transferred: 509388 bytes
Requests per second: 4539.89 [#/sec] (mean)
Time per request: 22.027 [ms] (mean)
Time per request: 0.220 [ms] (mean, across all concurrent requests)
Transfer rate: 1111.47 [Kbytes/sec] received
吞吐量4500左右
2. phpredis只填入一个从节点
<?php
$redis_list = ['10.30.5.161:7000'];
$client = new RedisCluster(NUll,$redis_list);
echo $client->get('new_item_key:d89b561fb759fd533a8c2781ef15dd5f');
每次都会重定向
Concurrency Level: 100
Time taken for tests: 9.726 seconds
Complete requests: 10000
Failed requests: 77
(Connect: 0, Receive: 0, Length: 77, Exceptions: 0)
Total transferred: 2490673 bytes
HTML transferred: 506073 bytes
Requests per second: 1028.14 [#/sec] (mean)
Time per request: 97.263 [ms] (mean)
Time per request: 0.973 [ms] (mean, across all concurrent requests)
Transfer rate: 250.07 [Kbytes/sec] received
吞吐量1028(有点低啊)
3. 使用单机模式连接
<?php
$redis_list = 'tcp://10.30.5.163:7000';
$client = new Redis();
$client->connect('10.30.5.163', '7000');
echo $client->get('new_item_key:d89b561fb759fd533a8c2781ef15dd5f');
结果
Concurrency Level: 100
Time taken for tests: 1.238 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 2510000 bytes
HTML transferred: 510000 bytes
Requests per second: 8078.76 [#/sec] (mean)
Time per request: 12.378 [ms] (mean)
Time per request: 0.124 [ms] (mean, across all concurrent requests)
Transfer rate: 1980.24 [Kbytes/sec] received
4. 使用predis集群模式
<?php
use Predis\Client;
require __DIR__ . '/../vendor/autoload.php';
$redis_list = [
'tcp://10.30.5.163:7000',
'tcp://10.30.5.163:7001',
'tcp://10.30.5.162:7000'
];
$redis = new Client($redis_list, ['cluster'=>'redis']);
echo $redis->get('new_item_key:d89b561fb759fd533a8c2781ef15dd5f');
结果
Concurrency Level: 100
Time taken for tests: 5.380 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 2510000 bytes
HTML transferred: 510000 bytes
Requests per second: 1858.68 [#/sec] (mean)
Time per request: 53.802 [ms] (mean)
Time per request: 0.538 [ms] (mean, across all concurrent requests)
Transfer rate: 455.59 [Kbytes/sec] received
性能和phpredis差一倍, 但是是在开启opcache的情况下, 因为加载predis的client, 需要使用psr-4查找文件, 不开启opcache, 性能会差挺多, 有兴趣可以自己尝试
-
总结
使用phpredis操作集群性能和单机相差一倍左右, predis和phpredis差距有点大, 在实际编程中可以按需要去选择 -
在java操作redis集群的库使用jedis, 可以为每一个节点设置一个连接池, 在发送请求前, 先计算槽, 根据本地缓存的槽和节点映射缓存就可以直接去请求数据保存的节点获取数据, 当槽和节点映射关系不正确, 会触发重试机制, 将最新的映射关系更新到缓存中
php一次请求过后, 变量的生命周期就结束了, 无法设置连接池以及使用槽节点缓存机制, 命令如果和请求节点不对应, 就会MOVED重定向, 产生性能损耗.
附录 redis扩展安装
~ git clone git@github.com:phpredis/phpredis.git
~ cd phpredis
~ git fetch
~ git checout feature/redis_cluster #切换到cluster分支
~ phpize
~ ./configure
~ make
~ make install
这样就可以用了。如果你是第一次安装redis扩展,还需要在php.ini中加上:
extension=redis.so
文档部分引用
http://www.php.cn/blog/detail/6690.html
http://www.laruence.com/2016/12/18/3137.html
网友评论