Redis 5种基本数据类型 + 3种特殊数据类型

Redis官网:http://doc.redisfans.com/


Redis键(key)基础命令

key *查看当前库中所有的key
exists key判断某个key是否存在
type key查看你的key是什么类型
del key删除指定的key数据
unlink key据value删除非阻塞删除,仅仅将keys从keyspace元数据中删除,真正的删除会在后续异步中操作
expire key 10为指定的key设置有效期10秒
ttl key查看指定的key还有多少秒过期,-1:表示永不过期,-2:表示已过期
select dbindex换数据库【0-15】,默认为0
 dbsize查看当前数据库key的数量
flushdb清空当前库
flushall通杀全部库

Redis 中的 8 种常用数据类型

  • 5 种基础数据类型:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。

  • 3 种特殊数据类型:HyperLogLog(基数统计)、Bitmap(位图)、Geospatial(地理位置)。

5 种基础数据类型

数据类型说明
String(字符串)一种二进制安全的数据类型,可以用来存储任何类型的数据比如字符串、整数、浮点数、图片(图片的 base64 编码或者解码或者图片的路径)、序列化后的对象。
List(列表)Redis 的 List 的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
Set(集合)无序集合,集合中的元素没有先后顺序但都唯一,有点类似于 Java 中的 HashSet 。
Zset(有序集合)和 Set 相比,Sorted Set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,还可以通过 score 的范围来获取元素的列表。有点像是 Java 中 HashMap 和 TreeSet 的结合体。
Hash(散列)一个 String 类型的 field-value(键值对) 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接修改这个对象中的某些字段的值。

String(字符串)

        String是Redis最基本的类型,可以理解成与Memcached一模一样的类型,一个key对应一个value。String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象。String类型是Redis最基本的数据类型,一个Redis中字符串value最多可以是512M。

  • 数据结构
        String的数据结构为简单动态字符串( Simple Dynamic String ,缩写 SDS )。是可以修改的字符串,内部结构上类似于Java ArrayList ,采用分配冗余空间的方式来减少内存的频繁分配。

        如图所示,内部为当前字符串实际分配的空间capacity 一般要高于实际字符串长度 len 。当字符串长度小于1M 时,扩容都是加倍现有的空间,如果超过 1M ,扩容时一次会多扩容 1M 的空间。要注意的是字符串最大长度为512M
  •  常用命令
set <key> <value>
添加键值对
get <key>
获取值
append <key> <value>
追价值
strlen <key>
获取值的长度
setnx <key> <value>
key不存在时,设置key的值
incr <key>
原子递增1
decr <key>
原子递减1
incrby/decrby <key> <步长>
递增或者递减指定的数字
mset <key1> <value1> <key2> <value2> ...
同时设置多个key-value
mget <key1> <key2> ...
获取多个key对应的值
msetnx <key1> <value1> <key2> <value2> ...
当多个key都不存在时,则设置成功
getrange key start end
获取值的范围,类似java中的substring
setrange <key> <起始位置> <value>
覆盖指定位置的值
setex <key> <过期时间(秒)> <value>
设置键值&过期时间(秒)
getset <key> <value>
以新换旧,设置新值同时返回旧值
  • 应用场景

1、需要存储常规数据的场景

  • 举例:缓存 Session、Token、图片地址、序列化后的对象(相比较于 Hash 存储更节省内存)。
  • 相关命令:SET、GET。

2、需要计数的场景

  • 举例:用户单位时间的请求数(简单限流可以用到)、页面单位时间的访问数。
  • 相关命令:SET、GET、INCR、DECR。

3、分布式锁

利用SETNX key value命令可以实现一个最简易的分布式锁(存在一些缺陷,通常不建议这样实现分布式锁)。

List(列表)

        单键多值
        redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。它的底层实际上是使用双向链表实现的(如下图),对两端的操作性能很高,通过索引下标操作中间节点性能会较差。

  • 数据结构
        List的数据结构为快速链表 quickList
        首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也就是压缩列表。它将所有的元素紧挨着一起存储,分配的是一块连续的内存。当就比较多的时候才会改成quickList。因为普通的链表需要的附加指针空间太大,会比较浪费空间,比如这个列表里存储的只是int 类型的书,结构上还需要2 个额外的指针 prev next  

        redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用,这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。

  • 常用命令
