Administrator
发布于 2022-12-19 / 66 阅读
0
0

Redis基础知识

前言

读书笔记而已,加点自己的理解。来源:

Redis开发与运维 - 付磊 & 张益军
Redis核心技术与实战

Redis概述

Redis是一种基于键值对(key-value)的NoSQL数据库,键值对中的值可以是由String(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)、Bitmaps(位图)、HyperLogLog、GEO(地理信息定位)等多种数据结构和算法组成。

Redis的特点

  1. 速度快

     读写性能可以达到10万/秒,为什么这么快?
     1、Redis的所有数据都是存放在内存中的,这是主要原因。
     2、C语言开发。面向过程编程性能比较好这个应该没问题。
     3、高效的数据结构,简单动态字符串、双向链表、压缩列表、哈希表、跳表和整数数组
        数据结构不是数据类型,那五种只是数据类型。
     4、单线程架构,IO多路复用。
     
     为什么在Redis里单线程就快?不该是合理的多线程会更好?
     首先,要知道的是:Redis中的单线程指的是网络 IO和键值对读写是由一个线程来完成的,
     但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。
     单线程的优点可以减少上下文切换造成的消耗以及减少并发控制(请求进入队列中(FIFO),排队执行)
     
     IO多路复用:
     这里我直接说个人理解,官方一点的话可以看别人的。
     基本的IO处理中会有很多步骤,比如建立连接;读取客户端请求等
     在这个过程中,如果连接的建立一直不成功或者读取请求但一直没到,这个时候就会发生阻塞(就是在等这个请求)
     在单线程的情况下这是很致命的,所以慢慢就有了IO多路复用(多路指多个请求;复用指一个线程复用)
     
     ps:虽然如此,如果在redis中某条命令执行的时间很长,那也是会造成其他命令阻塞的
     
     实现的效果类比:
     去银行办理业务,刚进去的时候,大堂经理就问办什么请求是吧,拿号然后填表,你那些办业务的基础事项
     都准备好了,你再去柜台。
     一样的:Redis运行的时候,内核会有一种机制,监听进来的请求,当请求连接完成且请求也进来之后,才
     交给Redis去运行,这样就不存在上面的阻塞问题,和办业务相比,这里没有取号,内核会有专门的机制去
     轮询什么请求已经好了,然后交给redis处理。
     
     这里的机制有:select;poll;epoll
     这里不展开说,贴两个看到的文章:
     https://cloud.tencent.com/developer/article/1680732
     https://www.cnblogs.com/flashsun/p/14591563.html
    
  2. 基于键值对

     redis键值对中的值有5种数据结构:字符串、哈希、列表、集合、有序集合。可以应对许多不同场景的
     应用开发。
    
  3. 功能丰富

     1、键过期功能,可以实现缓存。
     2、订阅功能,可以实现消息系统。(但是还是比不上专业的消息中间件的,看需求使用)
     3、Lua脚本功能,利用Lua创造出新的Redis命令。
     4、简单的事务功能,能在一定程度上保证事务特性。(Redis的事务理解和sql的不太一样,先不说)
     5、流水线(Pipeline)功能,客户端能将一批命令一次性传到Redis,减少了网络的开销。
     (这个主要是如果一条命令就建立一次连接,那么成千上百条都这样呢?
     一次两次无所谓,但都这样这开销就大了,所以在某种机制下将命令统一批次传入以减少开销)
    
  4. 持久化

     数据放在内存中是不安全的,所以此Redis提供了两种持久化方式:
     RDB和AOF,可以用两种策略将内存的数据保存到硬盘中。
    
  5. 主从复制

     复制功能;感觉和MySQL的差不多,只是一个是数据库,一个是缓存。复制功能是分布式Redis的基础
    
  6. 高可用和分布式

     这个自己意会吧
    

最后刻下个小问题:

既然Redis快是因为是基于内存的,为什么数据不直接放在内存呢?
nmmd,校招面试的惨痛回忆,那么简单不好好思考

Redis使用场景

1、缓存
这个不用说啦,做缓存可以减小数据库的压力,速度也更快。

2、排行榜系统
Redis提供了列表和有序集合数据结构,合理使用的话也是挺方便的

3、计数器应用
Redis天然支持计数功能而且计数的性能也非常好

4、社交网络
社交网站访问量通常比较大,而且传统的关系型数据不太适合保存诸如点赞;推送等的类型数据

5、消息队列系统
Redis虽然和专业的消息队列比还不够足够强大,但是对于一般的消息队列功能基本可以满足。

Redis的数据类型与命令

