目录
String 类型内部编码
3 种内部编码方式
String 类型应用场景
Cache 缓存
键名命名规则
计数(Counter)
共享会话(Session )
手机验证码
总结
String 类型内部编码
3 种内部编码方式
- int:用来表示 64 位 ——> 8 字节的整数
- embstr:压缩字符串,针对短字符串进行的特殊优化,适用于比较短的字符串
- raw:普通字符串,适用于表示更长的字符串 只是单纯的字节数组
实例理解
- 此处我们使用 object encoding key 命令来查看 key 对应 value 的编码方式
注意:
- 当 Redis 存储小数时,其本质还是当做字符串来进行存储的,和整数相比有一定差别
- 整数是直接用内部编码 int 来进行存储的(准确来说相当于 Java 中的 long 类型)
- 所以整数在进行算数运算时比较方便
- 但每当小数需要进行算数运算时,都需要把字符串转成小数再进行运算,结果再转回字符串来保存
String 类型应用场景
Cache 缓存
- 使用 Redis 作为缓存,其中绝大部分数据均从 Redis 中获取
- 因为 Redis 具有支撑高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用
- 下图为 Redis + MySQL 组成的缓存存储架构
整体思路
- 当应用服务器访问数据的时,先查询 Redis 中的数据
- 如果 Redis 上存在需访问的数据,就直接从 Redis 中取数据交给应用服务器,不继续访问 MySQL 数据库
- 如果 Redis 上不存在需访问的数据,便向下读取 MySQL 数据库,将读到的结果返回给 Redis,同时也将该数据写入到 Redis 中,继而再由 Redis 返回给应用服务器
注意点一:
- Redis 这样的缓存,经常用来存储 热点 数据,即高频被使用的数据,这个定义方式,结合业务场景有很多种方式
- 刚才上述描述的过程,相当于是把最近使用到的数据作为热点数据
- 暗含了一层假设,某个数据一旦被用到了,那么很可能在最近这段时间就会被反复用到
问题:
- 随着时间的推移,肯定会有越来越多的 key 在 Redis 中访问不到,从而将会有更多的数据从 MySQL 中读取并写入到 Redis 中
- 那么此时 Redis 中的数据不就会越来越多吗?
解决方案:
- 把数据写入 Redis 的同时给这个 key 设置一个过期时间
- Redis 在内存不足的时候,提供了淘汰策略
键名命名规则
- 与 MySQL 关系型数据库不同的是 Redis 没有表、字段这种命名空间,而且也没有对键名有着强制要求(除一些不能用的特殊字符外)
- 设计合理的键名有利于防止 键冲突 和 项目 的可维护性
推荐方式:
- " 业务名 : 对象名 : 唯一标识 : 属性 " 作为键名
实例理解
- MySQL 的数据库名为 " vs " 用户表名为 " user_info "
- 那么对应的键可以使用 " vs:user_info:3679 " 、"vs : user_info : 6379 : name"
- 如果当前 Redis 只会被一个业务使用,便可以省略业务名 " vs "
- 当然也可以使用团队内部都认同的缩写进行替代
- " user : 6379 : friends : messages : 5217 " 可以被 " u : 6379 : fr : m : 5217 " 代替
- 因为 键名过长 也会导致 Redis 的性能明显下降
计数(Counter)
- 许多应用都会使用 Redis 作为计数的基础工具
- 它可以实现快速计数、查询缓存的功能,同时可以异步处理或者落地到其他数据源
- 例如网站视频的播放量、点赞数
实例理解
- 记录 视频网站的视频播放次数
注意点一:
- 此处的 统计数据仓库 可能是 MySQL 也可能是 HDFS(分布式文件系统)
- HDFS 是 Hadoop 的核心组件之一,旨在存储和管理大规模数据集,并提供高度容错性
注意点二:
- 注意理解此处的 异步的方式将播放量同步到其他数据源
- 异步 是指操作不需要立即完成,而是可以延迟执行
- 当面对大量的播放请求时,只要持续不断地进行写入操作就行
通俗理解:
- 不要求 Redis 更新 value 的时候,统计数据仓库也跟着同步更新
- 统计数据仓库更新慢点没有关系,只要保证最终数据能正确写入就行了
注意点三:
- 实际开发中要完成一个成熟、稳定的真实计数系统,要面临的挑战远不止如此简单
挑战包括:
- 防作弊、按照不同纬度计数、避免单点问题、数据持久化到底层数据源等
- 最终我们还是要 根据实际的业务需求场景 进行确定
注意点四:
- Redis 不擅长数据统计
- 比如根据上述实例,统计出播放量前 100 的视频,基于 Redis 搞就十分麻烦
- 相比之下,使用 MySQL 来存储上述数据,一个 sql 就可以搞定
补充:
- 企业为什么总喜欢收集用户的数据?
原因:
- 需对数据进行统计,并根据统计来进一步明确用户的需求,再根据需求改进和迭代产品
共享会话(Session )
- 使用 Redis 将用户的 Session 信息进行机制管理
- 这种模式下,只要保证 Redis 是高可用和可拓展性的,无论用户被均衡到哪台 Web 服务器上,都集中从 Redis 中查询、更新 Session 信息
手机验证码
1、生产验证码
- 用户输入手机号码,点击获取验证码,限制 1分钟之内最多获取 5次验证码 或 每次获取验证码必须间隔 30 秒
- 主要因为怕用户频繁获取验证码,对服务器施加过大压力
2、检查验证码
- 将短信收到的验证码,提交到系统中,并由系统进行检查验证码是否正确
实例理解
- 此处我们提供伪一段代码来进行理解
- 此处的限制条件为 1分钟内最多获取 5次验证码
String 发送验证码(phoneNumber) {key = "shortMsg:limit:" + phoneNumber;// 设置过期时间为 1 分钟(60 秒)// 使⽤ NX,只在不存在 key 时才能设置成功bool r = Redis 执⾏命令:set key 1 ex 60 nxif (r == false) {// 说明之前设置过该⼿机的验证码了long c = Redis 执⾏命令:incr keyif (c > 5) {// 说明超过了⼀分钟 5 次的限制了// 限制发送return null;}}// 说明要么之前没有设置过⼿机的验证码;要么次数没有超过 5 次String validationCode = ⽣成随机的 6 位数的验证码();validationKey = "validation:" + phoneNumber;// 验证码 5 分钟(300 秒)内有效Redis 执⾏命令:set validationKey validationCode ex 300;// 返回验证码,随后通过⼿机短信发送给⽤⼾return validationCode ; }// 验证⽤⼾输⼊的验证码是否正确 bool 验证验证码(phoneNumber, validationCode) {validationKey = "validation:" + phoneNumber;String value = Redis 执⾏命令:get validationKey;if (value == null) {// 说明没有这个⼿机的验证码记录,验证失败return false;}if (value == validationCode) {return true;} else {return false;} }
注意:
- 像发送短信这样的操作,都是有专门的 SDK(软件开发工具包)来实现的,即第三方提供的短信平台服务(收费)
总结
- 以上介绍了四种关于 Redis 字符串数据类型的经典场景,但其适用场景远不止此
- 开发人员可结合字符串类型的特点以及提供的命令,充分发挥自己的想象力,在自己的业务中去找到适合的场景去使用 Redis 的字符串类型