Redis

目录

1.单线程架构

[1.1 引出单线程模型](#1.1 引出单线程模型)

[1.2 为什么单线程还能这样快](#1.2 为什么单线程还能这样快)

2.String字符串

[2.1 常见命令](#2.1 常见命令)

[2.1.1 SET](#2.1.1 SET)

[2.1.2 GET](#2.1.2 GET)

[2.1.3 MGET](#2.1.3 MGET)

[2.1.4 MSET](#2.1.4 MSET)

[2.2 计数命令](#2.2 计数命令)

[2.2.1 INCR](#2.2.1 INCR)

[2.2.2 INCRBY](#2.2.2 INCRBY)

[2.2.3 DECR](#2.2.3 DECR)

[2.2.4 DECRBY](#2.2.4 DECRBY)

[2.2.5 INCRBYFLOAT](#2.2.5 INCRBYFLOAT)

[2.3 其他命令](#2.3 其他命令)

[2.3.1 APPEND](#2.3.1 APPEND)

[2.3.2 GETRANGE](#2.3.2 GETRANGE)

[2.3.3 SETRANGE](#2.3.3 SETRANGE)

[2.3.4 STRLEN](#2.3.4 STRLEN)

[2.4 内部编码](#2.4 内部编码)

[2.5 典型使用场景](#2.5 典型使用场景)

[2.5.1 缓存功能](#2.5.1 缓存功能)

[2.5.2 计数功能](#2.5.2 计数功能)

[2.5.3 共享会话](#2.5.3 共享会话)

[2.5.4 手机验证码](#2.5.4 手机验证码)


1.单线程架构

Redis使用了单线程架构来实现高性能内存数据库服务,Redis只使用一个线程来处理所有命令请求

1.1 引出单线程模型

例如开启了两个redis-cli客户端同时执行命令

客户端1针对counter进行incr操作,客户端2也针对counter进行incr操作

我们都知道在多线程中,两个线程同时针对一个变量进行自增操作时,表面上看是自增两次,实际上可能自增了一次,就会有线程安全的问题。当前这两个客户端并发的发起上述请求,会不会有线程安全的问题呢?

实际上是不会的,由于redis服务器是单线程模型,它保证当前收到多个请求时串行执行的,当多个请求同时到达redis服务器,需要在队列中进行排队,再等redis服务器一个一个的取出里面的命令再执行,微观上讲,redis服务器都是顺序执行多个命令的(就好比去食堂一个窗口打饭,所有人都需要排队一个一个打),其中redis能够使用单线程模型很好的工作,在于redis的核心业务逻辑都是比较短比较快的,不太消耗cpu资源

redis的弊端:如果某个操作占用时间很长,就会阻塞其他命令的执行

1.2 为什么单线程还能这样快

Redis的效率高和速度快是参照mysql这种关系型数据库,和关系型数据库作比较

1)Redis访问的是内存,数据库则是访问的硬盘。Redis将所有数据存储再内存中,内存的响应速度是远远大于硬盘的,这是Redis达到每秒万级别访问的重要基础

2)Redis核心功能比数据库的核心功能更简单,数据库对数据的增删改查都有复杂的功能支持,因此需要花费更多的开销,而redis提供的功能相比mysql来说少了很多

3)单线程模型,避免了一些不必要的线程竞争开销

4)处理网络IO的时候,使用了epoll这样的IO多路复用机制(一个线程可以管理多个socket)

2.String字符串

字符串是Redis最基本的数据类型,在Redis中所有键的类型都是字符串类型,其他几种数据结构也是在字符串类似的基础上构建的,字符串类型的值实际上可以是字符串,数字,甚至是图片、音频、视频等,但是一个字符串的最大值不能超过512MB

2.1 常见命令

2.1.1 SET

将String类型的value设置到key中,如果key之前存在,则覆盖,无论原来的数据类型是什么,之前关于此key的TTL也全部失效

语法:

SET key value [expiration EX seconds|PX milliseconds] [NX|XX]

时间复杂度:O(1)

EX:使用秒作为单位设置key的过期时间

PX:使用毫秒作为单位设置key的过期时间

NX:当key不存在时才进行设置,如果key存在,设置不执行

XX:只有在key存在时才进行设置,如果key不存在,设置不执行

上述如果设置成功,返回OK,如果SET指定了NX或者XX但条件不满足,SET不会执行,并返回(nil)

2.1.2 GET

获取key对应的value,如果key不存在,返回nil,如果value的数据类型不是String,会报错

时间复杂度:O(1)

2.1.3 MGET

一次获取多个key的值,如果对应的key不存在或者对应的数据类型不是String,返回nil

时间复杂度:O(1)

2.1.4 MSET

一次性设置多个key的值

时间复杂度:O(N),N是key的数量

单次mget比多次get效率更高,使用mget/mset可以有效减少网络时间,提高性能

2.2 计数命令

2.2.1 INCR

将key对应的String表示的数字加1,如果key不存在,则视为key对应的value是0,如果key对应的string不是一个整型或者范围超过了64位有符号整型,则报错

时间复杂度:O(1)

返回值:integer类型加完后的数值

2.2.2 INCRBY

将key对应的String表示的数字加对应的值,如果key不存在,则视为key对应的value是0,如果key对应的string不是一个整型或者范围超过了64位有符号整型,则报错

时间复杂度:O(1)

返回值:integer类型加完后的数值

2.2.3 DECR

将key对应的String表示的数字减1,如果key不存在,则视为key对应的value是0,如果key对应的string不是一个整型或者范围超过了64位有符号整型,则报错

时间复杂度:O(1)

返回值:integer类型减完后的数值

2.2.4 DECRBY

将key对应的String表示的数字减对应的值,如果key不存在,则视为key对应的value是0,如果key对应的string不是一个整型或者范围超过了64位有符号整型,则报错

时间复杂度:O(1)

返回值:integer类型减完后的数值

2.2.5 INCRBYFLOAT

将key对应的string表示的浮点数加上对应的值。如果对应的值是负数,则视为减去对应的值。如果

key不存在,则视为key对应的value是0。如果key对应的不是string,或者不是⼀个浮点数,则报

错。允许采用科学计数法表示浮点数

时间复杂度:O(1)

返回值:加/减完后的数值

上述操作的时间复杂度都是O(1),由于Redis处理命令的时候都是单线程模型,多个客户端针对同一个key进行incr操作,不会有线程安全的问题

2.3 其他命令

2.3.1 APPEND

如果key存在并且是一个string,命令会将value拼接到string后边,如果key不存在,则效果等同SET命令

时间复杂度:O(1),拼接的字符串一般长度较短,因此可以视为O(1)

返回值:拼接完之后string的长度

redis的字符串不会对字符编码做任何处理,当前Xshell默认的字符编码是utf8,其中一个汉字在utf8字符集中占3个字节,上述的解决问题是在启动redis客户端的时候加上--raw就能让redis客户端自动把二进制数据尝试翻译

2.3.2 GETRANGE

返回key对应的string的字串,由start和end确定(左闭右闭),可以使用负数表示倒数,-1表示倒数第一个字符,-2表示倒数第二个,超过范围的偏移量会根据string的长度调整成正确的值

时间复杂度:O(N),N为[start,end]区间的长度,由于string通常比较短,可以视为是O(1)

返回值:string类型的字串

2.3.3 SETRANGE

覆盖字符串的一部分,从指定的偏移开始

时间复杂度:O(N),N为value的长度,由于value通常比较短,可以视为是O(1)

返回值:替换后的string的长度

2.3.4 STRLEN

获取key对应的string长度,当key存放的类型不是string时,会报错

时间复杂度:O(1)

返回值:string的长度,或者当key不存在时,返回0

2.4 内部编码

字符串类型的内部编码右3种:

int:8个字节的长整型

embstr:小于等于39个字节的字符串

raw:大于39个字节的字符串

Redis会根据当前值的类型和长度动态决定使用哪种内部编码实现

2.5 典型使用场景

2.5.1 缓存功能

Redis被当作缓存来存储一些热点数据,其中Redis作为缓存层,MySQL作为存储层,绝大部分请求的数据都是从Redis中获取,由于Redis具有支持高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用

在上述场景中,应用服务器访问数据的时候,先查询Redis,如果Redis上数据存在,就直接从Redis取数据交给应用服务器,不在继续访问数据库了,如果Redis上数据不存在,在读取MySQL,把读到的结果返回给服务器,同时,把这个歌数据写入到Redis中

注意:上述策略存在一个问题,随着时间的推移,肯定有越来越多的key在Redis上访问不到 ,需要从MySQL中读取并写入Redis,此时Redis中的数据就会越来越多

针对这种情况,Redis提供了两种解决方案

1)把数据写入Redis的同时,给这个key设置一个过期时间

2)Redis在内存不足的时候,提供了淘汰策略

2.5.2 计数功能

在许多应用中都会使用Redis作为计数的基础功能,它可以实现快速计数、查询缓存的功能,同时数据可以异步处理到其他的数据源,例如视频网站的视频播放量次数可以使用Redis来完成,用户每播放一次视频,相应的视频播放数就增加1

2.5.3 共享会话

如下图所示,在一个分布式WEB服务将用户的Session信息保存在各自的服务器中,但是这样会有一个问题,由于负载均衡,分布式服务会将用户的访问请求均衡到不同的服务器上,并且通常无法保证用户每次请求都会被均衡到同一台服务器上,当用户刷新一次访问有可能会出现重新登录这种情况

解决上述问题,可以使用Redis将用户的Session信息进行集中管理,如下图所示,在这种模式下,所有的会话都会被各个服务器共享了

2.5.4 手机验证码

现在在许多APP中进行登录都会有手机验证码这项功能,让用户输入手机号获取验证码,然后再根据验证码进行验证,从而确定是否为用户本人,为了避免频繁的获取验证码,会限制用户每分钟获取验证码的频率

相关推荐
李慕婉学姐7 小时前
【开题答辩过程】以《基于JAVA的校园即时配送系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·开发语言·数据库
奋进的芋圆9 小时前
Java 延时任务实现方案详解(适用于 Spring Boot 3)
java·spring boot·redis·rabbitmq
sxlishaobin9 小时前
设计模式之桥接模式
java·设计模式·桥接模式
model20059 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
荒诞硬汉9 小时前
JavaBean相关补充
java·开发语言
提笔忘字的帝国10 小时前
【教程】macOS 如何完全卸载 Java 开发环境
java·开发语言·macos
2501_9418824810 小时前
从灰度发布到流量切分的互联网工程语法控制与多语言实现实践思路随笔分享
java·开发语言
華勳全栈10 小时前
两天开发完成智能体平台
java·spring·go
alonewolf_9910 小时前
Spring MVC重点功能底层源码深度解析
java·spring·mvc
沛沛老爹10 小时前
Java泛型擦除:原理、实践与应对策略
java·开发语言·人工智能·企业开发·发展趋势·技术原理