全局通用命令

1、查看所有键

key *

2、查看当前数据库中键的总数

dbsize

3、检查键是否存在

exists key

4、删除键

del key [key ...]
支持删除多个键

5、设置键的过期时间

expire key seconds
当超过过期时间后,会自动删除键

6、查看键还有多少秒过期

ttl key
-1表示永不过期,-2表示已过期

7、查看键的数据类型

type key

数据类型对应的数据结构目前我只能复现这点,以后有机会再补充

redis数据类型

String

字符串类型的值实际可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB。

命令

set key value 	设置key值
get key 	查询key值
append key value 	将给定的value追加到原值末尾
strlen key 	获取值的长度
setnx key value 	只有在key不存在的时候,设置key值
incr key 	将key值存储的数字增1,只对数字值操作,如果为空,新增值为1
decr key 	将key值存储的数字减1,只对数字值操作,如果为空,新增值为1
incrby/decrby key <步长> 	将key值存储的数字增减如步长
mset key value key value..	同时设置一个或者多个key-value
mget key key...		同时获取一个或多个value
msetnx key value key value..	同时设置一个或者多个key-value.当且仅当所有给定key都不存在
getrange key <起始位置> <结束位置> 	获取key的起始位置和结束位置的值
setrange key <起始位置> value 	将value的值覆盖起始位置开始
setex key <> value 	设置键值的同时,设置过期时间
getset key value 	用新值换旧值

底层数据结构

1、int:8个字节的长整型。
2、embstr:小于等于44个字节的字符串。
3、raw:大于44个字节的字符串。
Redis会根据当前值的类型和长度决定使用哪一种实现

使用场景

1、缓存
2、计数
3、共享session
一个分布式Web服务将用户的Session信息(例如用户登录信息)保存在各自服务器中,
这样会造成一个问题,出于负载均衡的考虑,分布式服务会将用户的访问均衡到不同服务器上,
用户刷新一次访问可能会发现需要重新登录,这时候进行登录信息共享可以解决
4、限速
比如验证码2分钟内只能获取一次等操作

Hash

须知:hash中的值是一对键值对

常用命令

设置值
hset key field value		(field value 指的就是键值对)

获取值
hget key field

删除hash中的值
hdel key field [field ...]		可以删除多个值

计算hash中值的个数
hlen key

批量设置/获取值
hmget key field [field ...]
hmset key field value [field value ...]

判断hash中值是否存在
hexists key field

获取所有值的键
hkeys key

获取所有的值的值
hvals key

获取所有的键值对
hgetall key

