Redis——Redis五大数据类型——string和hash(及应用场景)

Redis的介绍

Redis是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件MQ。它支持多种类型的数据结构,如 字符串(string),散列(hashes),列表(list),集合(sets),有序集合(sorted sets)与范围查询,bitmaps,hyperloglogs和地理空间(geospatial)索引半径查询。Redis内置了 复制(replocation),LUA脚本(Lua scripting),LRU驱动事件(LRU eviction),事务(transactions)和不同级别的 磁盘持久化(persistence),并通过 Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(high availability)

Redis常用类型

  • string String
  • hash HashMap
  • list LinkedList
  • set HashSet
  • sorted_set TreeSet

redis 数据存储格式

  • redis自身是一个Map,其中所有的数据都是采用key:value的形式存储
  • 数据类型指的是存储的数据的类型,也就是value部分的类型,key部分永远都是字符串

string

  • 存储的数据:单个数据,最简单的数据存储类型,也是最常用的数据存储类型
  • 存储数据的格式:一个存储空间保存一个数据
  • 存储内容:通常使用字符串,如果字符串以整数的形式展示,可以作为数字操作使用

string 类型数据的基本操作

  • 添加/修改数据

    set key value

  • 获取数据

    get key

  • 删除数据

    del key

127.0.0.1:6379> set name maomao
OK
127.0.0.1:6379> get name
"maomao"
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> get name
(nil)

  • 添加/修改多个数据

    mset key1 value1 key2 value2 …

  • 获取多个数据

    mget key1 key2 …

  • 获取数据字符串长度(字符串个数)

    strlan key

  • 追加信息到原始信息后部(如果原始信息存在就追加,否则新建

    append key value

127.0.0.1:6379> mset k1 a k2 b k3 c
OK
127.0.0.1:6379> mget k1 k2 k3
1) "a"
2) "b"
3) "c"
127.0.0.1:6379> set name maomao
OK
127.0.0.1:6379> strlen name
(integer) 6
127.0.0.1:6379> append hello world
(integer) 5
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> append name zhu
(integer) 9
127.0.0.1:6379> get name
"maomaozhu"

string 类型数据的扩展操作

数据增减操作

业务场景

  • 大型企业级应用中,分表操作是基本操作,使用多张表存储同类型数据,但是对应的主键 id 必须保证统一性,不能重复。Oracle 数据库具有 sequence 设定,可以解决该问题,但是 MySQL数据库并不具有类似的机制,那么如何解决?

解决方案

  • 设置数值数据增加指定范围的值

    incr key
    incrby key increment
    incrbyfloat key increment

  • 设置数值数据减少指定范围的值

    decr key
    decrby key increment

redis本身就是单线程的,所以可以用这种方式解决分布式ID的问题

127.0.0.1:6379> set num 0
OK
127.0.0.1:6379> incr num	# 自增1
(integer) 1
127.0.0.1:6379> incr num
(integer) 2
127.0.0.1:6379> get num
"2"
127.0.0.1:6379> decr num	# 自减1
(integer) 1
127.0.0.1:6379> decr num
(integer) 0
127.0.0.1:6379> decr num
(integer) -1
127.0.0.1:6379> get num
"-1"
127.0.0.1:6379> incrby num 10	# 可以设置步长 指定增量
(integer) 9
127.0.0.1:6379> incrby num 10
(integer) 19
127.0.0.1:6379> decrby num 9
(integer) 10
127.0.0.1:6379> incrbyfloat num 2.5		# 小数
"12.5"
127.0.0.1:6379> incrby num 3.5	# incrby只能是int类型
(error) ERR value is not an integer or out of range

string 作为数值操作

  • string在redis内部存储默认就是一个字符串,当遇到增减类操作incr,decr时会转成数值型进行计算。
  • redis所有的操作都是原子性的,采用单线程处理所有业务,命令是一个一个执行的,因此无需考虑并发
    带来的数据影响。
  • 注意:按数值进行操作的数据,如果原始数据不能转成数值,或超越了redis 数值上限范围,将报错。
    9223372036854775807(java中long型数据最大值,Long.MAX_VALUE)