lpush/rpush <key1> <value1> <key2> <value2> ...
从左边或者右边插入一个或多个值
lrange <key> <star> <stop>
从列表左边获取指定范围内的值
lpop/rpop <key> <count>
从左边或者右边弹出多个元素
rpoplpush source destination
从一个列表右边弹出一个元素放到另外一个列表中
lindex key index
获取指定索引位置的元素(从左到右)
llen key
获得列表长度
linsert <key> before|after <value> <newvalue>
在某个值的前或者后面插入一个值
LREM key count value
删除指定数量的某个元素
lset <key> <index> <value>
替换指定位置的值
  •  应用场景

1、信息流展示

  • 举例:最新文章、最新动态。
  • 相关命令:LPUSH、LRANGE。

2、消息队列

List 可以用来做消息队列,只是功能过于简单且存在很多缺陷,不建议这样做。相对来说,Redis 5.0 新增加的一个数据结构 Stream 更适合做消息队列一些,只是功能依然非常简陋。和专业的消息队列相比,还是有很多欠缺的地方比如消息丢失和堆积问题不好解决。

Set(集合)

        redis set对外提供的功与 list 类似,是一个列表的功能,特殊之处在于 set 是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择。
        redis的 set string 类型的 无序集合 ,他的底层实际是一个 value null hash 表,收益添加,删除,查找复杂度都是O(1) 。一个算法,如果时间复杂度是O(1) ,那么随着数据的增加,查找数据的时间不变,也就是不管数据多少,查找时间都是一样的。
  • 数据结构

        set数据结构是字典,字典是用hash表实现的。

        Java中的HashSet的内部实现使用HashMap,只不过所有的value都指向同一个对象。

        Redis的set结构也是一样的,它的内部也使用hash结构,所有的value都指向同一个内部值。

  • 常用命令
sadd <key> <value1> <value2> ...
添加一个或多个元素
smembers <key>
取出所有元素
sismember <key> <value>
判断集合中是否有某个值
scard <key>
返回集合中元素的个数
srem key member [member ...]
删除多个元素
spop <key> <count>
随机弹出多个值
srandmember <key> <count>
随机获取多个元素,不会从集合中删除
smove <source> <destination> member
将某个原创从一个集合移动到另一个集合
sinter key [key ...]
取多个集合的交集
sinterstore destination key [key ...]
将多个集合的交集放到一个新的集合中
sunion key [key ...]
取多个集合的并集,自动去重
sinterstore destination key [key ...]
将多个集合的并集放到一个新的集合中
SDIFF key [key ...]
取多个集合的差集
sdiffstore destination key [key ...]
将多个集合的差集放到一个新的集合中
  • 应用场景

1、需要存放的数据不能重复的场景

  • 举例:网站 UV 统计(数据量巨大的场景还是 HyperLogLog更适合一些)、文章点赞、动态点赞等场景。
  • 相关命令:SCARD(获取集合数量) 。

2、需要获取多个数据源交集、并集和差集的场景

  • 举例:共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐(差集)、音乐推荐(差集)、订阅号推荐(差集+交集) 等场景。
  • 相关命令:SINTER(交集)、SINTERSTORE (交集)、SUNION (并集)、SUNIONSTORE(并集)、SDIFF(差集)、SDIFFSTORE (差集)。

3、需要随机获取数据源中的元素的场景、

  • 举例:抽奖系统、随机点名等场景。
  • 相关命令:SPOP(随机获取集合中的元素并移除,适合不允许重复中奖的场景)、SRANDMEMBER(随机获取集合中的元素,适合允许重复中奖的场景)。

ZSet(有序集合)

        redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。 不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。

        集合的成员是唯一的,但是评分是可以重复的。因为元素是有序的,所以你可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。访问有序集合中的中间元素也是非常快的,因为你能够使用有序集合作为一个没有重复成员你的智能列表。

  • 数据结构

        SortedSetzset)是redis提供的一个非常特别的数据结构,内部使用到了2种数据结构。

        1、 hash
        类似于java 中的 Map<String,score> key 为集合中的元素, value 为元素对应的 score ,可以用来快速定位元素定义的score ,时间复杂度为 O(1)
        2、跳表
        跳表(skiplist )是一个非常优秀的数据结构,实现简单,插入、删除、查找的复杂度均为 O(logN) 。类似java 中的 ConcurrentSkipListSet ,根据 score 的值排序后生成的一个跳表,可以快速按照位置的顺序或者score 的顺序查询元素。
