API
通用命令
keys:遍历所有key,筛选出复合条件的key,复杂度是*:零个或多个任意字符?:一个任意字符[a-z]:a到z中匹配的字符
dbsize:查看当前所有数据库中所有key的个数,复杂度是exists:判断key是否存在,复杂度是- 存在返回1
- 不存在返回0
del:根据key删除指定的键值对,也可同时指定多个key一起删除,复杂度是expire:设置指定key的过期时间,单位秒,复杂度是ttl:查看指定key的过期时间,复杂度是- 未过期返回还能存活多久的时间,单位秒
- 已过期返回-2
- 未设置过期时间返回-1
persist:删除指定key的过期时间设置,复杂度是type:返回key的value所对应的数据类型,复杂度是- key不存在返回none
- key存在返回对应的类型(全小写)
String数据类型
结构
-
虽说是字符串,但是在使用不同命令进行操作时内部可以转换成对应的数字、二进制数组
-
存储的最大限制是512MB
命令
get:根据key获取value,复杂度是- 若key不存在会返回nil,代表空
set:设置key和value,复杂度是- 若key不存在会添加,若key存在会覆盖
- 衍射出的子命令:key和value在中间位置,即nx和xx是后缀
set nx:若key存在则设置失败,即添加操作,也可写成setnxset xx:若key不存在则设置失败,即更新操作
mget:批量根据key获取value,复杂度是- 是原子操作
- m与批量操作的个数有关由,所以由于网络延时的原因,合理使用该命令会比单个使用
get命令效率高
mset:批量设置key和value,复杂度是- 是原子操作
- m与批量操作的个数有关,所以由于网络延时的原因,合理使用该命令会比单个使用
set命令效率高
getset:为key设置新的value,同时返回旧的value,复杂度是- 是原子操作
整型数字操作
incr:指定key自增1,复杂度是- 若key不存在会创建一个value为1的key
- 若value有非数字的字符会报错,包括小数点
decr:指定key自减1,复杂度是- 若key不存在会创建一个value为-1的key
- 若value有非数字的字符会报错,包括小数点
incrby:指定key自增指定步长,复杂度是- 若指定步长为负数就是递减了
- 若key不存在会创建一个value为步长的key
- 若value有非数字的字符会报错,包括小数点
decrby:指定key自减指定步长,复杂度是- 若指定步长为负数就是递增了
- 若key不存在会创建一个value为负步长的key
- 若value有非数字的字符会报错,包括小数点
浮点型数字操作
incrbyfloat:指定key自增指定步长,可以指定步长为小数,复杂度是- 若指定步长为负数就是递减了
- 若key不存在会创建一个value为步长的key
- 若value有非数字的字符会报错,不包括小数点
字符串操作
append:指定key的value后追加字符串,复杂度是strlen:指定key的value中字符串的长度,复杂度是- 注意:一个中文字符会占用两个长度
getrange:获取key的value某指定范围子串,复杂度是- 下标从0开始,闭区间
- 负数倒着数
setrange:设置key的value某指定位置开始,向后偏移的覆盖为指定内容,复杂度是- 下标从0开始
- 负数倒着数
使用场景
存储对象方案一
将整个对象进行序列化或标准化为JSON、XML,将key做为对象的ID
-
优点:访问Redis编程简单,节约内存
-
缺点:序列化开销,修改属性需要操作字符串整个数据
存储对象方案二
将key为对象的业务ID+属性名,对各个属性分散存储,比如:user:name,使用冒号分隔
- 优点:Redis数据直观可查,可部分属性更新,比较方便
- 缺点:内存占用较大,key较为分散
Hash数据类型
结构
对应着一个Map集合,该Map集合的key称之为field,value还是value,并且这个集合中的field和value只能存储字符串数据类型
Map
field value
+--------+--------+
| field1 | value1 |
key ----> | field2 | value2 |
| field3 | value3 |
| ... | ... |
+--------+--------+
命令
hget:获取指定key的field对应的value,复杂度是hset:设置指定key的field对应的value,复杂度是hsetnx:若field存在则设置失败,即添加操作
hdel:删除指定key的field对应的value,也可同时指定多个field一起删除,复杂度是- 若删除到整个Map都为空时,这个key也会被删除
hmget:批量获取指定key的field,复杂度是- 是原子操作
- m与批量操作的个数有关
hmset:批量设置指定key的field的value,复杂度是- 是原子操作
- m与批量操作的个数有关
hexists:判断指定key的field是否存在,复杂度是- 存在返回1
- 不存在返回0
hlen:获取指定key的field的个数,复杂度是
遍历操作
hgetall:获取所有field和value,复杂度是- m与field个数有关
hvals:获取所有的value,复杂度是- m与field个数有关
hkeys:获取所有的key,复杂度是- m与field个数有关
整型数字操作
hincrby:指定key的field的value自增指定步长,复杂度是- 若field不存在会创建一个value为步长的field
- 若key不存在会先创建key,再创建field
- 若指定步长为负数就是递减了
- 若value有非数字的字符会报错,包括小数点
- 若field不存在会创建一个value为步长的field
浮点数字操作
hincrbyfloat:指定key的field的value自增指定步长,可以指定步长为小数,复杂度是- 若field不存在会创建一个value为步长的field
- 若key不存在会先创建key,再创建field
- 若指定步长为负数就是递减了
- 若value有非数字的字符会报错
- 若field不存在会创建一个value为步长的field
使用场景
存储对象
将对象属性在field中,将key做为对象的ID
-
优点:Redis数据直观可查,可部分属性更新,比较方便,节约内存(底层到一定程度会该为压缩Map编码格式)
-
缺点:访问Redis编码稍微比较复杂,属性过期时间不好控制(需要自己逻辑实现)
List数据类型
结构
- 有序,有重复元素
- 有独特的负索引
[ a, b, c, d] : 元素列表
[ 0, 1, 2, 3] : 正索引
[-4, -3, -2, -1] : 负索引,即倒数第几个,从1开始
命令
增加
rpush:在指定key的右面添加一个或多个元素,复杂度是到,m取决于要添加的个数- 插入多个元素时,从左向右依次添加到右面
lpush:在指定key的左面添加一个或多个元素,复杂度是到,m取决于要添加的个数- 插入多个元素时,从左向右依次添加到左面
linsert:在指定key的指定元素前面或后面插入元素,需要遍历整个列表,复杂度是before:前面插入after:后面插入
删除
若删除到整个List都为空时,这个key也会被删除
rpop:从指定key的右面删除一个元素,复杂度是lpop:从指定key的左面删除一个元素,复杂度是lrem:从指定key中删除指定个数的与之匹配的元素,复杂度是count等于0:全部删除count是正数:从左往右删除count个count是负数:从右往左删除count个
ltrim:将指定key中的列表修剪为指定索引范围内(闭区间)的子列表,复杂度是brpop:阻塞的从指定key的右面删除一个元素,即若列表为空时会等待到有值时才会弹出,复杂度是timeout:设置的阻塞超时时间,单位秒,若设置为0,代表用不阻塞
blpop:阻塞的从指定key的左面删除一个元素,即若列表为空时会等待到有值时才会弹出,复杂度是timeout:设置的阻塞超时时间,单位秒,若设置为0,代表用不阻塞
修改
lset:设置指定key下指定索引的元素内容,复杂度是
查询
lrange:获取指定key的指定索引范围内(闭区间)的所有元素,复杂度是lrange key 0 -1:遍历整个列表
lindex:获取指定key的指定索引下的元素,复杂度是llen:获取指定key的列表长度,复杂度是
使用场景
- 栈:使用
lpush和lpop - 队列:使用
lpush和rpop - 固定容量的容器集合:使用
lpush和ltrim - 消息队列:使用
lpush和brpop
Set数据类型
结构
- 无序,无重复元素
- 支持集合间的操作
命令
集合内的操作
sadd:向指定key中添加元素(可添加多个),若元素已存在则不会添加进去,复杂度是srem:将指定key中的元素删除(可删除多个),复杂度是sismember:判断元素是否存在集合中,存在返回1,不存在返回0,复杂度是srandmember:获取指定key中随机的一个或多个元素,个数可以指定,复杂度是- 指定个数为正数:一次性获取过多时,不会超过集合中元素个数
- 指定个数为负数:一次性获取过多时,可以超过集合元素个数
spop:将指定key中元素随机删除一个或多个,个数可以指定,且不允许为负数,复杂度是smembers:获取指定key中的所有元素,复杂度是,m取决于集合中元素的个数scard:获取指定key中元素的个数,复杂度是
集合间的操作
sdiff:返回指定key的差集,可指定多个key,若只有一个则返回key集合本身sdiffstore:从第二个key开始计算,不返回结果,将结果存储在第一个key中
sinter:返回指定key的交集,可指定多个key,若只有一个则返回key集合本身sinterstore:从第二个key开始计算,不返回结果,将结果存储在第一个key中
sunion:返回指定key的并集,可指定多个key,若只有一个则返回key集合本身sinterstore:从第二个key开始计算,不返回结果,将结果存储在第一个key中
使用场景
抽奖系统
将参与的用户添加到集合中,在使用spop或srandmember获取获奖用户
赞踩系统
将赞或踩过的用户添加到该消息的赞踩集合中,使用集合间操作sinter,就可以得到共同爱好的用户
标签系统
给用户添加标签,使用sadd,将标签添加到用户集合中
给标签添加用户,使用sadd,将用户添加到标签集合中
ZSet数据类型
结构
- 有序,无重复元素
- 每个元素都多存储着一个分数信息,是根据分数来排序的,从小到大
Map
value key
| |
\|/ \|/
score element
+--------+----------+
| score1 | element1 |
key ----> | score2 | element2 |
| score3 | element3 |
| ... | ... |
+--------+----------+
命令
集合内的操作
zadd:向指定key中添加分数和元素(可添加多对),若元素已存在则不会添加进去,复杂度是zrem:将指定key中的元素删除(可删除多个),复杂度是zscore:获取指定key中指定元素的分数,复杂度是zrank:获取指定key中元素的从高到底排名,排名从零开始,复杂度是zrevrank:获取指定key中元素的从低到高排名,排名从零开始,复杂度是zincrby:将指定key中指定元素的分数自增指定步长,复杂度是- 若指定步长为负数就是递减了
- 若element不存在会创建一个score为步长的key
- 若key不存在会先创建key,再创建element
- 若value有非数字的字符会报错,不包括小数点
zcard:获取指定key中元素的个数,复杂度是
范围操作
zrange:获取指定key的指定排名(排名从零开始)范围内(闭区间)的所有元素(升序),复杂度是,n是指有序集合中的元素个数,m是指范围内的元素个数withscores:若加此后缀参数后,元素分数也会对应返回zrange key 0 -1:遍历整个有序集合zrange key 0 -1 withscores:遍历整个有序集合,包括分数
zrevrange:降序的zrangezrangebyscore:获取指定key的指定分数范围内(闭区间)的所有元素(升序),复杂度是,n是指有序集合中的元素个数,m是指范围内的元素个数withscores:若加此后缀参数后,元素分数也会对应返回
zrevrangebyscore:降序的zrangebyscorezcount:获取指定key的指定分数范围内(闭区间)的元素个数,复杂度是,n是指有序集合中的元素个数,m是指范围内的元素个数zremrangebyrank:删除指定key的指定排名范围(闭区间)的元素,复杂度是,n是指有序集合中的元素个数,m是指范围内的元素个数zremrangebyscore:删除指定key的指定分数范围(闭区间)的元素,复杂度是,n是指有序集合中的元素个数,m是指范围内的元素个数
集合间的操作
zunionstore:返回指定key的并集(必须指定要操作的key的个数),可指定多个key,从第二个key开始计算,将结果存储在第一个key中withscore:设置乘法因子,默认都是1,若有重复key在合并时,是将两个相同的元素的分数进行相加,若还需要乘上一个数在相加就需要在该参数中设置
zinterstore:返回指定key的交集(必须指定要操作的key的个数),可指定多个key,从第二个key开始计算,将结果存储在第一个key中withscore:设置乘法因子,默认都是1,重复key在合并时,是将两个相同的元素的分数进行相加,若还需要乘上一个数在相加就需要在该参数中设置
使用场景
排行榜
使用zadd进行分数添加、zincrby进行分数添加、zrem进行删除,对于这些最核心的就是分数使用什么表示
- 更新榜:使用时间戳作为分数
- 点赞榜:使用点赞量即可
- 月榜周榜日榜:使用日榜进行并集汇总
Bitmap数据结构
结构
其实就是字符串,每个字符都是以ASCII进行编码,位图提供了一种操作字符串中单个二进制位的API,对于这些API来说复杂度是,m取绝于字符串中二进制位的个数
命令
getbit:获取字符串中的指定索引的二进制位- 若超过字符串二进制位数长度,会返回0
setbit:设置字符串中的指定索引的二进制位,只能设置0和1- 若key不存在会创建key
- 若超过字符串二进制位数长度,会在前面自动补零
bitcount:获取指定key中二进制位为1的个数- 可指定范围,注意范围说的是字节范围,8个二进制位为一个字节,闭区间,索引从0开始
bitop:从第二个key开始做位运算,结果存放到第一个key中,返回结果key的字节长度and:与or:或not:非xor:异或
bitpos:获取指定key中,从左到右,第一个为0或1的二进制位索引- 可指定范围,注意范围说的是字节范围,8个二进制位为一个字节,闭区间,索引从0开始
使用场景
用户统计
将所有用户都与一个索引对应(用户ID),将赞或踩过的用户对应的索引位置为1
相比set,若要记录的用户越多则该方式越省内存,反之则会更浪费内存,因为set至少需要一个整型(32位)来记录一个用户,bitMap只需要一个二进制位,若用户ID很大会导致一个用户会使用很大的索引导致索引偏后,前面二进制位都是无用的内容
要注意,Redis中一个字符串的最大容量是512MB,并且在位操作时也比较耗时
HyperLogLog数据结构
结构
基于HyperLogLog算法,极小空间完成独立数量统计的数据结构,本质还是字符串
- 不会有重复元素
- 有错误率,错误率为0.81%
- 无法该结构内元素对单个用户进行判断是否已经存入
命令
pfadd:向指定key中添加元素(可添加多个),添加正常会返回1,而不是添加成功的个数pfcount:返回指定key中的元素个数pfmerge:从第二个key开始进行合并,合并后的结果存放到第一个key中
使用场景
只需要统计用户数,不关心其他问题,并且可以容忍错误率
GEO数据结构
结构
基于ZSet实现,所以可以使用ZSet的所有命令
命令
geoadd:向指定key中添加经纬度和地名(可添加多对),地名不会重复,仅仅其标识作用geopos:获取指定key下的指定地名的经纬度geodist:计算指定key下的两个地名之间的距离unit:指定距离单位,m、km、mi(英尺)、ft(尺),默认是m
georadius:获在指定key中某个地理位置在一定范围内(需要指定单位)的地理位置信息集合withcoord:返回包含经纬度withdist:返回包含距中心节点的距离,单位与指定的一致withhash:返回结果中包含geohash(52位有符号整数)count:返回指定数量结果asc|desc:返回结果按照距离中心位置升序或降序store:将返回结果的位置信息保存在指定键中storedist:将距离保存在指定的键中
使用场景
存储地理信息,计算距离,范围等
发布订阅