底层数据结构

ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)、同时所有值都小于hash-max-ziplist-value配置(默认64字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。

hashtable(哈希表):当哈希类型无法满足ziplist的条件时,Redis会使用hashtable作为哈希的内部实现

List

一个列表最多可以存储2^32-1个元素,元素有序且可重复。在Redis中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。列表是一种比较灵活的数据结构,它可以充当栈和队列的角色。

常用命令

从左或者右插入一个或者多个值(头插与尾插)
lpush/rpush key value value...

向某个元素前或者后插入元素
linsert key before|after pivot value

获取指定范围内的元素列表
lrange key start end
lrange key 0 -1 获取所有值

获取指定下标的值
lindex key index

获取列表长度
llen key

从左或者右弹出一个或者多个值(值在键在,值都没,键都没)
lpop/rpop key

删除指定元素
lrem key count value
count小于0,从右边开始删count的绝对值个;大于0,从左边开始删;等于0,删除所有

修改指定索引下标的值
lset key index newValue

image-1671519423237

底层数据结构

quicklist,它是以一个ziplist为节点的linkedlist(双向链表),它结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现

使用场景

1、消息队列
2、文章列表

lpush+lpop=Stack(栈)
lpush+rpop=Queue(队列)
lpsh+ltrim=Capped Collection(有限集合)
lpush+brpop=Message Queue(消息队列)

Set

Set和列表类型的差别是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。

一个集合最多可以存储2^32-1个元素。Redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集。

常用命令

添加元素
sadd key element [element ...]	返回结果为添加成功的元素个数

删除元素
srem key element [element ...]	返回结果为删除成功的元素个数

计算元素个数
scard key

判断元素是否在集合中
sismember key element

随机返回指定的元素个数,不写默认为1
srandmember key [count]

随机弹出元素
spop key [count]

获取所有元素
smembers key

求多个集合交集
sinter key [key ...]

求多个集合并集
suinon key [key ...]

求多个集合差集
sdiff key [key ...]	(第一个key有别的没有的)

底层数据结构

hashtable(哈希表),书里说的应该还有个整数数组,我没复现出来

使用场景

标签(例如CSDN那些标签,你可以看到和你选择了一样标签或者差不多的人)

ZSet

ZSet保留了集合不能有重复成员的特性,但不同的是,有序集合中的元素可以排序。但是它和列表使用索引下标作为排序依据不同的是,它给每个元素设置一个分数(score)作为排序的依据。

有序集合中的元素不能重复,但是score可以重复,就和一个班里的同学学号不能重复,但是考试成绩可以相同。

常用命令

添加成员
zadd key score member [score member ...]		返回结果代表添加成功的个数

计算成员个数
zcard key

计算某个成员的分数
zscore key member

计算成员排名
zrank key member
zrevrank key member
zrank是从分数从低到高返回排名,zrevrank反之。

删除成员
zrem key member [member ...]	返回结果为删除成功的个数

增加成员分数
zincrby key increment member

好多好繁琐,不想记这些了
贴一篇博客
https://blog.csdn.net/weixin_47872288/article/details/118410080

底层数据结构

ziplist(压缩列表):当有序集合的元素个数小于zset-max-ziplistentries配置(默认128个),同时每个元素的值都小于zset-max-ziplistvalue配置(默认64字节)时,Redis会用ziplist来作为有序集合的内部实现,ziplist可以有效减少内存的使用。

skiplist(跳表):当ziplist条件不满足时,有序集合会使用skiplist作为内部实现。跳表和二分查找很像,只不过是按索引跳而已。

image-1671521217754

使用场景

排行榜系统

Pipeline

Redis客户端执行一条命令分为如下四个过程:

发送命令
命令排队
命令执行
返回结果

其中发送命令时间和返回结果时间加起来称为Round Trip Time(RTT,往返时间)

Redis提供了批量操作命令(例如mget、mset等),有效地节约RTT。但大部分命令是不支持批量操作的,例如要执行n次hgetall命令。简单点说就是来一条命令就送一次和命令攒着然后一起送的区别。

Pipeline虽然可以模拟出批量操作的效果,但是与原生批量命令还是有区别的:

原生批量命令是原子的,Pipeline是非原子的。
原生批量命令是一个命令对应多个key,Pipeline支持多个命令。
原生批量命令是Redis服务端支持实现的,而Pipeline需要服务端和客户端的共同实现。

新数据类型

Bitmaps

Bitmaps本身不是一种数据结构,实际上就是字符串。但是它可以对字符串的位进行操作。可以把Bitmaps想象成一个以位为单位的数组,数组的每个单元只能存储0和1,数组的下标在Bitmaps中叫做偏移量。合理使用操作位可以有效地提高内存使用率和开发使用率

image-1671523242559

常用命令

设置值
setbit key offset value

获取值
gitbit key offset

获取指定范围内数值为1的个数
bitcount [start][end]

复合操作,它可以做多个Bitmaps的and(交集)、or(并集)、not(非)、xor(异或)操作并将结果保存在destkey中
bitop and(or/not/xor)destkey key

底层实现以及使用场景

前面说bitmaps只是基于redis的字符串类型的.。而一个字符串类型最多存储512M。所以只要将512M转换为bit就能知道bitmaps的最大长度。

8 * 1024 * 1024 * 512  =  2^32

至于使用场景:书中给出的例子是用户活跃问题即有没有登录。可以延伸一下,bitmaps的值只有0和1,那么就是对于只有两种可能的场景都可以尝试一下。

HyperLogLog

用于统计网页中页面访问量等(估计涉及统计的都可以试试)
其只会根据输入元素来计算基数,而不会储存输入元素本身,不能像集合那样,返回输入的各个元素
基数估计是存在误差的(可接受的范围内)快速计算(不重复元素的结算)

常用命令

添加指定的元素到hyperloglog中
pfadd key element

计算key的近似基数
pfcount key 

一个或多个key合并后的结果存在另一个key
pfmerge destkey sourcekey sourcekey

Geographic

提供经纬度设置,查询范围,距离查询等

发布订阅

发布消息

publish channel message

订阅消息

subscribe channel [channel ...]
订阅者可以订阅一个或多个频道

取消订阅

unsubscribe [channel [channel ...]]

注意事项:

客户端在执行订阅命令之后进入了订阅状态,只能接收subscribe、
psubscribe、unsubscribe、punsubscribe的四个命令。

新开启的订阅客户端,无法收到该频道之前的消息,因为Redis不会对发布的消息进行持久化。

评论