本文目录
- 初识Redis
- 什么是分布式系统
- 负载均衡的更新
- 业务拆分--微服务
- 关于Redis的特性
- Redis的安装
- ubuntu上安装
- centos上安装
- 虚拟机安装redis
- Redis客户端
- Redis操作
- 基本全局命令
- keys的pattern(样式)
- set/get
- Exists
- Del
- Expier
- TTL
- redis过期删除机制
- type
- Redis数据类型
- Redis单线程模型
前言:可能大家在没了解Redis之前,大概率是听说过mysql的,我们知道mysql是一个关系型数据库,使用结构查询语言管理数据库,mysql的特性也就是其事物的四个特性:持久性,原子性,隔离性,一致性。而Redis与mysql不同,他是一个内存级别的非关系性数据库,Redis有什么过人之处呢?现在就让我们来认识一下:
初识Redis
Redis是一个开源的、内存中的数据结构存储系统,它可以用作数据库、缓存和消息中介。
Redis支持多种类型的数据结构,如字符串、哈希、列表、集合、有序集合等,并提供了丰富的操作命令来处理这些数据。
由于其基于内存的特性,Redis具有极高的读写性能,适用于需要快速响应和高并发的场景。
此外,Redis还支持数据持久化、主从复制、分布式处理等功能,以确保数据的可靠性和可扩展性。
Redis是将自己内存的数据基于网络,支持多个进程,甚至不同主机间的进程的数据通信,适用于分布式系统。
相对于mysql:Redis访问数据的速度更快,直接向内存访问,而不是磁盘。 但由于内存是有限的,因此Redis存储的数据是有限的。
由于内存特性,Redis还会经常用作为缓存存储数据,例如将mysql与Redis结合使用,按照二八原则,将Redis作为缓存,mysql作为主存存储数据,实现一个又大又快的数据存储。
Redis最初是为了做一个消息中间件(消息队列),分布式下的生产消费模型。
综上,Redis的主要适用场景就是分布式系统,那什么是分布式系统呢》
什么是分布式系统
为了明确分布式系统,我们先来了解单机架构,那么什么是单机架构呢?
所谓的单机架构,即只有一台服务器,这个服务器负责所有的工作。
如上图一个简单的电商网站的组成,有一个单机服务器完成所有功能,单机服务器主要用有两个服务一个是应用服务(服务端程序),一个是数据库服务(数据存储)。此时一台主机能满足业务需求,这就是单价架构。
但是我们一台主机上的硬件资源是有限的,当同一时刻处理的请求多了,就可能会导致某个硬件资源不够用了,从而导致访问时间变长,甚至出错,一般遇到这种场景我们会进行优化:开源节流,软件上的优化。但仅此资源还是不够,于是不得已引入多台主机,而这样多台主机共组构成的一个服务系统,就是分布式系统了。这是一种无奈之举。
但这样系统的复杂性就会提高,出现bug的概率也会比较高。
负载均衡的更新
改进之后,引入多个应用服务器应对请求过多的情况,先访问负载均衡器上(一个网关),在让负载均衡器进行请求的分发。
那负载均衡器请求超了
负载均衡器也是一个服务器,再分发请求前,他会承担上万个更多的请求,它是如何承担的呢?事实上,负载均衡的承担量是远远高于应用服务器的,不过应用服务器失去执行的请求的,而负载均衡仅仅识分配任务,所以承担量更多,那如果请求量足够大呢?那我们会引入多个负载均衡器(引入多个机房)。
与此同时存储服务器的承担量也更多了,处理方法开源节流,开源门槛高,节流我们在引入新的的数据库服务器。
此时数据库服务分为两种,主数据库服务(master)与从数据库服务(slave),两个数据库服务器数据同步,master负责写,写的次数相对较少点,更多的是读,因此slave负责读—一主多从。
由于数据库响应慢,因此又添加了一个缓存服务器,这里会放一些热点数据(经常读写数据),遵循二八原则:20%的数据能够支持访问80%的请求。通过这种方式,能够大大提升访问速率。
此时这里的缓存服务器就也可以用Redis,快但是小。只有当缓存服务器中找不到,此时再去别的存储服务器。但在修改时,需要进行数据同步的实现。
其他:引入分布式系统,不仅去解决高并发请求问题,还要处理数量足够大。因为存在一台服务器可能存不下数据,因此会引入多台服务器,并对数据库进行拆分(分库,分表),这会根据具体的业务场景。
业务拆分–微服务
在实际业务需求中,我们的业务是非常复杂的,上图我们直接就用应用服务器表示我们处理业务请求,但其实,我们还可以对应用服务器在进行拆分,拆分成更加细致的应用服务(功能更单一,体量更小),因此我们会使用微服务架构进行业务拆分,使用一个微服务器完成业务中的某个功能。
之前我们使用一个大的服务器处理整个业务,这样太复杂了,而微服务就是将业务细化,使用一个根据业务大小类型的微服务器来处理这业务中的一个具体功能,因此服务器的种类和数量增加了,需要更多人手来维护,不过业务处理更加方便细致了。
但是引入微服务还是会有缺点的:
1.性能下降–查出更多的服务,多个功能相互依赖,进行网络通信可能速度比硬盘还要慢。虽然现在有了万兆网卡,读写速度比硬盘快很多,但是贵,需要配备对应的万兆交换机,路由器,网线等。
2.系统复杂性提高,可用性降低。
综上,我们对分布式系统做一下小结:❤️
1.区别于单机架构,分布式系统具有更多的主机/服务。
2.数据库与应用分离,应用程序与数据库程序在不同的主机上部署。
3.引入负载均衡-》应用服务器集群,通过负载均衡器将大量请求均匀的分发给应用服务器
4.引入读写分离,数据库主从结构,一个用做主,其他都是从,master用来写,slave用来读。
5.引入缓存数据库,对冷热数据分离,使用二八原则。
6.引入数据库分库分表,扩展存储空间----------------以上是针对大量数据以及请求
7.为了应对复杂业务,使用微服务进行业务拆分。
在这里,redis在这里的用途主要是用作缓存。
关于Redis的特性
关于Redis我们可以上官网的介绍去了解一下它:首先Redis是一个在内存中存储数据的中间件,我们可以用它作为数据库,缓存,消息队列等,对于数据库来说,他是一个非关系型数据库,存储的数据为键值对,它的key都是string,val可以是hash,list,set,string等。
Redis的操作:Redis可以通过命令行的方式进行数据操作,也可以通过脚本(lua)进行批量操作并且可附带一些逻辑性。
Redis的扩展:除此之外,Redis也支持在原有基础上进行功能扩展,提供了一批API,通过c/c++,Rust进行调用扩展(编写动态链接库)。
Redis的持久化:因为是作为内存存储的数据,为了保证数据的持久性,Redis会将苏剧存两份,硬盘与内存都存,在开始加载时,将数据加载到内存,对于数据操作,就在内存中,增删查改会在内存释放时写回磁盘中。Redis支持分布式系统的集群:即水平扩展,类似于数据库分库分表,由于一个主机的内存是有限的,因此我们可以引入多个Redis,每个存一份。
高可用性:所谓的高可用性即冗余/备份,Redis也支持主从结构,从节点伟主节点的备份。
Redis能在这么多数据库中占有一席之地,这是因为他快!!1.能存中访问数据。2.处理的业务逻辑简单。3,使用了IO多路复用的方式处理请求(epoll)4.使用的单线程模型,提高效率(Redis不太吃cpu)。
Redus的用途?:1.用作数据库2.例如用在负载均衡下的系统中,用来存储sessionid信息的缓存数据,通过这种方式即使应用服务器重启,会话都不会丢失。3.用作消息队列,一个服务器(生产消费模型)。
凡事都有两面性,有以上能做的,当然也有不能做的,对于存储大量数据来说,Redis是不太适合的。
Redis的安装
首先就是版本选择,对于我们初学者来说,Redis5就够用了,且企业一般也会选择版本5,而Redis7安装会比较麻烦一点,两者用法差别不大。
其次Redis的安装是不支持windows系统的,不过微软维护了一个window的Redis,对于我们来说,就用Linux就行,Linux系统可以使用虚拟机,也可以购买云服务器,都是可以的,不过前者稍微有点麻烦,但问题都不大,当然后面我们也可以使用docker进行安装。下面我们对centos与ubuntu两个发行版本上介绍一下如何安装。😙
ubuntu上安装
//切换root
su root
//安装redis,先搜索安装包
apt serch redis
//这里我们就选择redis
apt install redis
//一般安装成功,服务就会被启动起来
netstat -nap |grep redis
//由于当前是本地换回ip,因此需要修改配置文件
vim /etc/redis/redis.conf
//修改bind 172.0.0.1 ::1 为 0.0.0.0 ::1 代表其他主机也可以访问
//除此之外,为了能进行不同主机间的访问,将protected-mode改为设置为no
//修改完配置进行重启
service redis-server restart(就是一个脚本指令)
//最后我们可以使用redis自带的客户端连接服务器
redis-cil 进入redis自己的客户端
ping 会用PONG 说明正常工作
centos上安装
centos使用yum进行安装。如果是centos8的话里面的安装包就是redis5,我这里是7,自带的安装包版本是会比较老的,你可可以使用如下指令查看redis安装包的版本:
yum list | grep redis
可以看到确实有点老了,此处我们去安装其他源,我们这里可以安装一个scl源。
yum install centos-release-scl-rh
//下载之后通过这个下载源,我们进行redis的安装
yum install rh-redis5-redis
//由于安装后的redis的一些文件在协管的目录下,我们创建符号连接,创建方式与创建软连接一样
cd /usr/bin //该目录下访问
ln -s /opt/rh/rh-redis5/root/usr/bin/redis-server ./redis-server
ln -s /opt/rh/rh-redis5/root/usr/bin/redis-sentinel ./redis-sentinel
ln -s /opt/rh/rh-redis5/root/usr/bin/redis-cli ./redis-cli
// 针对配置⽂件设置符号链接 所有文件放在该目录写的redis中
cd /etc/
ln -s /etc/opt/rh/rh-redis5/ ./redis
之后就是改配置文件了,进入配置文件,设置ip为0.0.00,设置保护模式为no,设置守护进程(后台程序)daemonize为yes,
之后就是配置工作目录:
之后使用redis创建的数据文件都活保存在工作目录下:
mkdir -p var/lib/redis
dir var/lib/redis
//在设置日志文件
mkdir -p var/log/redis/ //创建目录
//之后需要在配置文件中设置日志文件
logfile /var/log/redis/redis-server.log //设置路径
启动redis服务器,使用命令来启动
使用命令来启动
redis-server /etc/redis/redis.conf
//查看服务
netstat -anp r| grep redis
//关闭服务
可以杀掉这个进程
kill pid
虚拟机安装redis
百度网盘下载
//先去下载需要版本的redis的,实用工具xftp安装到虚拟机中
tar -zxvf redis-5.0.0.tar.gz //解压
//静茹redis目录
cd redis-5.0.0
make && make install //编译并安装
redis-server//启动redis服务
修改redis.conf配置文件(操作同上):
将daemonize no 修改为 daemonize yes
设置Redis密码 找到这一行 #requirepass foobared 在下面添加 requirepass 123456
ip 地址 bind 127.0.0.1 改为0.0.0.0 最后
log日志
:wq 保存退出即可
为了方便服务启动编写脚本配置成系统服务
新建redis.service文件,配置成系统服务 将文件存到到 /usr/lib/systemd/system
redis.service
[Unit]
Description=Redis
After=syslog.target network.target remote-fs.target nss-lookup.target[Service]
Type=forking
ExecStart=/root/redis-5.0.0/src/redis-server /root/redis-5.0.0/redis.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true[Install]
WantedBy=multi-user.target
这里注意需要自己改动的地方时redis这个文件的路径替换掉这里的root。(我这里是root,方便看)
其实启动程序就是把redis里面的服务端程序的路径与配置文件的路径告诉给这个ExecStart,之后使用这个指令进行程序替换,就实现了脚本启动。
重新启动服务:systemctl daemon-reload
之后就可以使用系统服务来启动关闭redis了。
Redis客户端
Redis也是一个客户端服务器程序,服务器作为本体存储数据,客户端作为用户使用的工具,客户端的存在方式也有几种:1.第一种就是自带命令行的客户端,redis-client,自带的客户端程序。2.图形化桌面程序,web程序。3.基于redis提供的API自己开发一个客户端。这也是工作中最主流的方式。
Redis操作
Redis的整体学习,我们大多是根据文档来的,因为Redis的命令非常多。点击这里到官网。点击这里直接到command搜索。
以下介绍一些常用的指令:
基本全局命令
首先Redis有五种数据结构,但他们,但是他们都是键值对的结构,而对于键(key)来说有一些通用的命令。
keys的pattern(样式)
⭐️返回所有满足样式的key,即key的字符串格式。支持以下的统配样式:
h?llo ?代表任意一个字符 可以匹配hallo hello hmllo hhll
h*llo *表示任意0个或者多个 可以匹配heello hhllo hhhhhhllo等
h[ebcl]lo [ ] 表示只能匹配括号里的字符。hello hbllo hcllo
h[^e]llo 表示除了括号里的,都能匹配。
h[a-b]llo 表示范围内的匹配。
之后我们就可以格局这种类似正则表表达式的用法来查询key
127.0.0.1:6379> set hello 1
OK
127.0.0.1:6379> set hallo 1
OK
127.0.0.1:6379> set hellllllo 1
OK
127.0.0.1:6379> set heeeeeeeelo 1
OK
127.0.0.1:6379> keys h?llo
1) "hallo"
127.0.0.1:6379> keys h[a-b]llo
1) "hallo"
keys命令的时间复杂度为O(N),这已经算比较耗时的了,因此在生产环境中是不会去使用keys的,尤其是keys *.这也是因为redis是一个单线程进程,如果查找时间过长,就会被阻塞,其他用户就无法使用redis了。
set/get
⭐️ 我们知道redis存储的数据是键值对,而get就是根据key获取value,set就是设置key对应的value.
这里再进行数据的存储时,字符串可以不带“”,且不区分大小写。
如上图,其中get如果查找不到就会返回nil,其实就是NULL,表示空。
Exists
⭐️判定key是否存在,返回值表示key的个数,因为我们可以一行判断多个key。虽然存储的数据为键值对,但是value可以是很多数据结构,比如链表,字符串,数组等。相比于一次次的查询,多次查询效率更高一些.
语法格式:exists key [kye1,key2],可以以此判断多个key是否存在。
127.0.0.1:6379> exists hello hallo
(integer) 2
127.0.0.1:6379> exists hello
(integer) 1
Del
⭐️ 作用是删除指定key,语法格式:del key[keys1,keys2].以exists一致,可以删除一个或者多个key。时间复杂度O(1),返回值表示删除的个数。
127.0.0.1:6379> del heeeeeeeelo
(integer) 1
若不存在就返回0.对于删除数据,大家其实太过害怕,因为redis一般是用作缓存,存储的是一些热点数据,即使已删掉几个,影响也不大,因为我们的mysql才是真正存储我们用户的数据。不过我们还是不建议一般区删除数据。
Expier
⭐️ 作用:给key设定过期时间,即生命周期,到达时间后,自动删除key。这种业务场景也经常见,比如短信验证码,它的有效益一般只有2分钟。
语法格式:expire key seconds ,单位是秒。除了expire,还有pexpire key time,单位是毫秒。当然,此处设定key的周期,我们需要前提是有key。成功为1,失败返回0.时间复杂度O(1).
127.0.0.1:6379> expire hello 5
(integer) 1
127.0.0.1:6379> exists hello
(integer) 0
TTL
⭐️ 作用:查询key的生命周期,即time to live。除了ttl,对应上面的expire,还有pttr对应上面的pexpire。
语法格式:ttl key ,返回值要么是过期的时间,要么是-1,未设置生命时间,-2没有这个key。
127.0.0.1:6379> expire hallo 1000
(integer) 1
127.0.0.1:6379> ttl hello
(integer) -2
127.0.0.1:6379> ttl hallo
(integer) 989
127.0.0.1:6379> ttl hallo
(integer) 986
redis过期删除机制
我们知道redis是会存许多数据的,那么这么多key哪些是设置了生命周期的,哪些没设置,哪些过期了,哪些没过期,我们是需要去区分的,首先对于删除有两种形式,一种是定时删除,到了时间直接删除,一种是惰性删除,到了时间并不会立刻就删除,也有可能到了时间也不会删除(看情况),但是到了第二次访问 时就会被删除。无论是定期还是惰性,都需要去遍历key找到对应的key干掉他,而遍历key的策略是,每次抽查一部分数据出来遍历。虽然使用了上述策略,但还是有过期的key没被删除。
除此之外,redis还有一种内存淘汰机制。
其次这里对定期删除的时间,也是有要求的,这是因为我们redis是单线程的程序,主要的任务是去处理每个指令,不过因为扫描过期的key的时间太长,反而会导致其他正常的请求命令被阻塞住,产生服务中断。我们是要去避免与keys *这种会阻塞服务请求的情况的发生.
其次,redis是并没有采用这种定时器的策略去删除过期key,这种方式实现堆同一时间的key的删除也能更高效,但是redis并没有去使用。大概率是因为redis的作者想走单线程走到底,使用定时器务必要引入多线程,但这也只是猜想。
对于定时器的实现也是有很多形式,我们介绍一下:
1.以优先级队列/堆实现的多线程的定时器。
2.基于时间轮的定时器。(将一段时间进行划分,每隔一段时间,指针移动一个格子)。
type
说明:返回key对应value的数据类型,例如none,string,list,set,zset,hash,stream等等。时间复杂度为O(1)
127.0.0.1:6379> set key3 111
OK
127.0.0.1:6379> type key3
string
127.0.0.1:6379> lpush key4 111 222 333
(integer) 3
127.0.0.1:6379> type key4
list
Redis数据类型
对于我们当前redis5版本的支持的数据类型有如下数据类型:
但对于我们来说,掌握常用的五个数据类型即可:Strings,List,Sets,Hashes,Storted sets.虽然Redis承诺有这些数据结构,以及这些数据的操作是啥,但是面对不同的情况下,底层是否有就是我们认为的数据结构,是不一定的,Redis只保证了你可以这样用,以及时间复杂度O(1).
当然,我们也可以使用指令查看当前编码的方式是啥,Object encoding
key ,查看key的实际编码。
Redis单线程模型
redis能够很好的使用单线程模型工作,主要原因是redis的核心业务逻辑都是短平快的,不太消耗cpu资源不需要多核,redis说是单线程并不是只有一个线程,而是对于redis的操作执行都是在一个线程里完成的。在面对并发情况下,redis使用单线程模型,在面对多个服务时按顺序(串行)的去执行对应的指令。
但是redis的单线程模型也就有了弊端,当一个服务阻塞住了时,其他服务就无法进行,比如之前说的keys *.
即使业务逻辑短平快,但你使用单线程模型处理请求,为啥Redis还能这么快呢?
1.之前就说过redis时内存数据,直接就访问内存了,不用再去访问硬盘。
2.redis的核心功能简单,像mysql在插入删除时还要面对大量数据,约束的情况,各种查询,redis相对于更加简单,根据key查询删除。
3.单线程模型,也有好处,减少了其他不必要的线程开销。
4.在处理网络请求的时候,使用的是之前说过的IO多路复用,使用epoll模型。