命令
publish:向指定频道发送消息(频道就是key),返回订阅者的数量subscribe:订阅频道(可同时订阅多个频道)psubscribe:模式订阅,可使用通配符进行多个频道的订阅
unsubscribe:取消订阅(可同时取消订阅多个频道)punsubscribe:模式取消订阅,可使用通配符进行多个频道的订阅
pubsub channels:返回至少有一个订阅者的频道pubsub numsub:列出指定频道的订阅者数量
消息格式
-
首次开始监听订阅消息时的三行内容:
- 固定内容subscribe,即刚才执行的命令
- 频道的名称,即刚才执行命令的参数
- 固定内容返回1
-
之后的接收的消息时的三行内容:
- 固定内容message
- 频道的名称
- 收到的新消息
代码
Java
发布者
import redis.clients.jedis.Jedis;
public class Main {
public static void main(String[] args) {
Jedis jedis = null;
try {
jedis = new Jedis("127.0.0.1", 6379);
jedis.auth("redis"); //操作数据前设置的密码
jedis.publish("channel", "hello");
} finally {
if (jedis != null) {
jedis.close();
//3. 关闭连接
}
}
}
}
订阅者
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
public class Main {
public static void main(String[] args) {
Jedis jedis = new Jedis("122.51.213.160", 6379);
jedis.auth("redis"); //操作数据前设置的密码
/**
* Jedis对象的subscribe()方法接收两个参数
* JedisPubSub jedisPubSub :用来写监听接收到消息的监听函数,无法使用lambda表达式
* 需要实现内部的onMessage()方法,监听函数会阻塞掉当前线程的运行
* String channel :接收到是那个频道的消息
* String message :接收到的消息
* String... channels :所监听的频道,是个可变参数,所以可以同时监听多个频道
*/
jedis.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.out.println("接收到 " + channel + " 频道发送的:" + message);
}
}, "channel");
}
}
Node
发布者
const redis = require("redis"); //导入redis模块
const client = redis.createClient(6379, "122.51.213.160", {
password: "redis"
}); //获取redis连接对象
client.on("error", function (err) { //监听连接出错
throw err;
});
client.once("connect", () => { //监听连接建立成功
client.publish("testPublish", "message"); //向指定频道发布消息
client.quit(() => console.log("关闭连接")) //关闭连接
})
订阅者
const redis = require("redis"); //导入redis模块
const client = redis.createClient(6379, "122.51.213.160", {
password: "redis"
}); //获取redis连接对象
client.on("error", function (err) { //监听连接出错
throw err;
});
client.once("connect", () => { //监听连接建立成功
client.subscribe('testPublish'); //订阅频道
})
client.on('message', function (channel, msg) { //监听以订阅频道消息
console.log(channel, ":", msg);
});

Comments NOTHING