基础知识
Redis相关知识:https://www.runoob.com/redis/redis-tutorial.html
Github:https://github.com/phpredis/phpredis#close
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
1.字符串
string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。 string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。 string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。
代码语言:javascript复制SET dog "憨皮"
GET dog
DEL dog
2.HASH
Redis hash 是一个键值(key=>value)对集合。 Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。 每个 hash 可以存储 232 -1 键值对(40多亿)。
代码语言:javascript复制HMSET runoob field1 "Hello" field2 "World"
HGET runoob field1
DEL runoob [hashkey]
3.列表
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。 一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。
代码语言:javascript复制LPUSH runoobkey redis
LPUSHX runoobkey redis
LRANGE runoobkey 0 10
DEL redis
4.集合
Redis 的 Set 是 string 类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
代码语言:javascript复制sadd runoob rabbitmq #成功返回1,失败返回0
smembers runoob
Del
5.有序集合
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。 不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。 zset的成员是唯一的,但分数(score)却可以重复。
补充
官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。
Redis支持多个数据库,并且每个数据库的数据是隔离的不能共享,并且基于单机才有,如果是集群就没有数据库的概念。
Redis是一个字典结构的存储服务器,而实际上一个Redis实例提供了多个用来存储数据的字典,客户端可以指定将数据存储在哪个字典中。这与我们熟知的在一个关系数据库实例中可以创建多个数据库类似,所以可以将其中的每个字典都理解成一个独立的数据库。
每个数据库对外都是一个从0开始的递增数字命名,Redis默认支持16个数据库(可以通过配置文件支持更多,无上限),可以通过配置databases来修改这一数字。客户端与Redis建立连接后会自动选择0号数据库,不过可以随时使用SELECT命令更换数据库
redis命令大全:https://redis.io/commands/、http://www.redis.cn/
redis数据库相关参考,命令行连上的默认为0号数据库:https://www.runoob.com/redis/redis-data-types.html
安装redis
1.下载,解压,编译
代码语言:javascript复制$ wget http://download.redis.io/releases/redis-3.2.8.tar.gz
$ tar -xzf redis-3.2.8.tar.gz
$ cd redis-3.2.8
$ make
编译完成后,在src目录下,有四个可执行文件redis-server、redis-benchmark、redis-cli和redis.conf。
- redis-server,用于启动Redis外。
- redis.conf,redis配置文件。
- redis-cli,redis命令行工具。
- redis-benchmark可以为Redis做基准性能测试
redis.config在src所在的目录下,redis的安装目录就是make所在的目录。
执行文件详解:https://blog.csdn.net/baidu_41388533/article/details/108438878
Redis配置文件详解:https://www.runoob.com/redis/redis-conf.html
2.启动redis
将上面提到的可执行文件移动到任意目录,然后执行,例如:
代码语言:javascript复制$ /usr/redis/redis-server /usr/redis/redis.conf
后台运行redis,将配置文件中daemonize no,改为daemonize yes,再启动即可。
参考文章:https://www.cnblogs.com/ygw1010/p/7446003.html
make命令:https://www.cnblogs.com/linshengqian/p/15585858.html
3.安装PHP Redis拓展
Github:https://github.com/phpredis/phpredis
代码语言:javascript复制$ wget https://github.com/phpredis/phpredis/archive/3.1.4.tar.gz
$ tar zxvf 3.1.4.tar.gz # 解压
$ cd phpredis-3.1.4 # 进入 phpredis 目录
$ /usr/local/php/bin/phpize # php安装后的路径
$ ./configure --with-php-config=/usr/local/php/bin/php-config
$ make && make install
修改php.ini,加入extension=redis.so;重启LNMP,大功告成。
PHP Redis相关操作方法:https://www.cnblogs.com/githup/p/13397405.html
PHP保存数组到Redis,先json或者序列化存入,取回来再反序列化,json解码。
4.查看安装目录
代码语言:javascript复制[root@localhost ~]# which Redis
/usr/local/redis #redis安装目录
代码语言:javascript复制#查看redis进程
ps -aux | grep redis
#或者
ps -ef|grep redis
#假设得到redis的进程号123,然后使用以下命令查看安装位置。
ll /proc/123/cwd
redis应用
1.PHP 操作Redis
代码语言:javascript复制<?php
/*连接本地的 Redis 服务*/
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
echo "Connection to server successfully";
/*查看服务是否运行*/
echo "Server is running: " . $redis->ping();
?>
- set、del、get(ket,[value]),存入和取出字符串数据。
- $redis->setex('key', 3600, 'value'); // setex 带生存时间的写入值
- $redis->setnx(key,value); 如果key不存在才设置它的值;
- $redis->rawCommand - 对服务器执行任何通用命令(redis命令行命令)。
- $redis->subscribe - 订阅
- $redis->setOption();//设置redis模式
- $redis->getOption();//查看redis设置的模式
- $redis->ping();//查看连接状态
- $redis->close();//断开连接
提示
connect:脚本结束之后连接就释放了。 pconnect:脚本结束之后连接不释放,连接保持在php-fpm进程中。 所以使用pconnect代替connect,可以减少频繁建立redis连接的消耗。
2.redis常用命令行
cd打开redis.cli所在目录,然后输入命令进行连接:redis-cli -h 127.0.0.1 -p 6379,以下为常用命令:
- 登录redis:redis-cli -h 127.0.0.1 -p 6379
- 查看所有的key值:keys *
- 删除指定索引的值:del key
- 清空整个redis服务器的数据:fiushall
- 清空当前库中的所有key:flushdb
- shutdown,退出并关闭redis
3.redis最常用的应用场景
1.缓存、2.数据共享分布式、3.分布式锁、4.全局ID、5.计数器、6.限流、7.位统计、8.购物车、9.用户消息时间线timeline、10.消息队列、11.抽奖、12.点赞、签到、打卡、13.商品标签、14.商品筛选、15.用户关注、推荐模型、16.排行榜
相关参考:
https://zhuanlan.zhihu.com/p/484345565 https://www.lanmper.cn/redis
4.Redis数据有效期
参考文章:https://www.php.cn/redis/437105.html
例如
$redis->setex('key', 3600, 'value'); // setex 带生存时间的写入值
Redis订阅与发布
Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。
1.订阅
代码语言:javascript复制#根据频道名字订阅
SUBSCRIBE Channel Channel ...
#指定匹配规则订阅,可以使用*通配符
PSUBSCRIBE pattern [pattern ...]
2.取消订阅
代码语言:javascript复制#退订指定频道。
UNSUBSCRIBE Channel Channel ...
#退订所有给定模式的频道。
PUNSUBSCRIBE [pattern [pattern ...]]
3.发布消息
代码语言:javascript复制#将信息发送到指定的频道。
PUBLISH channel message
4.缺点
Redis可以提供基本的发布订阅功能,但毕竟不像消息队列那种专业级别,所以会存在以下缺点:
- redis无法对消息持久化存储,消息一旦被发送,如果没有订阅者接收,数据会丢失消息队列提供了消息传输保障,当客户端连接超时或
- 事物回滚的等情况发生时,消息会重新发布给订阅者,redis没有该保障,导致的结果就是在订阅者断线超时或其他异常情况时,将会丢失所有发布者发布的信息
- 若订阅者订阅了频道,但自己读取消息的速度很慢的话,那么不断积压的消息会使redis输出缓冲区的体积变得越来越大,这可能使得redis本身的速度变慢,甚至直接崩溃
- 缓冲区的默认配置:client-output-buffer-limit pubsub 32mb 8mb 60。它的参数含义如下: 32mb:缓冲区一旦超过 32MB,Redis 直接强制把消费者踢下线。 8mb 60:缓冲区超过 8MB,并且持续 60 秒,Redis 也会把消费者踢下线。
PHP redis订阅
PHP调用订阅命令后将进入阻塞状态,除了退出无法主动取消。在swoole协程中可以通过主动取消协程中断订阅,然后在defer回调内close关闭redis,取消订阅
Redis事务
1.事务介绍
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
- 批量操作在发送 EXEC 命令前被放入队列缓存。
- 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
- 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历以下三个阶段: 开始事务 -> 命令入队 -> 执行事务。
2.相关命令
redis事务相关的命令如下:
- DISCARD,取消事务,放弃执行事务块内的所有命令。
- EXEC,执行所有事务块内的命令。
- MULTI,标记一个事务块的开始。
- UNWATCH,取消 WATCH 命令对所有 key 的监视。
- WATCH key [key ...],监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
注意
单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
3.命令行示例代码
代码语言:javascript复制redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> SET book-name "Mastering C in 21 days"
QUEUED
redis 127.0.0.1:6379> GET book-name
QUEUED
redis 127.0.0.1:6379> SADD tag "C " "Programming" "Mastering Series"
QUEUED
redis 127.0.0.1:6379> SMEMBERS tag
QUEUED
redis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
2) "C "
3) "Programming"
4.PHP示例代码
代码语言:javascript复制try {
//监视一个(或多个)key,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断
$redis->watch(array($key1, $key2));
//模拟监视 key 被打断
//$redis->set($key1, '12345');
$redis->multi();
$redis->set($key1, '1123');
$redis->set($key2, '2123');
//执行事务块内的所有命令
$status = $redis->exec();
//失败则取消事务
if (!$status) {
$redis->discard();
}
} catch (Exception $e){
echo $e->getMessage();
exit;
}
Redis消息队列
1. 什么是消息队列?
“消息队列”是在消息的传输过程中保存消息的容器。“消息”是在两台计算机间传送的数据单位。消息队列管理器在将消息从它的源中继到它的目标时充当中间人。队列的主要目的是提供路由并保证消息的传递;如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它。一般具有如下特点:
- 支持阻塞等待拉取消息
- 支持发布 / 订阅模式
- 消费失败,可重新消费,消息不丢失
- 实例宕机,消息不丢失,数据可持久化
- 消息可堆积
2.消费者、消费者组、消息之间的关系
每个消费组都有一份消息队列中完整的消息,不同消费组之间消费进度彼此不受影响。
也就是说,消息队列中的一条消息被 Consumer Group1 消费过,也会再给 Consumer Group2 消费。 消费组中包含多个消费者,同一个组内的消费者是竞争消费的关系,每个消费者负责消费组 内的一部分消息。如果一条消息被消费者 Consumer1 消费了,那同组的其他消费者就不 会再收到这条消息。
3.业务流程
例如一个抢购的场景,消息队列有100份消息,一个消费组内100个人进行消费。
4.消息队列相关命令
- XADD - 添加消息到末尾
- XTRIM - 对流进行修剪,限制长度,裁剪消息到指定个数(保留指定个数的最新消息,http://www.redis.cn/commands/xtrim.html)
- XDEL - 删除消息
- XLEN - 获取流包含的元素数量,即消息长度
- XRANGE - 获取消息列表,会自动过滤已经删除的消息
- XREVRANGE - 反向获取消息列表,ID 从大到小
- XREAD - 以阻塞或非阻塞方式获取消息列表
5.消费者组相关命令
- XGROUP CREATE - 创建消费者组
- XREADGROUP GROUP - 读取消费者组中的消息
- XACK - 将消息标记为"已处理"
- XGROUP SETID - 为消费者组设置新的最后递送消息ID
- XGROUP DELCONSUMER - 删除消费者
- XGROUP DESTROY - 删除消费者组
- XPENDING - 显示待处理消息的相关信息
- XCLAIM - 转移消息的归属权
- XINFO - 查看流和消费者组的相关信息;
- XINFO GROUPS - 打印消费者组的信息;
- XINFO STREAM - 打印流信息
相关参考:https://blog.csdn.net/z2431435/article/details/124978166
Redis数据备份与恢复
1.数据备份
Redis SAVE 命令用于创建当前数据库的备份,命令基本语法如下:
代码语言:javascript复制redis 127.0.0.1:6379> SAVE
该命令将在 redis 安装目录中创建dump.rdb文件。
2.数据恢复
如果需要恢复数据,只需将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可。获取 redis 目录可以使用 CONFIG 命令,如下所示:
代码语言:javascript复制redis 127.0.0.1:6379> CONFIG GET dir
1) "dir"
2) "/usr/local/redis/bin"
3.后台备份
创建 redis 备份文件也可以使用命令 BGSAVE,该命令在后台执行。
使用unix socket
UNIX Domain Socket是在socket架构上发展起来的用于同一台主机的进程间通讯(IPC),它不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程,当用户连接到Redis通过TCP/IP连接或Unix域连接,千兆网络的典型延迟大概200us,而Unix Domain Socket可能低到30us。
代码语言:javascript复制# 打开 redis.conf
# bind 127.0.0.1
port 0
unixsocket /tmp/redis.sock
unixsocketperm 700
提示
unixsocket选项没有默认值,不指定unixsocket就不会监听任何。如果不指定unixsocketperm,unix socket⽂件将使⽤默认权限(umask相关)
提示
如果PHP链接Redis报错Uncaught RedisException: Permission denied
,请将上方700改成777(或者查查SELinux)
<?php
$redis = new Redis();
$redis->connect("/tmp/redis.sock");
//命令行如下
redis-cli -s /tmp/redis.sock
Redis版本相关
Redis发展至今已历经7个大版本,而每个大版本都有大量的新特性产生。例如从3.0开始支持cluster集群模式;4.0开发的lazyfree和PSYNC2解决了Redis长久的大key删除阻塞问题及同步中断无法续传的问题;5.0新增了stream数据结构使Redis具备功能完整的轻量级消息队列能力;6.0更是发布了诸多企业级特性如threaded-io、TLS和ACL等,大幅提升了Redis的性能和安全性。
Redis社区目前主流维护版本是6.0,5.0版本已经进入低维护阶段,而国内还有大量2.8,3.0,4.0版本在使用中。这就很矛盾,一方面,一些对用法,性能,稳定性和抖动控制的能力都贡献在了新版本上,另一方面,国内用户却“看的到,用不上”。
除去6.0,7.0上的高级稳定性优化不说,比如在4.0前,没有lazyfree删除一个大key是同步的会卡住数据库引擎直接导致业务中断。又比如,Redis其实安全性尤其是lua是一直有较大问题的,这些升级也都“隐含”在了新版本中。
问题总结
1.取值错误
某日使用redis,通过setex设置键值,取出来的时候都是false;排查许久,设置前的变量是存在值的,setex返回的也是true;命令行取值发现确实是空;又排查了许久,难道同步的PHP代码,出现了JS那样的异步问题?又排查发现设置的时候键名后面有一个空格。。。。。
2.链接超时
PHPredis默认连接60s,就超时关闭链接;如下设置为永不超时:
代码语言:javascript复制<?php
$redis->setOption(3, -1);
3.批量删除
代码语言:javascript复制redis-cli -h 192.168.0.1 -a 123456 KEYS "newHand*" | xargs redis-cli -h 192.168.0.1 -a 123456 DEL