Redis初步学习
一、编译安装 Redis
下载地址:https://redis.io
(1)编译: make
(2)复制 redis 相关程序和配置文件到 /usr/local/redis
cp ~/downloads/redis-4.0.10/src/redis-server /usr/local/redis/ cp ~/downloads/redis-4.0.10/src/redis-cli /usr/local/redis/
cp ~/downloads/redis-4.0.10/redis.conf /usr/local/redis
(3)编辑 redis.conf 配置文件
bind 127.0.0.1 port 6379 daemonize yes # 以守护进程运行 pidfile /var/run/redis_6379.pid
(4)示例启动脚本
» redis
REDIS_SERVER=/usr/local/redis/redis-server REDIS_CLI=/usr/local/redis/redis-cli REDIS_CONF=/usr/local/redis/redis.conf start_server() { $REDIS_SERVER $REDIS_CONF } stop_server() { $REDIS_CLI shutdown } cli() { $REDIS_CLI } case "$1" in 'server') start_server ;; 'client') cli ;; 'stop') stop_server ;; *) echo 'unknow!' esac
(5)启动
./redis server
执行结果:
二、Redis 数据结构
2.1 String 类型
string 是 redis 最基本的类型,而且 string 类型是二进制安全的。
redis 的 string 可以包含任何数据。包括 jpg 图片或者序列化的对象,最大上限是1G字节。 如果只用 string 类型,redis 就可以被看作加上持久化特性的 memcached。命令:
- set key value:设置指定 key 的值
- get key:获取指定 key 的值
set name txl # 设置 name = txl get name # 获取 name keys name # 获取对应的 key,可以用匹配模式 del name # 删除 key 为 name 的数据
2.2 Hash 类型
redis hash 是一个 string 类型的 field 和 value 的映射表。
hash 特别适合用于存储对象。相较于将对象的每个字段存成单个 string 类型。将一个对象存储在 hash 类型中会占用更少的内存,并且可以更方便的存取整个对象。
命令:
- hset key field value:将哈希表 key 中的字段 field 的值设为 value
- hsetnx key field value:只有在字段 field 不存在时,设置哈希表字段的值
- hget key field:获取存储在哈希表中指定字段的值
- hdel filed1 [filed2...]:删除一个或多个哈希表字段
- hlen key:获取哈希表中字段的数量
- hexists key field:查看哈希表 key 中,指定的字段是否存在
- hkeys key:获取哈希表 key 中的所有字段
- hvals key:获取哈希表 key 中的所有值
实例:统计网站在线人数
思路:用户登录时,记录 Hash 类型的用户信息到 redis 中。如:key 为 UserLogin,filed 为 用户ID(或sessionid),value 为用户 ID、登录 IP、登录时间的 Json。最后通过 shell 调用 redis-cli 客户端的 hlen 方法就可以统计网站在线人数。
但是需要定时清理离线用户。如:理想情况下,用户主动注销,同时清除 redis 对应的用户记录。但是当用户非正常退出或长时间无任何请求时,可以设定一个机制:
当用户访问网站任意页面时,都加载一段脚本,触发该用户的时间标识,如:
<script src=“/redis/useronline.php”></script>
并同时记录在 redis 中。只需定时执行清理脚本:判断服务器时间和用户时间标识的时间差,如超过20分钟没有任何更新则认为该用户为离线用户,删除该用户记录。
» usersonline
# 统计所有离线用户 IDS=`/usr/local/redis/redis-cli hvals users_login | awk -F ',' '{print $1 $3}' | awk 'gsub(/\{"uid":|"utime"|\}/,"",$1)' | awk -F ':' ' BEGIN{result=""; time_diff=0} { time_diff=systime()-$2; #print time_diff; if(time_diff>1800) { result=result" "$1; } } END{print result} '` # 从 redis 中删除离线用户记录 /usr/local/redis/redis-cli hdel users_login $IDS echo $IDS" is deleted"
执行结果:
2.3 List(列表)类型
List 是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部或尾部。
命令:
- lpush key value1 [value2...]:将一个或多个值插入到列表头部
- rpush key value1 [value2...]:将一个或多个值插入到列表尾部
- llen key:获取列表长度
- lrange key start stop:获取列表指定范围内的元素(0代表第一个,-1代表最后一个)
- lpop key:移出并获取列表的第一个元素
- rpop key:移出并获取列表的最后一个元素
2.4 Set(集合)类型
Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
命令:
- sadd key member1 [member2...]:向集合添加一个或多个成员
- scard key:获取集合的成员数
- smembers key:返回集合中的所有成员
- sismember key member:判断 member 元素是否是集合 key 的成员(1代表是,0代表不是)
- spop key:移除并返回集合中的一个随机元素
- srem key member1 [member2...]:移除集合中一个或多个成员
- smove source destination member:将 member 元素从 source 集合移动到 destination 集合
- sdiff key1 [key2...]:返回给定所有集合的差集
- sdiffstore destination key1 [key2...]:返回给定所有集合的差集并存储在 destination 中
- sinter key1 [key2...]:返回给定所有集合的交集
- sinterstore destination key1 [key2...]:返回给定所有集合的交集并存储在 destination 中
- sunion key1 [key2...]:返回所有给定集合的并集
- sunionstore destination key1 [key2...]:所有给定集合的并集存储在 destination 集合中
2.5 Sorted set(有序集合)类型
Sorted set 有序集合和集合一样也是 String 类型元素的集合,且不允许重复的成员。集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。
命令:
- zadd key score1 member1 [score2 member2...]:向有序集合添加一个或多个成员,或者更新已存在成员的分数
- zcard key:获取有序集合的成员数
- zcount key min max:计算在有序集合中指定区间分数的成员数
- zrange key start stop [withscores]:通过索引区间返回有序集合成指定区间内的成员,分数从低到高(strart从0开始,stop如果为负数代表到最后第几个)
- zrangebyscore key min max [withscores] [limit]:通过分数返回有序集合指定区间内的成员
- zrevrange key start stop [withscores]:通过索引区间返回有序集中指定区间内的成员,分数从高到底(strart从0开始,stop如果为负数代表到最后第几个)
- zrevrangebyscore key max min [withscores]:通过分数返回有序集合指定区间内的成员
- zscore key member:返回有序集中,成员的分数值
- zrank key member:返回有序集合中指定成员的索引,索引从低到高
- zrevrank key member:返回有序集合中指定成员的索引,索引高低到低
- zincrby key increment member:有序集合中对指定成员的分数加上增量 increment
- zrem key member1 [member2...]:移除有序集合中的一个或多个成员
- zremrangebyrank key start stop:移除有序集合中给定的排名区间的所有成员
- zremrangebyscore key min max:移除有序集合中给定的分数区间的所有成员
实例①:消息队列单点处理(redis+shell)
当处理高并发请求时往往会使用消息队列来做“缓冲”。
如门户网站后台发布页面(生成静态或缓存文件),可以在消息队列中存放多个“发布任务”:
- 后端程序不断地“压任务”,存放有序集合到 redis,如 score 存放任务时间戳,member 存放文件名(可以放序列化过后的类、存储过程或 shell 脚本名等等)
- Shell 脚本定时处理消息队列(任务),将生成文件发布到网站目录中
» job
# 获取redis中第一条任务 GET_JOB="/usr/local/redis/redis-cli zrange job 0 0" function dojob() { # 生成文件 echo "job:$1" > $2"/"$1 # 清除redis中该条任务 /usr/local/redis/redis-cli zrem job $1 } # 导出dojob函数用于awk调用 export -f dojob $GET_JOB | awk 'BEGIN{wwwroot="/var/www/job"} { # 调用dojob并将任务名(文件名)和生成路径传递过去 "dojob "$1" "wwwroot|getline; #print $1; } '
实例②:消息队列多进程处理(redis+php)
» job_fork.php
#! /usr/local/php7.2.6/bin/php <?php // 10个进程同时处理 for($i=0; $i<10; $i++) { $pid = pcntl_fork(); if($pid == 0) { while (true) { $redis = new Redis(); $redis->connect('180.76.232.93',6379); // 判断队列是否有序列,如果没有则继续循环 if(count($redis->keys('job'))==0) { continue; } // 加锁 if($redis->setnx('joblock',1)==1) { // 从队列中读取第一个 $getjob = $redis->zrange('job',0,0); // 删除当前队列 $redis->zrem('job',$getjob[0]); // 解锁 $redis->del('joblock'); // 注意,这里要用到redis原子事务 multi和exec // $redis->multi(); //开启事务 // redis相关的命令... // $redis->exec(); //结束事务 // 执行任务 file_put_contents('/var/www/job/'.$getjob[0],'job:'.$getjob[0]); // 应该输出到日志中 echo $getjob[0]."is done".PHP_EOL; sleep(1); } } } }
2.6 HyperLogLog 结构
HLL 是用来做基数统计的算法,它的优点是在输入元素的数量或者体积非常大时,计算基数所需的空间总是固定的、并且是很小的。
命令:
- pfadd key element [element...]:添加指定元素到 HLL 中,重复值不会累计
- pfcount key [key...]:返回给定 HLL 的基数估算值
- pfmerge destkey sourcekey [sourcekey...]:合并若干 key 到目标 key,重复值依旧不会累加
2.7 Bitmap 类型
Bitmap 是属于 String 类型的一种数据结构,就是通过一个 bit 位来表示某个元素对应的值或者状态,其中的 key 就是对应元素本身。我们知道 8 个 bit 可以组成一个 byte,所以 Bitmap 本身会极大的节省储存空间。它可以用于很多的统计、数据分析等应用场景。
命令:
- setbit key offset value:设置或者清空 key 的 value(字符串)在 offset 处的 bit 值(只能是0或者1)
- getbit key offset:获取指定索引的值
- bitcount key [start end]:统计出指定范围(start到end,单位是字节不是位,如不指定则就是获取全部)bit 值为 1 的数量