redis 用于控制数据库表主键id,为数据库表主键提供生成策略,保障数据库表的主键唯一性。适用于所有数据库,且支持数据库集群

数据时效性设置

业务场景

  • 海选投票,只能通过微信投票,每个微信号每 4 小时只能投1票。
  • 电商商家开启热门商品推荐,热门商品不能一直处于热门期,每种商品热门期维持3天,3天后自动取消热门。
  • 新闻网站会出现热点新闻,热点新闻最大的特征是时效性,如何自动控制热点新闻的时效性。

解决方案

  • 设置数据具有指定的生命周期

    setex key seconds value
    psetex key milliseconds value

  • 设置独立的数据

    setnx key value

127.0.0.1:6379> setex tel 10 '123456789'	# 设置一个keys的值为num 10秒后过期
OK
127.0.0.1:6379> ttl tel		# 生命周期
(integer) 6
127.0.0.1:6379> get tel
"123456789"
127.0.0.1:6379> ttl tel
(integer) 1
127.0.0.1:6379> get tel
(nil)
127.0.0.1:6379> setnx subject 'redis'	# 如果key不存在 则创建相应的
(integer) 1
127.0.0.1:6379> setnx subject 'mongodb'	# 如果key存在 则创建失败
(integer) 0
127.0.0.1:6379> get subject
"redis"

127.0.0.1:6379> psetex zhu 5000 'zhuzhu'	# 毫秒
OK
127.0.0.1:6379> ttl zhu
(integer) 1
127.0.0.1:6379> get zhu
(nil)

127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> get k4
(nil)
127.0.0.1:6379> msetnx k1 v1 k4 v4	# msetnx 是一个原子性的操作,要么一起成功,要么一起失败
(integer) 0
127.0.0.1:6379> get k4
(nil)
127.0.0.1:6379> get k1
"v1"

redis 控制数据的生命周期,通过数据是否失效控制业务行为,适用于所有具有时效性限定控制的操作

string类型数据的一些其他操作
  • 获取字符串的范围

    getrange key start end

  • 替换指定范围的字符串

    setrange key offset value

127.0.0.1:6379> set name 'hello,maomao'
OK
127.0.0.1:6379> get name
"hello,maomao"
127.0.0.1:6379> getrange name 0 3
"hell"
127.0.0.1:6379> getrange name 0 -1
"hello,maomao"
127.0.0.1:6379> setrange name 2 mmm
(integer) 12
127.0.0.1:6379> get name
"hemmm,maomao"
127.0.0.1:6379> setrange name 0 ilove
(integer) 12
127.0.0.1:6379> getrange name 0 -1
"ilove,maomao"
  • 先get再set

    getset key value

127.0.0.1:6379> getset db redis	 # 如果不存在值 则返回 nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongoDB	# 如果存在值,获取原来的值,并设置新的值
"redis"
127.0.0.1:6379> get db
"mongoDB"
  • 储存一个对象
set user:1 {name:maomao,age:18}	# 设置一个user:1 对象 值为 json字符来保存一个对象!

这里的key是一个巧妙地设计:user:{id}:{filed} , 如此设计在Redis中完全ok

127.0.0.1:6379> mset user:1:name maomao user:1:age 18 user:1:gender nv
OK
127.0.0.1:6379> mget user:1:name user:1:age user:1:gender
1) "maomao"
2) "18"
3) "nv"

string 类型数据操作的注意事项

  • 数据操作不成功的反馈与数据正常操作之间的差异
    1. 表示运行结果是否成功
      (integer) 0 → false 失败
      (integer) 1 → true 成功
    2. 表示运行结果值
      (integer) 3 → 3 3个
      (integer) 1 → 1 1个
  • 数据未获取到
    (nil)等同于null
  • 数据最大存储量
    512MB
  • 数值计算最大范围(java中的long的最大值)
    9223372036854775807

