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

相关推荐
hummhumm5 分钟前
数据结构第08小节:双端队列
java·数据结构·spring boot·spring·java-ee·maven·intellij-idea
不吃饭的猪22 分钟前
【无标题】
java·开发语言·docker
六月的雨__22 分钟前
二手物品交易小程序的设计
java·sql·学习·小程序
Casual_Lei1 小时前
Hibernate
java·oracle·hibernate
在无清风1 小时前
数据结构(期末)
java·开发语言
计算机周老师1 小时前
java-arraylist 源码分析 1
java·python·算法
醉颜凉1 小时前
多态的优点
java·面试·职场和发展·多态的优点·可替换性·可扩充性
无知的小菜鸡1 小时前
SSM学习2:依赖注入、依赖自动装配、集合注入、加载properties文件
java
Kitty@Q1 小时前
docker k8s
java·docker·kubernetes