关于redis那些你不得不知道的事儿(一)

前言

redis(全称:Remote Dictionary Server,即远程字典服务),近年来可以说是火遍了大江南北,它能干的事儿是真不少。最基本的,做为系统缓存我们可以使用它;我们还可以使用它维护一个高效的消息队列。然而Redis能够实现的功能远远不止于此,小小的键值对被玩出了无数的花样(当然这和Redis本身提供的丰富功能也脱不了关系)。

如果你已经迫不及待的想要使用Redis了,那么,这里有一些关于Redis你不得不知道的事儿

本篇不着重阐述原理,而是从最基本的API使用上进行较为详细的介绍,方便快速代入使用场景

为什么要使用Redis

  • 最为重要的一点就是Redis速度够快,能够明显的提高系统性能。
  • Redis能够非常简单的实现特定的业务场景,比如,排行榜,好友关系等
  • 能够实现简单的消息队列

速度

Redis速度快的最主要的原因是它是“完全”基于内存的数据库(当然也提供了持久化),这使得 它避免了使用写磁盘的格式对内存数据接口编码的开销(磁盘的数据依旧是要读到内存中才能够被使用,这里面存在着很大的开销),故而直接从内存读取数据是要比读磁盘要快非常多。

另外,其他次要一些原因还有:

  1. Redis是由C语言实现的(语言级别速度的保证(´・ω・`) )
  2. 其线程模型为单线程,避免了线程切换的开销
  3. 网络方面使用epoll解决了高并发问题

根据官方说法,Redis最高能够达到 10w OPS(operation per second)!

高效、丰富的数据结构

除了最基本的键值对,Redis提供了丰富的数据结构,包括

  1. 字符串(String)
  2. 哈希(Hash Tables)
  3. 列表(Linked List)
  4. 集合(Sets)
  5. 有序集合(Sorted Set)

其他还有一些基于上述结构实现的数据结构

  1. 位图 (Bit Map)
  2. HyperLogLog(使用超小内存(12k)实现唯一值,本质是字符串)
  3. GEO (地理信息定位)

持久化

虽然内存只要一断电就嗝屁,但是Redis也是提供持久化功能的,在内存上操作保证速度的同时,也提供了RDB和AOF两种方式进行持久化,将数据的更新异步的保存到磁盘上,无需担心数据的丢失了:)

其他

丰富的功能

  1. Redis支持多种编程语言
  2. 提供发布订阅,事务功能
  3. 支持编写Lua脚本
  4. 支持pipline
  5. 支持主从复制,分布式(Redis-Cluster),高可用(Redis-Sentinel)

简单

使用简约但不简单!

Redis API

下面通过介绍Redis API来多方面的展现Redis的强大功能

字符串

字符串是redis最基本的结构,很多redis的高级数据结构底层都是由字符串实现的,下面来看看redis字符串的API。

字符串的使用场景很多,最为基本的是作为缓存,另外还有计数器(字符串提供的自增),分布式锁等等。

  1. get/del操作 O(1)
# 获取key对应的value
get key
# 删除key 
del key
  1. set操作 O(1)
# 设置一个值(无论是否存在)
set key value
# key不存在时才做设置
setnx key value
# key存在时才设置
set key value xx
  1. mget O(n)
# 批量获取key值
mget key1 key2 key3

对于redis来说,所有的key都是字符串,value则类型多样

对于value字符串来说,value可以是字符串,也可以是数字和二进制类型,这两种类型是在redis内部自动转换的,之后将要提到的位图(bitmp)即是使用字符串的二进制形式所实现的。

单个字符串value的大小小于等于512MB,通常一个value限制在100k以内是比较合适的。

对于批量获取操作mget来说,如果有需要多次取值的操作,不妨使用mget进行批量获取,相比于多次get更加的高效。

  1. 整型操作
# key自增1,如果key不存在,自增后get(key)= 1
incr key
# 同上相反 ,自减
decr key
# key自增k,如果key不存在,自增后get(key)= k
incrby key k
# key 自减k,其他同incrby相反 
decrby key k

前面说的计数器,就可以用上面的自增API进行实现,非常的方便:)

  1. 其他一些操作
#  set key newvalue并返回旧的value
getset key newvalue
# 将value追加到旧的value
append key value
# 返回字符串长度(注意中文)
strlen key

# 增加key对应的值
incrbyfloat key value
# 获取字符串指定下标所有的值
getrange key start end
# 设置,其他同上
setrange key index value

Hash

redis的hash是一个mapMap的结构,其结构长这样
在这里插入图片描述
hash的key依旧是字符串,其value分为两个部分:fieldval,其中field的值不能相同,val值可以相同(如果理解哈希概念的话,这个地方应该不会陌生),你可以把它理解为编程语言中的哈希表,字典等数据结构。其实redis本身就是一个大字典,hash相当于一个嵌套字典了

举个🌰, 我们可以用hash来存储一个用户的相关信息,像这样
在这里插入图片描述
下面我们来看看redis hash结构的相关API

# 获取hash key对应的field的value
hget key field
# 设置hash key对应的field的value
hset key field value
# 删除hash key对应的field的value
hdel key field
# 判断hash key是否有field
hexists key field
# 获取hash key field的数量
hlen key
# 批量获取hash key的一批field对应的值
hmget key field1 field2 ... fieldN
# 批量设置hash key的一批field value
hmset key field1 value field2 value2...fieldN valueN
# 返回hash key 对应所有的field 和 value(谨慎使用), 数据量大时会阻塞
hgetall key
# 返回hash key对应所有field的value
hvals key
# 返回hash key对应的所有field
hkeys key
# 设置hash key对应的field的value(如field已经存在则失败)
hsetnx key filed value
# hash key对应的field的value自增inCounter
hincrby key field intCounter
# hincrby浮点数版
hincrbyfloat key field floatCounter

使用hash保存复杂信息

涉及到复杂信息更新时,我们通常可以有限考虑使用redis的hash结构,从上面的用户信息例子可以看出,hash结构存储往往更加直观,并且能够节省内存(这是由其数据结构决定的),最关键的是,它可以部分更新。同样的实现用户信息存储则必须全量更新才行。

但是需要注意的是,我们可以为redis数据设置过期时间,而hash结构的过期精度只支持到key级别,不能为field单独设置过期时间

list

直观一点,就是一个列表结构,像这样
在这里插入图片描述
你可以直接把它当成编程语言中的一个列表结构(尽管内部实现可能不一致):有序、内部元素可重复,可以在列表左边和右边插入和弹出元素。
看看redis list的API

# 从列表右端插入值(1-N个)
# e.g. rpush listkey c b a: 结果  c—>b—>a
rpush key value1 value2... valueN
# 和rpush相反
lpush
# 从列表左侧弹出一个item
lpop key
# 与lpop相反
rpop
# 根据count值,从列表中删除所有value相等的项
# count > 0,从左到右,删除最多count个value相等的项
lrem key count value
# 在list指定的值前 | 后插入newValue
linsert key before | after value newValue
# 按照索引范围修剪列表(start end为需要保留的范围)
ltrim key start end
# 获取列表指定索引范围所有item(包含end)
lrange key start end
# 获取列表指定索引的item
lindex key
# 获取列表长度
llen key
# 设置列表指定索引值为newValue
lset key index newValue
# lpop阻塞版本,timeout是阻塞超时时间,timeout=0为永远不阻塞(对生产者消费者模型,消息队列实现有帮助)
blpop key timeout
# 与blpop相反
brpop key timeout

通过使用redis的list API,我们可以非常方便的实现一些功能,例如

list的使用场景(栈、队列、消息队列…)

  • 使用 lpush + lpop 可以实现一个简单的栈(后进先出)
  • 使用 lpush + rpop 可以实现一个简单的队列(先进先出)
  • 使用 lpush + ltrim 可以实现一个固定数量的列表
  • 使用 lpush + brpop 可以实现一个消息队列

set 集合

我们直接类比到编程语言的集合数据结构(比如python的set),它长这样
在这里插入图片描述
集合内部的元素是无序的且不重复的,多个集合直接我们可以进行一些类似并集,交集的操作,话不多说,直接来看看redis提供的set API

=====================集合内操作===========================
# 添加集合元素
sadd key value1 value2
# 删除集合元素
srem key value1 value2
# 计算集合大小
scard key
# 判断value是否在集合中(返回1表示存在)
sismember key value
# 从集合中随机挑count个元素
srandmember key count
# 从集合中随机弹出一个元素
spop key
# 获取集合所有元素(小心使用, 可能会阻塞) 
smembers key
=====================集合间操作===========================
# 取value交集
sinter key1 key2
# 取所有不同value
sdiff  key1 key2
# 取所有value
sunion key1 key2
# 将上述集合间操作结果保存至destkey
sinter|sdiff|sunion + store destkey

set的使用场景(抽奖,点赞,共同关注…)

这是微博上随便截取的一个抽奖微博截图,如果让我们用redis来实现一个抽奖,那么,使用set就是一个非常好的选择。
在这里插入图片描述
假设我们的有一个key叫users,他对应的value是一个set,里面存储了所有用户的id,那么,我们就可以使用spopsrandmember来很方便的实现抽奖功能。

其他的业务场景比如知乎文章的赞同,反对,我们可以使用集合的API来实现(对于每一篇文章,当用户点赞时,调用sadd将用户id推进集合当中)

还有类似于共同关注的功能,我们也可以很方便的使用集合的取交集和并集的操作来完成。

zset(有序集合)

zset同样也是一个集合,但是与上节的set相比,zset是有序的,redis中的zset结构长这样:
在这里插入图片描述
如果说set的value是一个个游离的单独元素的话,那么zset就是给这些元素打上了一个序号(score)

老规矩,先来看看API

# 添加socre和value(可以是多对)
zadd key score1 value1 score2 value2
# 删除元素(可以是多个)
zrem key value1 value2
# 返回元素的score
zscore key value
# 增加或减少元素的socre
zincrby key increSocre value
# 返回元素的总个数
zcard key
# 返回value的排名(类似索引id)
zrank key value
# 返回范围内的 value score ,-1代表最后一个
zrange key start end [WITHSCORES]
# 返回指定分数范围内的升序元素[分值]
zrangebyscore key minScore maxScore [WITHSCORES]
# 获取有序集合内在指定分数范围内的个数
zcount key minScore maxScore
# 删除指定排名内的升序元素
zremrangebyrank key start end
# 删除指定分数内的升序元素
zremrangebyscore key minScore maxScore

使用zset实现一个排行榜

在这里插入图片描述
这是一个微博的热搜截图,在这里热搜数据相当于一个zset,图上的4685796, 4384536,4368888…就是zset的score(搜索量),具体的信息就是一个个value,zset内部会根据socore进行排序, 这里的排名跟就是根据这个socre来的。你看,通过对zset API的灵活运用,一个基本的排名功能是不是就出来了呢:)

后记

本篇只是对redis中几种典型的数据结构进行了一番介绍,在下一章中,我们还将进行一些redis高级特性的讨论(比如redis的慢查询管道pipline发布订阅位图HyperLoglog以及GEO等等),这些特性会给我们在日常开发当中提供许多的便利~

在这里插入图片描述

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值