string 类型应用场景

主页高频访问信息显示控制,例如B站up主,主页显示粉丝数与,获赞数,播放数
在这里插入图片描述

  • 在redis中为大V用户设定用户信息,以用户主键和属性值作为key,后台设定定时刷新策略即可
    • user: id:110:fans → 2389000
    • user: id:110:likes → 9476000
    • user: id:110:views → 480000000
  • 在redis中以json格式存储up主信息,定时刷新(也可以使用hash类型)
    eg: user: id:110 →
    {“id”:110,“name”:“王老菊”,“fans”:2389000,“likes”:9476000, “views”:480000000}
127.0.0.1:6379> mset user:id:110:fans 2389000 user:id:110:likes 9476000 user:id:110:views 480000000OK

127.0.0.1:6379> mget user:id:110:fans user:id:110:likes user:id:110:views
1) "2389000"
2) "9476000"
3) "480000000"

127.0.0.1:6379> incr user:id:110:fans
(integer) 2389001
127.0.0.1:6379> incr user:id:110:views
(integer) 480000001
127.0.0.1:6379> incr user:id:110:views
(integer) 480000002
127.0.0.1:6379> mget user:id:110:fans user:id:110:likes user:id:110:views
1) "2389001"
2) "9476000"
3) "480000002"

127.0.0.1:6379> set user:id:120 {id:120,name:wanglaoju,fans:2389000,likes:9476000,views:480000000}OK

127.0.0.1:6379> get user:id:120
"{id:120,name:wanglaoju,fans:2389000,likes:9476000,views:480000000}"

key 的设置约定

  • 数据库中的热点数据key命名惯例
    表名 :主键名 :主键值 : 字段名
    order : id : 294375951 : name
    equip : id : 390472345 : type
    news : id : 202004150 : title

hash

  • 新的存储需求:对一系列存储的数据进行编组,方便管理,典型应用存储对象信息
  • 需要的存储结构:一个存储空间保存多个键值对数据
  • hash类型:底层使用哈希表结构实现数据存储

关于哈希表参考
大佬的文章
在这里插入图片描述

hash存储结构优化

  • 如果field数量较少,存储结构优化为类数组结构
  • 如果field数量较多,存储结构使用HashMap结构

hash 类型数据的基本操作

  • 添加/修改数据

    hset key field value

  • 获取数据

    hget key field
    hgetall key

  • 删除数据

    hdel key field1 [field2]

127.0.0.1:6379> hset user name xiaotian
(integer) 1
127.0.0.1:6379> hset user age 20
(integer) 1
127.0.0.1:6379> hset user gender nan
(integer) 1
127.0.0.1:6379> hgetall user
1) "name"
2) "xiaotian"
3) "age"
4) "20"
5) "gender"
6) "nan"
127.0.0.1:6379> hget user name
"xiaotian"
127.0.0.1:6379> hget user gender
"nan"

127.0.0.1:6379> hdel user gender
(integer) 1
127.0.0.1:6379> hgetall user
1) "name"
2) "xiaotian"
3) "age"
4) "20"
  • 添加/修改多个数据

hmset key field1 value1 field2 value2 …

  • 获取多个数据

hmget key field1 field2 …

  • 获取哈希表中字段的数量

hlen key

  • 获取哈希表中是否存在指定的字段

hexists key field

127.0.0.1:6379> hmget user name age
1) "xiaotian"
2) "20"

127.0.0.1:6379> hmset user name feifei age 22 weight 46
OK
127.0.0.1:6379> hgetall user
1) "name"
2) "feifei"
3) "age"
4) "22"
5) "weight"
6) "46"

127.0.0.1:6379> hlen user	# 获取的是field的数量
(integer) 3

127.0.0.1:6379> hexists user age	# 判断是否存在指定字段
(integer) 1
127.0.0.1:6379> hexists user height
(integer) 0

hash 类型数据扩展操作

  • 获取哈希表中所有的字段名或字段值