这里我们来看一下跳表的原理,首先从考虑一个有序表开始:

        从该有序表中搜索元素 < 23, 43, 59 > ,需要比较的次数分别为 < 2, 4, 6 > ,总共比较的次数为 2 + 4 + 6 = 12 次。有没有优化的算法吗 ? 链表是有序的,但不能使用二分查找。类似二叉搜索树,我们把一些节点提取出来,作为索引。得到如下结构:

        这里我们把 < 14, 34, 50, 72 > 提取出来作为一级索引,这样搜索的时候就可以减少比较次数了。我们还可以再从一级索引提取一些元素出来,作为二级索引,变成如下结构:

        这里元素不多,体现不出优势,如果元素足够多,这种索引结构就能体现出优势来了。
  • 应用场景 

1、需要随机获取数据源中的元素根据某个权重进行排序的场景

举例:各种排行榜比如直播间送礼物的排行榜、朋友圈的微信步数排行榜、王者荣耀中的段位排行榜、话题热度排行榜等等。
相关命令:ZRANGE (从小到大排序)、 ZREVRANGE (从大到小排序)、ZREVRANK (指定元素排名)。

2、需要存储的数据有优先级或者重要程度的场景 比如优先级任务队列。

举例:优先级任务队列。
相关命令:ZRANGE (从小到大排序)、 ZREVRANGE (从大到小排序)、ZREVRANK (指定元素排名)。

Hash(散列)

        Redis hash是一个键值对集合。 Redis hash是一个string类型的fieldvalue的映射表,hash特别适合用于存储对象。 类似于java里面的Map<String,Object>

  • 数据结构

        Hash类型对应的数据结构是2中:ziplist(压缩列表),hashtable(哈希表)。 当field-value长度较短个数较少时,使用ziplist,否则使用hashtable

  • 常用命令
hset key field value [field value ...]
设置多个 field 的值
hget key field
获取指定 filed 的值
hgetall key
返回 hash 表所有的域和值
hmset key field value [field value ...]
hset 类似(已弃用)
hexists key field
判断给定的 field 是否存在, 1 :存在, 0 :不存在
hkeys key
列出所有的 filed
hvals key
列出所有的 value
HLEN key
返回 filed 的数量
hincrby key field increment
filed 的值加上指定的增量
hsetnx key field value
filed 不存在的时候,设置 filed 的值
  •  应用场景

 1、对象数据存储场景:

  • 举例:用户信息、商品信息、文章信息、购物车信息。
  • 相关命令:HSET (设置单个字段的值)、HMSET(设置多个字段的值)、HGET(获取单个字段的值)、HMGET(获取多个字段的值)。

3 种特殊数据类型

数据类型说明
HyperLogLog(基数统计)你可以将 Bitmap 看作是一个存储二进制数字(0 和 1)的数组,数组中每个元素的下标叫做 offset(偏移量)。通过 Bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 Bitmap 本身会极大的节省储存空间。
Bitmap(位图)Redis 提供的 HyperLogLog 占用空间非常非常小,只需要 12k 的空间就能存储接近2^64个不同元素。不过,HyperLogLog 的计数结果并不是一个精确值,存在一定的误差(标准误差为 0.81% )。
Geospatial(地理位置)Geospatial index(地理空间索引,简称 GEO) 主要用于存储地理位置信息,基于 Sorted Set 实现。

HyperLogLog(基数统计)

 在工作当中,我们经常会遇到与统计相关的功能需求,比如统计网站 PVPageView 页面访问量),可以使用 Redis incrincrby 轻松实现。但像 UVUniqueVisitor 独立访客)、独立 IP 数、搜索记录数等需要去重和计数的问题如何解决?这种求集合中不重复元素个数的问题称为基数问题。

解决基数问题有很多种方案:

数据存储在 MySQL 表中,使用 distinct count 计算不重复个数。

使用 Redis 提供的 hashsetbitmaps 等数据结构来处理。

以上的方案结果精确,但随着数据不断增加,导致占用空间越来越大,对于非常大的数据集是不切实际的。能否能够降低一定的精度来平衡存储空间?Redis 推出了 HyperLogLog

Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是:在输入元素的数量或者体积 非常非常大时,计算基数所需的空间总是固定的、并且是很小的。

在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以HyperLogLog 不能像集合那样,返回输入的各个元素。

