PHP安装、使用Redis,学习笔记。

2023-02-17 14:19:33 浏览数 (2)

基础知识

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,以下为常用命令:

  1. 登录redis:redis-cli -h 127.0.0.1 -p 6379
  2. 查看所有的key值:keys *
  3. 删除指定索引的值:del key
  4. 清空整个redis服务器的数据:fiushall
  5. 清空当前库中的所有key:flushdb
  6. 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)

代码语言:javascript复制
<?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

0 人点赞