常用数据结构
Redis常用的5种数据结构分别是, string, hash, list, set, zset, 其中string是基本数据类型, 其他4种都是基于string的某种容器, 其内部将一些string按照某种结构组织起来, 提供了便于访问内部string的各种api
有一些Api是基于不同数据结构使用的, 有一些Api是公用的, 适用于多种数据结构, 如 del, exists, expire, keys, ttl, type, rename, scan 等命令
string
在从Redis中获取值然后转换成Java基本数据类型时会有类型转换异常的问题
/**
* get
*
* 通过Json转换后,基本数据类型会被反序列化为默认的类型(Integer和Float),
* 存Byte,Short,Long取出来是Integer,存Double取出来是Float,可能还有其他的,
* 需要做特殊处理(我甚至怀疑使用JdkSerializationRedisSerializer会更好,虽然不利于查看,但是会携带类型等信息,api使用会比较方便)
* 需要明确存储时使用的类型,滥用可能会导致精度丢失
*
* String value = (String) redis.get("key");
* List<String> values = (List<String>) redis.get("key");
*/
@SuppressWarnings("unchecked")
public <T> T get(String key, Class<T> clazz) {
Object object = redisTemplate.opsForValue().get(key);
if ((clazz == Long.class || clazz == long.class) && object instanceof Integer) {
Integer integerObject = (Integer) object;
return (T) Long.valueOf(integerObject.longValue());
} else if ((clazz == Short.class || clazz == short.class) && object instanceof Integer) {
Integer integerObject = (Integer) object;
return (T) Short.valueOf(integerObject.shortValue());
} else if ((clazz == Byte.class || clazz == byte.class) && object instanceof Integer) {
Integer integerObject = (Integer) object;
return (T) Byte.valueOf(integerObject.byteValue());
} else if ((clazz == Float.class || clazz == float.class) && object instanceof Double) {
Double doubleObject = (Double) object;
return (T) Float.valueOf(doubleObject.floatValue());
} else if ((clazz == Character.class || clazz == char.class) && object instanceof String) {
String StringObject = (String) object;
return (T) Character.valueOf(StringObject.charAt(0));
} else {
return (T) object;
}
}
场景
单值缓存
缓存页面的访问次数
对象缓存
缓存某配置项的对象 (建议配置好Spring的StringRedisSerializer和GenericJackson2JsonRedisSerializer, 直接存储和获取对象本身, 无需自行转换Json)
分布式锁
最基本的 setNX
计数器
电商项目中, 存储某个商品库存
session共享
分布式全局序列号
incrby, 一次一个太浪费, 可一次多个, 本地缓存, 用完再取
hash
类似Map
场景
对象缓存
一个字段当做一个内Key
电商购物车
以用户标识为Key, 以商品标识为Field, 商品数量为Value
操作: 添加商品-hset, 增加数量-hincrby, 商品总数-hlen, 删除商品-hdel, 获取所有-hgetall
优点
- 同类数据归类整合存储, 方便数据管理
- 相比string, 操作消耗的内存和cpu更少
- 相比string, 存储更节省空间
缺点
- 过期功能只能使用在Key上, 不能使用在Field上
- Redis集群结构下, 使用需要考虑数据平衡, 防止大key产生
list
类似List, 有序
可以实现Stack, Queue, BlockingQueue 的效果
- Stack: lpush + lpop
- Queue: lpush + rpop
- BlockingQueue: lpush + brpop
场景
消息流, 如微博消息和微信公众号消息
假如我(mrathena)关注了XCIOS和备胎说车两个微信公众号, 微信给每个用户都单独维护了一个list(我的list的key是msg:mrathena)
- XCIOS发布了一篇文章, lpush msg:mrathena 文章id
- 备胎说车发布了一篇文章, lpush msg:mrathena 文章id
- 我查看微信公众号消息, lrange msg:mrathena 0 4
如果粉丝太多或者关注了太多的人, 可以配合push和pull两种模式, 可以先给在线用户push, 后面再分批给其他用户push, 或者直接等用户上线时自己到大v的list中获取其最新消息, …
set
类似 Set, 无序
除了常规操作还有取交集, 并集, 差集的功能
- 取交集: sinter key1 key2
- 取交集并把结果存到新key中: sinterstore newKey key1 key2
- 取并集: sunion key1 key2, sunionstore newKey key1 key2
- 取差集(从key1中减去后面所有集合的并集): sdiff key1 key2 key3 [key …], sdiffstore newKey key1 key2
场景
抽奖
年会现场抽奖, 假如要从所有人中抽3个三等奖, 2个二等奖, 1个一等奖, 每个人只有一次获奖机会
- 把每一个员工加入到集合中 - sadd key mrathena
- 查看参与抽奖的员工 - smembers key
- 抽取count名获奖者(可再被抽到) - srandmember key count
- 抽取count名获奖者(不可再被抽到) - spop key count
点赞,收藏
微信朋友圈, 假如我发了一个朋友圈(我的消息id是101)
- 点赞 - sadd like:101 点赞用户id
- 取消但咱 - srem like:101 点赞用户id
- 判断用户是否点赞过 - sismember like:101 用户id
- 获取点赞的用户列表(zset有序) - smembers like:101
- 获取点赞用户数 - scard like:101
关注模型
微博关注, 我关注了张靓颖, 我和她有共同关注, 我关注的人也关注她
假如aSet关注了bc,bSet关注了acdef,cSet关注了abce,dSet关注了cf, 我是a, 我当前在看d的信息
- a和d的共同关注 - sinter aSet dSet - c
- a关注的人也关注他(d) - sismember bSet d, sismember cSet d
- a可能认识的人(d比我多关注的人) - sdiff dset aSet - f
电商商品筛选
sadd brand:huawei p40
sadd brand:xiaomi mi10
sadd brand:iphone iphone12
sadd os:android p40 mi10
sadd cpu:gaotong p40 mi10
sadd ram:6g p40 mi10 iphone12pro
- 查找符合条件6g运存,安卓系统,高通cpu的手机 - sinter os:android cpu:gaotong ram:6g - p40,mi10
zset
和 set 一样, 只不过每个元素都加了一个 score 属性, 可排序, 可以为操作该分值
场景
排行榜
微博热搜
- 点击新闻 - zincrby hotnews:20201030 1 iphone12
- 展示当日排行前十 - zrevrange hotnews:20201030 0 9 withscore
- 三日榜单统计 - zunionstore hotnews:20201028-20201030 hotnews:20201028 hotnews:20201029 hotnews:20201030
- 三日榜单前十 - zrevrange hotnews:20201028-20201030 0 9 withscore
公共命令
其他公共命令在该命令的相关命令中
keys
全量遍历键, 列出所有满足特定正则字符串规则的key, 当redis数据量比较大时, 性能比较差, 要避免使用
scan
SCAN cursor [MATCH pattern] [COUNT count]
渐进式遍历键, 第一次遍历时, cursor值为0, 然后将返回结果中的第一个整数值作为下一次遍历的cursor, 一直遍历到返回的cursor的值为0
在scan的过程中如果有键的增删改或者rehash操作调整redis的存储结构等变化, 遍历可能会出现丢失key, 重复key等情况, 需要考虑
有三个参数
- cousor: 游标, 即hash桶的索引值
- pattern: key的正则匹配模式
- count: 一次遍历的key的数量(是从所有key中取count个返回其中满足条件的, 并不是取出count个满足条件的, 参考值, 或多或少)
info
查看redis服务运行信息, 分为 9 大块, 每个块都有非常多的参数
- Server: 服务器运行的环境参数
- Clients: 客户端相关信息
- Memory: 服务器运行内存统计数据
- Persistence: 持久化信息
- Stats: 通用统计数据
- Replication: 主从复制相关信息
- CPU: CPU 使用情况
- Cluster: 集群信息
- KeySpace: 键值对统计数量信息
connected_clients:2 # 正在连接的客户端数量
instantaneous_ops_per_sec:789 # 每秒执行多少次指令
used_memory:929864 # Redis分配的内存总量(byte),包含redis进程内部的开销和数据占用的内存
used_memory_human:908.07K # Redis分配的内存总量(Kb,human会展示出单位)
used_memory_rss_human:2.28M # 向操作系统申请的内存大小(Mb)(这个值一般是大于used_memory的,因为Redis的内存分配策略会产生内存碎片)
used_memory_peak:929864 # redis的内存消耗峰值(byte)
used_memory_peak_human:908.07K # redis的内存消耗峰值(KB)
maxmemory:0 # 配置中设置的最大可使用内存值(byte),默认0,不限制
maxmemory_human:0B # 配置中设置的最大可使用内存值
maxmemory_policy:noeviction # 当达到maxmemory时的淘汰策略