什么是基数?比如数据集 {1, 3, 5, 7, 5, 7, 8},那么这个数据集的基数集为 {1, 3, 5 ,7, 8},基数 (不重复元素) 5。基数估计就是在误差可接受的范围内,快速计算基数。

  • 常用命令
  pfadd key element [element ...]
添加多个元素
pfcount key1 key2 ...
获取多个HLL合并后元素的个数
pfmerge destkey sourcekey [sourcekey ...]
将多个HLL合并后元素放入另外一个HLL  
  •  应用场景

1、数量量巨大(百万、千万级别以上)的计数场景

  • 举例:热门网站每日/每周/每月访问 ip 数统计、热门帖子 uv 统计、
  • 相关命令:PFADD、PFCOUNT 。

Bitmap (位图)

        现代计算机使用二进制(位)作为信息的基本单位,1个字节等于8位,例如“abc”字符串是有3个字节组成,但实际在计算机内存储时将其使用二进制表示,“abc”分别对应的ASCII码是:979899,对应的二进制分别是011000010110001001100011,如下图:

        合理地使用位操作能够有效地提高内存使用率和开发效率。

        Redis提供了Bitmaps这个“数据类型可以实现对位的操作:

  • Bitmaps本身不是一种数据类型, 实际上它就是字符串(key-value) , 但是它可以对字符串的位进行操作,字符串中每个字符对应1个字节,也就是8位,一个字符可以存储8bit位信息。
  • Bitmaps单独提供了一套命令, 所以在Redis中使用Bitmaps和使用字符串的方法不太相同。 可以把Bitmaps想象成一个以位为单位的数组, 数组的每个单元只能存储01, 数组的下标在 Bitmaps中叫做偏移量。
  • bitmapsset比较  

        假设网站有 1 亿用户, 每天独立访问的用户有 5 千万, 如果每天用集合类型和 Bitmaps 分别存储活跃用户可以得到表:

set 和 Bitmaps 存储一天活跃用户对比
数据类型
每个用户 id 占用空间
需要存储的用户量
全部内存量
set 集合
64
50000000
64 * 50000000 = 400MB
Bitmaps
1
100000000
1 * 100000000 = 12.5MB
        很明显, 这种情况下使用 Bitmaps 能节省很多的内存空间, 尤其是随着时间推移节省的内存还是非常可观的。
set 和 Bitmaps 存储独立用户空间对比
数据类型
一天一月一年
set 集合
400MB
12GB
144GB
Bitmaps
12.5MB
375MB
4.5GB

        但 Bitmaps 并不是万金油, 假如该网站每天的独立访问用户很少, 例如只有 10 万(大量的僵尸用户) , 那么两者的对比如下表所示, 很显然, 这时候使用 Bitmaps 就不太合适了, 因为基本上大部分位都 是 0

数据类型
每个 userid 占用空间
需要存储的用户量
全部内存量
set 集合
64
100000
64 * 100000 = 800KB
Bitmaps
1
100000000
1 * 100000000 = 12.5MB
  •  常用命令
SETBIT key offset value
设置某个偏移量的值( 0 1
GETBIT key offset
获取某个偏移位的值
BITCOUNT key [start] [end]
统计 bit 位都为 1 的数量
BITOP operation destkey key [key ...]
对一个多个 bitmaps 执行位操作
  • 应用场景

1、需要保存状态信息(0/1 即可表示)的场景

  • 举例:用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。
  • 相关命令:SETBIT、GETBIT、BITCOUNT、BITOP。

Geospatial (地理位置)

        Reids3.2 中增加了对GEO类型的支持,GEOGeographic),地理信息的缩写。该类型,就是元素的 2 维坐标,在地图上就是经纬度,redis基于该类型,提供了经纬度设置、查询、范围查询、距离查询,经纬度Hash等常见操作。

  • 常用命令
geoadd key longitude latitude member [longitude latitude member ...]
添加多个位置的经纬度
geopos key member [member ...]
获取多个位置的坐标值
geodist key member1 member2 [m|km|ft|mi]
获取两个位置的直线距离
georadius key longitude latitude radius m|km|ft|mi
以给定的经纬度为中心,找出某一半径内的元素
  •  应用场景

1、需要管理使用地理空间数据的场景

  • 举例:附近的人。
  • 相关命令: GEOADD、GEORADIUS、GEORADIUSBYMEMBER 。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值