1.Redis定义
Redis(全称:Remote Dictionary Server 远程字典服务)是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。
2.Redis数据类型以及应用场景
五大基本数据类型:
- 字符串(String):可以用来做最简单的数据,可以缓存某个简单的字符串,也可以缓存某个son格式的字符串,Redis分布式锁的实现就利用了这种数据结构,还包括可以实现计数器、Session共享、分布式ID。
- Hash(哈希表):可以用来存储一些key-value对,更适合用来存储对象。
- Set(集合):和列表类似,也可以存储多个元素,但是不能重复,集合可以进行交集、并集、差集操作,从而可以实现类似,我和某人共同关注的人、朋友圈点赞等功能。
- Zset(有序集合):集合是无序的,有序集合可以设置顺序,可以用来实现排行榜功能。
- List:Redis的列表通过命令的组合,既可以当做栈,也可以当做队列来使用,可以用来缓存类似微信公众号、微博等消息流数据。
三大特殊数据类型:
- geospatial(地理位置):Redis的Geo在Redis3.2版本就推出了,这个功能可以推算出地理位置的信息,两地之间的距离,方圆几里的人。
只有6个命令:
- geoadd:添加地理位置。
- geopos:获得当前位置:一定是一个坐标值。
- geodist:两个之间的直线距离。
- georadius:以给定的经纬度为中心,找出某一半径内的元素!
- georadiusbymember:找出位于指定元素周围的其他元素
- geohash:返回一个或多个位置元素的Geohash表示该命令将返回11个字符的Geohash字符串!将二维的经纬度转换为一维的字符串!如果两个字符串越接近,则距离越近!
- HyperLogLogs(基数统计)
什么是基数:
A{1,3,5,7,8,9} B{1,3,5,7,8}
基数:不重复的元素= 9,可以接收误差
网页的UV(一个人访问一个网站多次,但是还是算做一个人!):PV:page view 页面浏览量UV(unique visitor,网站独立访客)
传统的方式:set保存用户的id,然后就可以统计 set 中的元素数量作为标准判断!
这个方式如果保存大量的用户id,就会比较麻烦!我们的目的是为了计数,而不是保存用户id;
如果允许容错,那么一定可以使用 Hyperloglog!
如果不允许容错,就使用set或者自己的数据类型即可!
- Bitmaps (位图)
位存储
怎么筛选用户最快?
-
统计疫情感染人数: 0 1 0 0 1 0 1
-
统计用户信息,活跃,不活跃
-
登录、未登录!
-
打卡,365天打卡(userid status day)
-
两个状态的都可以使用bitmaps
Bitmaps 位图,数据结构!都是操作二进制位来进行记录,就只有0 和 1两个状态!
365天 = 365 bit 1字节 = 8位 46个字节左右!
3.基础命令
Redis:默认有16个数据库,默认使用是第0个。
可以使用select来切换数据库!
[root@ bin]# redis-cli -p 6379
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> select 3 # 切换数据库
OK
127.0.0.1:6379[3]> dbsize # 查看DB大小
(integer) 0
127.0.0.1:6379[3]>
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> dbsize
(integer) 0
127.0.0.1:6379[3]> select 0
OK
127.0.0.1:6379> dbsize
(integer) 5
127.0.0.1:6379> get name
"xiaoming"
127.0.0.1:6379>
keys * # 查看数据库所有的key
flushdb # 清空当前数据库
flushall # 清空全部数据库
exists + 字段名:字段是否存在,结果为1 则存在
move 字段名 数据库编号:表示将字段从本数据库移动到目标数据库
expire 字段名 过期时间:设置字段过期时间
4.Redis单线程
明白redis是很快的,官方表示,Redis是基于内存操作的,cpu不是redis的性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了!所以就使用了单线程。
Redis是C语言写的,官方提供的数据为 100000+的QPS,完全不比同样是使用key-value的Memecache差!
Redis为什么单线程还这么快?
1.误区1:高性能的服务器一定是多线程的?
2.误区2:多线程(CPU上下文会切换)一定比单线程效率高!
CPU>内存>硬盘 速度
核心:redis将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程之间CPU会上下文切换,是一个耗时的操作!!!,对于内存系统来说,如果没有上下文切换,效率就是最高的!多次读写都是在一个cpu上的,在内存情况下,这个就是最佳的方案!
原因:
-
纯内存操作。
-
核心是基于非阻塞的IO多路复用机制。
-
单线程反而避免了多线程的频繁上下文切换带来的性能问题。
5.Redis过期键删除策略
Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间,Redis的过期策略就是指当Redis中缓存的key过期了,Redis该如何处理。
- 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况下可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量的内存。
- 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已经过期的key。该策略是前两者的一个折中方案。提高调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU的内存资源达到最优的平衡效果。
(expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。)
Redis中同时使用了惰性过期和定期过期两种过期策略。
6.Redis事务
Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行的过程中,会按照顺序执行!
一次性、顺序性、排他性!执行一系列的命令!
Redis事务没有隔离级别的概念!
所有命令在事务中并没有直接被执行,只有发起执行命令的时候才会执行!
Redis单条命令是保证原子性的,但事务不保证原子性!
Redis事务:
-
开启事务(multi)
-
入队(...)FIFO
-
执行事务(exec)
1.正常执行事务
127.0.0.1:6379> MULTI # 开启事务
OK
# 命令入队
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> EXEC # 执行事务
1) OK
2) OK
3) "v2"
4) OK
2.编译型异常(代码有问题!命令有错误!事务中所有的命令都不会被执行)
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> getset k3 # 错误的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> set k5 v5
QUEUED
127.0.0.1:6379(TX)> EXEC # 执行事务报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5 # 所有的命令都不会被执行
(nil)
3.运行时异常(1/0 ),如果事务队列中存在语法型错误,那么执行命令时,其他命令可以正常执行的,错误命令会抛异常。
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incr k1 # 执行的时候会失败
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> exec
1) (error) ERR value is not an integer or out of range # 虽然第一条命令报错了,但是依旧正常执行成功了!
2) OK
3) OK
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3
监控!Watch命令
WATCH命令是一个乐观锁,可以为Redis事务提供check-and-set (CAS)行为。可以监控一个或多个键,一旦其有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> WATCH money #监视money对象
OK
127.0.0.1:6379> multi # 事务正常结束,数据期间没有发生变动,这个时候就正常执行成功!
OK
127.0.0.1:6379(TX)> decrby money 20
QUEUED
127.0.0.1:6379(TX)> INCRBY out 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
2) (integer) 20
一旦事务结束,监控就自动结束 !
执行失败会自动解锁
先获取监视对象的值,在执行事务的过程中去比较对象的值是否发生变化,如果发生变化则不执行
Redis不支持事务回滚机制,但是它会检查每一个事务中的命令是否错误。
7.Redis持久化
RDB:
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。
Redis会单独创建 ( fork )一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。我们默认的就是RDB,一般情况下不需要修改这个配置!
有时候生产环境我们会将这个文件进行备份!
rdb保存的文件是 dump.rdb,都是在我们的配置文件中快照中进行配置的!
触发机制
1、save的规则满足的情况下,会自动触发rdb规则
2、执行flushall命令,也会触发我们的rdb规则!
3、退出redis时,也会产生rdb文件!
备份就自动生成一个 dump.rdb
AOF(Append Only File )
将我们的所有命令都记录下来,history,恢复的时候就把这个文件全部在执行一遍。
以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
Aof保存的是 appendonly.aof文件
默认是不开启的,需要将appendonly改为yes即可,就开启了aof
重启后,redis就可以生效了!
aof和rdb同时开启,先加载rdb,在加载aof,参考配置 aof-use-rdb-preamble,默认是yes
redis会自动尝试修复,当提示无法修复的时候再用check
区别:
-
AOF 文件比 RDB 更新频率高,优先使用 AOF 还原数据;
-
AOF比 RDB 更安全也更大;
-
RDB 性能比 AOF 好;
-
如果两个都配了优先加载 AOF。
8、缓存击穿、穿透、雪崩
-
缓存雪崩:如果缓存中某一时刻大批热点数据同时过期,那么就可能导致大量请求直接访问Mysql了,解决办法就是在过期时间上增加一点随机值,另外如果搭建一个高可用的Redis集群也是防止缓存雪崩的有效手段
解决办法:
-
加锁排队:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个 key 只允许一个线程查询数据和写缓存,其他线程等待;
-
数据预热:可以通过缓存 reload 机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的 key,设置不同的过期时间,让缓存失效的时间点尽量均匀;
-
做二级缓存,或者双缓存策略:Cache1 为原始缓存,Cache2 为拷贝缓存,Cache1 失效时,可以访问 Cache2,Cache1 缓存失效时间设置为短期,Cache2 设置为长期。
-
在缓存的时候给过期时间加上一个随机值,这样就会大幅度的减少缓存在同一时间过期。
-
-
缓存击穿:和缓存雪崩类似,缓存雪崩是大批热点数据失效,而缓存击穿是指某一个热点key突然失效,也导致了大量请求直接访问Mysql数据库,这就是缓存击穿,解决方案就是考虑这个热点key不设过期时间
解决方案:
-
设置热点数据永不过期
-
加互斥锁:分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。
-
-
缓存穿透(查不到):假如某一时刻访问redis的大量key都在redis中不存在(比如黑客故意伪造一些乱七八糟的key),那么也会给数据造成压力,这就是缓存穿透。
解决方案1是使用布隆过滤器,它的作用就是如果它认为一个key不存在,那么这个key就肯定不存在,所以可以在缓存之前加一层布隆过滤器来拦截不存在的key
解决方案2:缓存空对象:如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
理解:缓存就是一堵墙,穿透就是越过墙,击穿就是墙破了个洞,雪崩就是墙倒了!
9、Redis哨兵
(自动选举老大的模式)
主从切换技术的方法是︰当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。Redis从2.8开始正式提供了Sentinel (哨兵)架构来解决这个问题。
谋朝篡位的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
这里的哨兵有两个作用:
-
通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
-
当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。
假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1王观的认为土服穷落个可用,这1现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵乙同就云进行一火仅D,内亦PA果由一个哨兵发起,进行failover[故障转移]操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切 换主机,这个过程称为客观下线。
以上仅是Redis基础,仅供参考!!