- 今日内容
- 数据存储类型介绍
- string
- **Redis数据存储格式**
- **string类型**
- **string类型数据的基本操作**
- **string类型数据的扩展操作**
- **string类型数据操作的注意事项**
- **string类型应用场景**
- **key的设置约定**
- hash
- list
常用的数据类型一共有一下5种:
- string
- hash
- list
- set
- sorted_set
- 数据类型实践案例
业务数据的特殊性
- 作为缓冲使用
- 原始业务功能设计
- 秒杀
- 京东618活动
- 天猫双11活动
- 火车排队购票
- 运营平台监控到的突发高频访问数据
- 突发时政要闻,被强势关注围观
- 高频、复杂的统计数据
- 在线人数
- 投票排行榜
- 附加功能
- 系统功能优化或升级
- 单服务器升级集群
- Session管理
- Token管理
Redis数据类型(5种常用)
- string
- hash
- list
- set
- sorted_set
- Redis自身是一个Map,其中所有的数据都是采用key:value的形式存储
- 数据类型指的是存储的数据的类型,也就是value部分的类型,key部分永远都是字符串
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2NBOyzvI-1659702772759)(en-resource://database/4938:1)]
- 存储的数据:单个数据,最简单的数据存储类型,也是最常用的数据存储类型
- 存储数据的格式:一个存储空间保存一个数据
- 存储内容:通常使用字符串,如果字符串以整数的形式展示,可以作为数字操作使用,但其本质还是字符串
- 添加/修改数据:set key value
- 添加/修改多个数据:mset key1 value1 key2 value2…
- 追加信息到原始信息后部(如果原始信息存在就追加,否则新建):append key value
- 获取数据:get key
- 获取多个数据:mget key1 key2…
- 获取数据字符个数(字符串长度):strlen key
- 删除数据:del key
- (integer) 0 代表失败
- (integer) 1 代表成功
业务场景
大型企业级应用中,分表操作是基本操作,使用多张表存储同类型数据,但是对应的主键id必须保证统一性,不能重复。Oracle数据库具有sequence设定,可以解决该问题,但是MySQL数据库并不具有类似的机制,那么该如何解决呢?
解决方案:
- 设置数值数据增加指定范围的值
- incr key 如果是数值,则给value做增操作,每次增加1个单位
- incrby key increment 增加指定的整数的值(可以为负数)
- incrbyfloat key increment 增加指定的小数的值
- 设置数值数据减少指定范围的值
- decr key 如果是数值,则给value做减操作,每次减少1个单位
- decrby key increment 减少指定的整数的值(可以为负数)
string作为数值操作
- string在redis内部存储默认就是一个字符串,当遇到增减类操作incr,decr时会转成数值型进行计算。
- redis所有的操作都是原子性的,采用单线程处理所有业务,命令是一个一个执行的,因此无需考虑并发带来的数据影响。
- 注意:按数值进行操作的数据,如果原始数据不能转成数值,或超越了redis 数值上限范围,将报错。
9223372036854775807(java中long型数据最大值,Long.MAX_VALUE)
Tips 1:
- redis用于控制数据库表主键id,为数据库表主键提供生成策略,保障数据库表的主键唯一性
- 此方案适用于所有数据库,且支持数据库集群
业务场景
- “最强女生”启动海选投票,只能通过微信投票,每个微信号每 4 小时只能投1票。
- 电商商家开启热门商品推荐,热门商品不能一直处于热门期,每种商品热门期维持3天,3天后自动取消热门。
- 新闻网站会出现热点新闻,热点新闻最大的特征是时效性,如何自动控制热点新闻的时效性。
解决方案
- 设置数据具有指定的生命周期
- setx key seconds value 秒
- 例如:setex tel 10 1 当10s到达后,tel就失效为空了(没有了)
- 例如:set tel 2 此命令已经更新,上面的语句已经没有用了
- psetex key milliseconds value 毫秒
- 例如:psetex tel 9999 1
- setx key seconds value 秒
string类型数据操作的注意事项Tips 2:
redis 控制数据的生命周期,通过数据是否失效控制业务行为,适用于所有具有时效性限定控制的操作
- 数据操作不成功的反馈与数据正常操作之间的差异
- 表示运行结果是否成功
- (integer) 0 -> false 失败
- (integer) 1 -> true 成功
- 表示运行结果值
- (integer) 3 -> 3 3个
- (integer) 1 -> 1 1个
- 数据为获取到
- (nil) 等同于null 不存在
- 数据最大存储量
- 512MB
- 数值计算最大范围(java中的long的最大值)
- ±9223372036854775807
- 表示运行结果是否成功
业务场景
- 主页高频访问信息显示控制。
- 例如新浪微博大V主页显示粉丝数与微博数量
解决方案
- 在Redis中为大V用户设定用户信息,以用户主键和属性值作为key,后台设定定时刷新策略即可。
- eg: user3506728370:fans → 12210947
- eg: user3506728370:blogs → 6164
- eg: user3506728370:focuss → 83
- 例子:set user00789:blogs 789
- 例子:set user:id00789:fans 123456789
- 在Redis中以json格式存储大V用户信息,定时刷新(也可以使用hash类型)
- eg: user3506728370 →
{“id”:3506728370,“name”:“春晚”,“fans”:12210862,“blogs”:6164, “focus”:83} - 例子:set user00789 {id:00789,blogs:789,fans:999}
- 使用这种方式: incr user00789:blogs --> (integer) 790
- eg: user3506728370 →
key的设置约定Tips 3:
redis应用于各种结构型和非结构型高热度数据访问加速
- 数据库中的热点数据key命名规范
存储的困惑
hash类型
- 新的存储需求:对一系列存储的数据进行编组,方便管理,典型应用存储对象信息
- 需要的存储结构:一个存储空间保存多个键值对数据
- hash类型:底层使用哈希表结果实现数据存储
hash类型数据的基本操作
- 添加/修改数据
- hset key field value
- 例子:hset user name zhangsan
- 例子:hset user age 38
- 例子:hser user weight 80
- hset key field value
- 获取数据
- hget key field
- 单查询:hget user name
- hgetall key
- 全查询:hgetall user
- hget key field
- 删除数据
- hdel key field1 [field2]
- 删除weight:hdel user weight
- hdel key field1 [field2]
- 添加/修改多个数据
- hmset key field1 value1 field2 value2…
- 例子:hmset user name zhangsanfeng weight 68 已经有的就改了,没有的就加上去了。
- 获取多个数据
- hmget key field1 field2…
- 例子:hmget user name age -->纯值
- 获取哈希表中字段的数量
- hlen key
- 例子:hlen user 看的是field的字段
- 获取哈希表中是否存在指定的字段
- hexists key field
- 例子:hexists user age – (integer) 1 true
- 例子:hexists user hight – (integer) 0 false
hash类型数据扩展操作
- 获取哈希表中所有字段名或字段值
- hkeys key
- 例子:hkeys user 打印field
- hvals key
- 例子:hvals user 打印value
- hkeys key
- 设置指定字段的数值数据增加指定范围的值
- hincrby key field increment
- 例子: hincrby user age 2 value字段增加整数范围内的值
- hincrbyfloat key field increment
- 例子:hincrbyfloat user age 1.1 value字段增加小数范围内的值
- hincrby key field increment
hash类型数据操作的注意事项
- hash类型下的value只能存储字符串,不允许存储其他数据类型,不存在嵌套现象。如果数据未获取到对应的值为(nil)
- 每个hash键值对式有上限的,可以存储2^32 -1个键值对
- hash类型十分贴近对象的数据存储形式,并且可以灵活添加删除对象属性,但hash设计初衷不是为了存储大量对象设计的,切记不可滥用,更不可以将hash作为对象列表使用(随着对象存储的增多,会很麻烦,效率会很低)
- hgetall操作可以获取全部属性,如果内部field过多,遍历整体数据效率就会很低,有可能成为数据访问瓶颈
hash类型应用场景
业务场景
电商网站购物车设计与实现
业务分析
- 仅分析购物车的Redis存储模型
- 添加、浏览、更改数量、删除、清空
- 购物车于数据库间持久化同步(不讨论)
- 购物车于订单间的关系(不讨论)
- 提交购物车:读取数据生成订单
- 商家临时价格调整:隶属于订单级别
- 未登录用户购物车信息存储(不讨论)
- cookie存储
解决方案
- 以客户id作为key,每位客户创建一个hash存储结构存储对应的购物车信息
- 将商品编号作为field,购买数量作为value进行存储
- 添加商品:最佳全新的field与value
- 浏览:遍历hash
- 更改数量:自增/自减,设置value的值
- 删除商品:删除field
- 清空:删除key
购物车实现
- 用户001 商品g01 g02
- hmset 001 g01 100 g02 200
- 用户002 商品g02 g04 g05
- hmset 002 g02 1 g04 7 g05 100
- 给001用户添加一个商品 g03 添加field
- hset 001 g03 5
- 查看用户001的所有商品
- hgetall 001
- 用户001 的g01商品不要了 删除field
- hdel 001 g01
- 用户001 的 g03商品 在加10个 添加field对应的值
- hincrby 001 g03 10
提问:我们当前的设计是否加速了购物车的呈现?
当前仅仅是将数据存储到了Redis中,并没有起到加速的作用,商品信息还需要二次查询数据库。
- 每条购物车中的商品记录保存成两条field
- field1专用与保存购买数量
- 命名规则:商品ID:nums
- 保存数据:数值
- field2 专用于保存购物车中显示的信息,包含文字描述,图片地址,所属商家信息等
- 命名格式:商品ID:info
- 保存数据:json
实现
- 用户003 商品信息 json
- hmset 003 g01:nums 100 g01:info {…}
- 查看一下:hgetall 003
- 用户004 商品信息
- hmset 004 g01:nums 5 g01:info {…} 与003用户命令差不太多
- 查看一下:hgetall 004
- 出现问题:大量的信息重复
1.
2. hsetnx key field value key中的field如果存在,就无效(不动),不存在则生效(创建) - 独立hash,提升速度效率
Tips 4:
redis 应用于购物车数据存储设计
业务场景
商家的东西
- 双11活动日,销售手机充值卡的商家对移动、联通、电信的30元、50元、100元商品推出抢购活动,每种商品抢购上限1000张。
解决方案
- 以商家ID作为key
- 将参与抢购的商品ID作为field
- 将参与抢购的商品数量作为对应的value
- 抢购时使用降值的方式控制产品数量
- 实际业务总还有超卖等实际问题,这里不做讨论
实现
- 设置抢购的商品
- hmset p01 c30 1000 c50 1000 c100 1000
- 假设有一个人买到了一张,p50卡
- hincrby p01 c50 -1
- 大老板购买p100的20张
- hincrby p01 c100 -20
- 查看商品售卖情况
- hgetall p01
Tips 5:
redis 应用于抢购,限购类、限量发放优惠卷、激活码等业务的数据存储设计
业务场景
- string存储对象(json)与hash存储对象
- string讲究一次性,讲究读
- hash有一个分隔(field)讲究的是更新
list类型
- 数据存储需求:存储多个数据,并对数据进入存储空间的顺序进行区分
- 需要的存储结构:一个存储空间保存多个数据,且通过数据可以体现进入顺序
- list类型:保存多个数据,底层是用双向链表存储结构实现
list 类型数据基本操作
- 添加/修改数据
- lpush key value1 [value2] … 从左边进
- 例子:lpush list1 huawei
- lpush list1 apple
- lpush list1 microsoft
- rpush key value1 [value2] … 从右边进
- 例子: rpush list2 a b c
- lpush key value1 [value2] … 从左边进
- 获取数据
- lrange key start stop list有索引,start stop指的是索引 从n开始,从m结束
- 例子: lrange list1 0 4
- lrange list1 0 -1 这就可以看完
- lindex key index index代表的是索引
- 例子:lindex list1 0 0号索引是什么?
- llen key
- 例子:llen list1
- lrange key start stop list有索引,start stop指的是索引 从n开始,从m结束
- 获取并移除数据
- lpop key 从左边出(把key中的元素拿出来)
- 添加数据:lpush list3 a b c
- 拿出数据:lpop list3 --> c
- 查字段:llen list3 --> 2
- rpop key
- 向右拿出数据: rpop list3 --> a
- 查字段: llen list3 --> 1
- lpop key 从左边出(把key中的元素拿出来)
list 类型数据扩展操作
- 规定时间内获取并移除数据
- blpop key1 [key2] timeout
- brpop key1 [key2] timeout
- 例子:
- 添加数据: lpush list0 a b
- 拿出数据:lpop list0
- 阻塞版本:我可以等[30s]
- blpop list0 10 [key …] timeout
- 能拿出来(key中存在)就拿出来,等效于lpop
- 不能拿出来就会等待(阻塞) 之后返回nil 或者直到key中存在,然后马上把它取出来
业务场景
- 微信朋友圈点赞,要求按照点赞顺序显示点赞好友信息
- 如果取消点赞,移除对应的好友信息
解决方案
- 移除指定数据
- lrem key count value
- l表示从左边删除 count代表个数 value删除哪个东西
实现
- 进来5个人
- rpush 005 a b c d e
- 查看那些人
- lrange 005 0 -1
- 现在c取消点赞了 动了中间的
- lrem 005 1 c
- 再加点东西
- rpush 005 a b a b a c e
- 删除3个a
- lrange 005 0 -1
Tips 6:
redis 应用于具有操作先后顺序的数据控制
list 类型数据操作注意事项
- list中保存的数据都是string类型的,数据总容量是有限的,最多2^32-1个元素(4294967295)
- list具有索引的概念,但是操作数据时通常以队列的形式进行入队出队操作,或以栈的形式进行入栈出栈操作
- 获取全部数据操作结束索引设置为-1
- list可以对数据进行分页操作,通常第一页的信息来自于list,第二页及更多的信息通过数据库形式加载
list 类型应用场景
业务场景
-
Twitter、新浪微博、腾讯微博中个人用户的关注列表需要按照用户的关注顺序进行展示,,粉丝列表需要将最近的粉丝列在前面
-
新闻、资讯类网站如何将最新的新闻或资讯按照发生的时间顺序展示?
-
企业运营过程中,系统将产生出大量的运营数据,如何保障多台服务器操作日志的统一顺序输出?
解决方案
- 依赖list的数据具有顺序的特征对信息进行管理
- 使用队列模型解决多路信息汇总合并的问题
- 使用栈模型解决最新消息的问题
实现
Redis充当的是汇总的角色,非常好的是它有顺序
- 窗口1
- rpush logs a1
- rpush logs a1…
- lrange logs 0 -1
- 窗口2
- rpush logs b1
- rpush logs b1…
- 窗口3
- rpush logs c1
- rpush logs c1…
Tips 7:
redis 应用于最新消息展示
——此文档为学习笔记!