hkeys key
hvals key

  • 设置指定字段的数值数据增加指定范围的值

hincrby key field increment
hincrbyfloat key field increment

127.0.0.1:6379> hkeys user		# 所有的字段名
1) "name"
2) "age"
3) "weight"

127.0.0.1:6379> hvals user		# 所有的字段值
1) "feifei"
2) "22"
3) "46"

127.0.0.1:6379> hincrby user age 1
(integer) 23
127.0.0.1:6379> hincrby user weight 10	# 指定增量
(integer) 56
127.0.0.1:6379> hincrby user weight -5	# 指定减量
(integer) 51

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
id:001指用户id
g01 g02 指商品id

127.0.0.1:6379> hmset id:001 g01 100 g02 200
OK
127.0.0.1:6379> hmset id:002 g02 10 g04 8 g05 100
OK
127.0.0.1:6379> hset id:001 g03 5
(integer) 1
127.0.0.1:6379> hgetall id:001
1) "g01"
2) "100"
3) "g02"
4) "200"
5) "g03"
6) "5"
127.0.0.1:6379> hdel id:001 g01
(integer) 1
127.0.0.1:6379> hgetall id:001
1) "g02"
2) "200"
3) "g03"
4) "5"
127.0.0.1:6379> hincrby id:001 g03 3
(integer) 8
127.0.0.1:6379> hgetall id:001
1) "g02"
2) "200"
3) "g03"
4) "8"

但目前这样是不够的,当前仅仅是将数据存储到了redis中,并没有起到加速的作用,商品信息还需要二次查询数据库

解决方案

  • 每条购物车中的商品记录保存成两条field

  • field1专用于保存购买数量

    • 命名格式:商品id:nums
      保存数据:数值
  • field2专用于保存购物车中显示的信息,包含文字描述,图片地址,所属商家信息等

    • 命名格式:商品id:info
      保存数据:json
127.0.0.1:6379> hmset id:003 g01:nums 100 g01:info {diannao,8000}
OK
127.0.0.1:6379> hgetall id:003
1) "g01:nums"
2) "100"
3) "g01:info"
4) "{diannao,8000}"
127.0.0.1:6379> hmset id:004 g01:nums 50 g01:info {diannao,8000}
OK
127.0.0.1:6379> hgetall id:004
1) "g01:nums"
2) "50"
3) "g01:info"
4) "{diannao,8000}"

但这样又出现新的问题 就是g01:info是重复的,因此我们把field2作为独立hash 专门用来保存商品信息的hash

在这里插入图片描述
最后再做一个判断 ,如果field有值,则不添加,没有值就添加

hsetnx key field value

127.0.0.1:6379> hset id:004 g01:nums 200
(integer) 0
127.0.0.1:6379> hget id:004 g01:nums
"200"
127.0.0.1:6379> hsetnx id:004 g01:nums 400
(integer) 0
127.0.0.1:6379> hsetnx id:004 g05:nums 400
(integer) 1
127.0.0.1:6379> hgetall id:004
1) "g01:nums"
2) "200"
3) "g01:info"
4) "{diannao,8000}"
5) "g05:nums"
6) "400"
商家抢购活动

双11活动日,iphone商家对iPhone12,iPad Pro, MacBook Pro的5000元、6000元、10000元商品推出抢购活动,每种商
品抢购上限100台

解决方案

  • 以商家id作为key
  • 将参与抢购的商品id作为field
  • 将参与抢购的商品数量作为对应的value
  • 抢购时使用降值的方式控制产品数量
127.0.0.1:6379> hmset shop:001 iphone12 100 ipad 100 macbook 100
OK
127.0.0.1:6379> hincrby shop:001 iphone12 -1
(integer) 99

127.0.0.1:6379> hincrby shop:001 macbook -20
(integer) 80

127.0.0.1:6379> hgetall shop:001
1) "iphone12"
2) "99"
3) "ipad"
4) "100"
5) "macbook"
6) "80"
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值