Redis 基本数据类型
参考资料:
主要是 哔哩哔哩 里面的教学视频:https://www.bilibili.com/video/av49517046
学着学着发现,里面基础内容使用的资料是 菜鸟教程 里面 https://www.runoob.com/redis/redis-tutorial.html
里面关于集群的内容 redis 版本是 4.0.1 有点旧,在 centos 7 中总是无法实现主从赋值,集群搭建,在网上找一些资料后安装的版本是 4.0.10 下面是写的很好的博客
VMware 安装 Centos 7: https://blog.csdn.net/babyxue/article/details/80970526
Centos 7 安装Redis 4.0.10 与集群搭建:https://www.cnblogs.com/frankdeng/p/9332618.html
Redis 单机安装:https://www.cnblogs.com/frankdeng/p/9332618.html
Redis 配置主从复制,实现读写分离:https://blog.csdn.net/u014691098/article/details/82391608###
Linux 安装 Redis 集群:https://www.cnblogs.com/gaojingya/p/10601193.html
springboot 结合 Redis Cluster RdisTmplate:https://blog.csdn.net/wo18237095579/article/details/80925586
Redis 默认支持 16 个数据库(是可以通过修改配置文件来增加,无上限),客户端与 Redis 建立连接之后会自动选择 0 号数据库,通过 select 命令可以更换数据库。 select 1 表示选择 1 号数据库
Redis 支持五种数据类型,String、hash、list、set、sorted set
Key 名称是区分大小写的
1.String
是 redis 最近本的类型,该类型是_二进制安全的_,表示 redis 的 string 可以包含任何数据。比如:jpg 图片或者序列化的对象。最大的存储值为 512 M。
二进制安全值,在传输数据时,不被篡改,破译等,如果被攻击,能够及时的检测出来。
常用命令
-
SET key_name value
赋值,如果 key 已经存在存储值,set 会覆盖旧值,且无视数据类型。 -
SETNX key_name value
只有当 key_name 不存在时才添加该数据,SET if Not eXists -
MSET key value [key value ...]
同时设置一个或者多个 key-value 对 -
GET KEY_NAME
取值,如果不存在返回 nil ,如果 key 存储的值不是字符串类型,返回一个错误。 -
GETRANGE key start end
获取 key 对应的值的 子字符串,start 和 end 标记起始点 -
MGET key1 [key2 ...]
用来获取一个或者多个给定 key 的值 -
GETSET key_name value
用来设置指定的 key 的值,如果 key_name 已经指定值,则覆盖并将旧值返回,没有旧值,返回 nil,这个是解决分布式锁的方案之一 -
STRLEN key
返回 key 存储的字符串值的长度 -
INCR key_name
Incr 命令将 key 中存储的 数字值 增加 1,如果不存在,那么 key 的值会先被初始化为 0 然后执行 INCR 操作39.108.141.193:6379> incr topic:num (integer) 1 39.108.141.193:6379> get topic:num "1" 39.108.141.193:6379> incr topic:num (integer) 2 39.108.141.193:6379> incr topic:num (integer) 3 39.108.141.193:6379> get topic:num "3"
INCRBY key_name 增量值
将 key 存储的数字加上指定的增量值 -
DECR key_name
DECRBY key_name 减值
-
APPEND key_name value
为指定的 key 对应的值末尾增加 value 如果不存在,表示为其赋值
应用场景
- String 通常用于保存单个字符串或者 JSON 字符串
- 因为 String 是二进制安全的,所以可以用来存储图片
- 计数器,常规的 key-value 缓存应用。常规技术:微博数,粉丝数
INCR 等指令本身就具有原子性操作的特性,所以我们完全可以利用 redis 的 INCR、INCRBY、DECR、DECRBY 等指令来实现原子计数的效果。
2. Hash
Redis hash 是一个 string 类型的 field 和 value 映射表,hash 特别适合用户存储对象。
每个 hash 可以存储 2^32 -1 键值对(40 多亿)
127.0.0.1:6379> hmset tom name "Tom" gender "male"
OK
127.0.0.1:6379> hget tom name
"Tom"
127.0.0.1:6379> hget tom gender
"male"
看着很像 Java 中的一个实例化之后的对象。
常用命令
-
HSET key_name field value [field value ....]
同时将一个或者多个 field-value 对设置到哈希表 key_name 中 -
HGET key_name field
获取才能出在 hash 中的值,对比于 Java 中的 hash 就是 key_name 为 hashMap 的变量名,field 就是存储在该变量中的 keys 中的一个值 -
HMGET key field [field ....]
获取多个值 -
HGETALL key
获取所有的值 -
HKEYS key
获取所有的 field -
HLEN key
获取哈希表中的字段的数量39.108.141.193:6379> hgetall user:tom 1) "name" 2) "tom" 3) "gender" 4) "male" 5) "age" 6) "12" 39.108.141.193:6379> hlen user:tom (integer) 3 39.108.141.193:6379> hkeys user:tom 1) "name" 2) "gender" 3) "age" 39.108.141.193:6379> hget user:tom age "12"
39.108.141.193:6379> keys user:* 1) "user:marry" 2) "user:tom" 39.108.141.193:6379> hgetall user:tom 1) "name" 2) "tom" 3) "gender" 4) "male" 5) "age" 6) "12" 39.108.141.193:6379> hgetall user:marry 1) "name" 2) "marry" 3) "gender" 4) "femal" 5) "age" 6) "18"
-
HDEL key field [field]
删除一个或者多个 hash 表字段 -
HSETNX key field value
只有在字段field 不存在的时候,设置哈希表字段的值 -
HINCRBY key field increment
为哈希表 key 中指定字段的整数值加上增量 increment -
HINCRBYFLOAT key field increment
为哈希表 key 中的指定字符段的浮点数值加上增量 increment -
HEXISTS key field
查看哈希表 key 中,指定的字段是否存在
应用场景
常用于存储一个对象
原因:
hash 是最接近关系型数据库的数据类型,可以将数据库一条记录或者程序中一个对象转换成 hashmap 存放在 redis 中。
用户 ID 作为查找的 key 存储的 value 用户对象包含姓名,年龄,生日等信息,如果用普通的 key / value 结构里存储,主要有以下 2 种存储方式:
第一种,将用户的 ID 作为查找 key 把其他的信息封装成一个对象一序列化方式存储(如,json 数据),这种方式的缺点是:增加了序列化和反序列化的开销,而且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护(redis 是单线程的)等复杂问题。
第二种,这个用户信息对象有多少成员就存成多少个 key-value 对,用用户的 ID + 对应属性的名称作为唯一标识来取得对应属性的值,虽然省去了序列化开销和并发问题,但是用户 ID 为重复的存储,如果存在大量这样的数据,内存浪费还是非常严重的。删除也是很麻烦的。
总结:
Redis 提供的 Hash 很好的解决了这个问题,Redis 的 Hash 实际是内部存储的 value 为一个 HashMap 并提供了直接存取这个 Map 成员的接口。
3. List
字符串列表,按照插入的顺序排序,可以在元素的头部或者尾部添加一个新的元素。
类似 Java 中的 LinkedList 类型。
每个 list 最多可以存储 2^32 -1 个元素。
127.0.0.1:6379> lpush week mon
(integer) 1
127.0.0.1:6379> lpush week tue
(integer) 2
127.0.0.1:6379> lpush week web
(integer) 3
127.0.0.1:6379> lrange week 0 7
1) "web"
2) "tue"
3) "mon"
常用命令
赋值语法
LPUSH key value [value ...]
将一个或多个值插入到队列头部,也就是左侧添加RPUSH key value [valu ...]
将一个或多个值从后端加入LPUSHNX key value
RPUSHNX key value
取值语句
LLEN key
获取列表的长度LINDEX key value
通过索引获取列表中的元素LRANGE key start end
获取列表中指定范围内的元素。(start 和 end 是允许使用负数的,表示从后往前数,所以想要获取指定列表的所有元素可以使用lrange list 0 -1
,0 表示 第一个, -1 表示最后一个)
删除语法
-
LPOP key
移除并获取列表的第一个元素 -
RPOP key
移除列表的最后一个元素,返回值为移除的元素 -
BLPOP key timeout
移除并获取列表中的第一个元素,如果列表元素会阻塞,直到等待超时或发现可弹出元素位置。时间单位为 毫秒。127.0.0.1:6379>blpop list 100
操作会被阻塞,如果指定的列表 list 存在,会返回该列表的第一个元素,否则会在 100ms 之后返回 nil
-
BRPOP key timeout
操作指定列表的最后一个元素 -
LTRIME key start end
对一个列表进行修剪,让列表仅仅保留指定区间内的元素,区间外的将被删除。
修改语法
LSET key index value
通过索引设置列表元素的值LINSERT key BEFORE | AFTER world value
在列表的元素(world)之前或者之后插入元素(value)
高级语法
RPOPLPUSH source destination
移除列表的最后一个元素,并将该元素添加到另一个列表并返回
例如:
127.0.0.1:6379> RPOPLPUSH a1 a2 // 将 a1 的最后元素移动到 a2 的左侧
127.0.0.1:6379> RPOPLPUSH a1 a1 // 循环列表,将最后一个元素移动到最左侧
BRPOPLPUSH source destination timeout
从列表中弹出一个元素,将弹出的元素插入到另一个列表中并返回,如果列表中没有元素会阻塞直到等待超时或者发现可弹出的元素位置。
应用场景
- 对数据量大的集合数据删减操作
- 任务队列
1. 对数据量大的集合数据删减操作
列表数据显示,关注列表,粉丝列表,留言评价等,分页,热点新闻(top5)等。
利用 LRANGE
可以很方便的实现分页的功能,在博客系统中,每篇博文的评论可以存入到一个单独的 list 中。
2. 任务队列
list 通常用来实现一个消息队列,而且可以确认先后顺序。
任务队列介绍(生产者和消费者):
在处理 web 客户端发送的请求命令时,某些操纵的执行时间可能会比我们预期的更长一些,通过将待执行任务的相关信息放入到队列中,并在之后对队列进行处理,用户可以推迟执行那些需要一段时间才能完成的操作,这种共组交给任务处理器来执行的做法被称为任务队列。
RPOPLPUSH source destination
移除列表最后一个元素,并将该元素添加到另一个列表并返回。
常用案例:订单系统的下单流程,用户系统登录注册短信等。
用户系统登录注册短信:有的网站是首先允许用户注册登录进去,但是没有强制进行实名认证。当执行一些保密性较强的操作的时候会查看实名认证的操作是否完成,才会允许后续操作。整个注册、认证等也可以是一个队列。
模拟订单流程:
在完成付款之后,后台会帮助我们生成一个物流队列。
商品的 ID 为 key 值
北京 --> 南京:
商家出货 – 小哥发货 – 北京海定区 某小区 – 首都机场 – 南京机场 – 建业区 – 送达 – 商品评价
private ListOperations<String, String> list = redisTemplate.opsForList();
// 订单流程实例
@GetMapping("/listQueueInit/{cardId}")
public void listQueueInit(@PathVariable("cardId") String cardId) {
String key = "prod:" + cardId; // 初始化的 key 确定需要完成的任务
list.leftPushAll(key, "1. 商家发货", "2 小哥派件", "3.小哥发货 -- 北京海定区 某小区",
"4.北京海定区 某小区 -- 首都机场", "5.首都机场 -- 南京机场 ",
"6.南京机场 -- 建业区","7. 建业区 -- 送达", "8.送达 -- 商品评价");
}
// 触发事件
@GetMapping("/listQueueTouch/{cardId}")
public void listQueueTouch(@PathVariable("cardId") String cardId) {
String key = "prod:" + cardId + ":succ"; // 已经完成的任务队列
list.rightPopAndLeftPush("prod:" + cardId, key);
}
// 客户查询快递位置
@GetMapping("/listQueueSucc/{cardId}")
public List<String> listQueueSucc(@PathVariable("cardId")String cardId) {
String key = "prod:" + cardId + ":succ"; // 已完成任务队列
return list.range(key, 0, -1);
}
// 快递公司的物流查询
@GetMapping("/listQueueWait/{cardId}")
public List<String> listQueueWait(@PathVariable("cardId")String cardId) {
String key = "prod:" + cardId;
return list.range(key, 0, -1);
}
4. Set
string 类型的无序集合,集合是通过哈希表实现的,所以添加、删除、查找的复杂度都是O(1)。
每个 set 最多可以存储 2^32 -1 个元素。
类似于 Java 中的 Hashtable 集合,是 无序的
redis 的集合对象 set 底层使用 intset 和 hashtable 两种数据结构存储的,intset 可以理解为数组,hashtable 就是普通的 哈希表
intset 内部其实是一个数组(int8_t coentents[] 数组),而且存储数的时候是有序的,因为在查找数据的时候可以通过二分法来实现。
127.0.0.1:6379> sadd month jan
(integer) 1
127.0.0.1:6379> sadd month feb
(integer) 1
127.0.0.1:6379> sadd month feb
(integer) 0
127.0.0.1:6379> smembers month
1) "jan"
2) "feb"
常用命令
-
SADD key member [member ...]
向集合中添加一个或者多个成员 -
SCARD key
获取集合中的成员个数 -
SMEMBERS key
获取集合中所有的成员 -
SISMEMBER key member
判断 member 元素是否是集合 key 的成,开发中可以测试是否存在 -
SRANDMEMBER key [count]
返回集合中一个或者多个随机数,可以用来实现抽奖功能 -
SREM key member [member ...]
移除集合中一个或者多个成员 -
SPOP key [count]
移除并返回集合中的一个随机元素 -
SMOVE source destination member
将 member 元素从 source 集合移动到 destination 集合 -
SDIFF key1 [key ...]
返回给所有集合的差集(左侧)SDIFFSTORE destination key1 [key ...]
返回所有集合的差集并存储在 destination -
SINTER key1 [key ...]
返回给定所有集合的交集
SINTER STORE destination key1 [key ...]
返回所有集合的交集并存储在 destination
-
SUNION key1 [key ...]
返回所有给定集合的并集SUNIONSTORE destination key1 [key ...]
返回所有集合的并集并存储在 destination
应用场景
对两个集合间的数据进行交集,并集,差集计算
- 非常方便的实现共同关注,共同爱好,二度好友等功能,对上面的所有集合操作,还可以使用不同的命令选择将结果返回给客户端或存储到一个新的集合中
- 利用唯一性,可以统计访问网站的所有独立的 IP
5. Sorted Set(zset)
zset 和 set 一样是一个 string 类型的元素的集合,并且不允许重复的成员。
不同的是每个元素都会关联一个 double 类型的 score,redis 正是通过这个 score 来为集合中的成员进行从小到大的排序,zset 的成员是唯一的,但是 score 是可以重复的。
常用命令
ZADD key score1 member1 [score2 member2 ...]
向有序集合添加一个或者多个成员,或者更新已经存的成员的分数ZCARD key
获取有序集合的成员数ZCOUNT key min max
计算在有序集合中指定区间分数的成员数ZRANK key member
返回有序结合中指定的成员的索引ZRANGE key start end [WITHSCORES]
通过索引区间返回有序集合成指定区间内的成员(低到高)ZREVRANGE key start end [WITHSCORES]
返回有序集合中指定区间内的成员,通过索引,从高到低ZREM key member [member ...]
移除有序集合中的一个或者多个成员ZREMRANGEBYRANK key start end
通过 rank 进行排序(由低到高)之后根据下标删除执行范围的数据(下表从 0 开始)ZREMRANGEBYSCORE key min max
删除指定 score 范围的数据
应用场景
排行榜
-
比如 一个存储全班同学成绩的 Sorted Set 其集合的 value 可以是同学的学号,而 score 可以是学生的分数,这样在数据插入集合的时候,就进行了自动排序
-
做权重队列,比如,普通消息的 score 为 1 重要消息的 score 为 2 然后工作线程可以按照 score 的倒序来获取工作任务。让重要的任务优先执行。