首页 > 其他 > 详细

Redis 的简单介绍

时间:2020-05-04 18:22:17      阅读:52      评论:0      收藏:0      [点我收藏+]

redis 特点

单线程

执行过程按顺序执行,不会同时执行多个操作,保证操作的原子性,省去了很多上下文切换线程的时间,不必考虑资源竞争和可能出现死锁

为什么使用单线程 ?

官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了

性能高

官方数据:Redis能读的速度是110000次/s,写的速度是81000次/s。这是以为redis的命令操作都是在内存中进行,可以在纳秒级别就处理完,使用了epoll非阻塞IO。epoll是目前最好的多路复用技术,减少网络IO的时间消耗

注:这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程

丰富的数据类型

redis支持的数据类型:string(字符串),hash(哈希),list(列表),set(集合),zset(有序集合)

扩展类型 HyperLogLoggeobitmap

  • HyperLogLog(string):实现独立用户的统计,会用 0.81%误差
  • geo(zset):记录地理位置信息,可以计算两个地点的距离或是某个点的周边范围
  • bitmap(string):可以实现用户签到等功能,大幅度减少内存消耗
Bitmap 对于一些特定类型的计算非常有效。如使用 bitmap 实现用户上线次数统计

假设现在我们希望记录自己网站上的用户的上线频率,比如说,计算用户 A 上线了多少天,用户 B 上线了多少天,诸如此类,以此作为数据,从而决定让哪些用户参加 beta 测试等活动 —— 这个模式可以使用 SETBIT key offset value 和 BITCOUNT key [start] [end] 来实现。

比如说,每当用户在某一天上线的时候,我们就使用 SETBIT key offset value ,以用户名作为 key ,将那天所代表的网站的上线日作为 offset 参数,并将这个 offset 上的为设置为 1 。

举个例子,如果今天是网站上线的第 100 天,而用户 peter 在今天阅览过网站,那么执行命令 SETBIT peter 100 1 ;如果明天 peter 也继续阅览网站,那么执行命令 SETBIT peter 101 1 ,以此类推。

当要计算 peter 总共以来的上线次数时,就使用 BITCOUNT key [start] [end] 命令:执行 BITCOUNT peter ,得出的结果就是 peter 上线的总天数。

redis使用场景

热点数据缓存,秒杀,计数器,并发锁,消息队列,分布式session,限时/限数(验证码过期,错误次数等),防爬虫(请求次数在单位时间内超过制定数组则可认为被爬虫采集)。除此之外,redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案。

事务

Redis 事务的执行并不是原子性的。事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做

Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:

  • 批量操作在发送 EXEC 命令前被放入队列缓存。
  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。但出现错误时 ,整个事务会被取消
  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
multi
incr num
incr num
exec
get num #2

# 使用 watch 可以监控某个key值发生变化时,事务会被打断
watch num # A 机器
multi num # A 机器
inrc num # A 机器
inrc num # B 机器
exec # A 机器 ,返回 nil,即操作被打断

discard # 取消事务
unwatch # 取消监视

管道 pipeline

Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。这意味着通常情况下一个请求会遵循以下步骤:
客户端向服务端发送一个查询请求,并监听scoket返回,通常是以阻塞模式,等待服务端响应。
服务端处理命令,并将结果返回给客户端。
Redis管道技术可以在服务端末响应时,客户端可以继续想服务端发送请求,并最终一次性读取所有服务端的相应。

$pipe = $redis->multi(Redis::PIPELINE);   //开启管道
for($i=?0 ; $i<??10000 ; $i++)?{
? ? ? ?$pipe->set("key::$i",str_pad($i,?4,‘0‘,?0));
? ? ? ?$pipe->get("key::$i");
}
$pipe->exec();   //提交管道里操作命令

管道和事务的区别

  • pipeline选择客户端缓冲,multi选择服务端缓冲。
  • 请求次数的不一致,multi需要每个命令都发送一次给服务端,pipeline最后一次性发送给服务端,请求次数相对于multi减少。
  • multi/exec可以保证原子性,而pipeline不保证原子性。

发布/订阅

redis 提供的发布/订阅功能只是一个低配版的,无法对消息持久化存储,一旦消息被发送,如果没有订阅者接收,那么消息就会丢失,Redis并没有提供消息传输保障。所以,如果系统中已经有了Redis,并且只需要基本的发布订阅功能,对消息的安全性没有高要求,那就可以使用redis的发布/订阅功能

subscribe news
publish news hello

# unsubscribe 取消订阅

阻塞队列

使用系统提供的阻塞原语,在新元素到达时立即进行处理,而新元素还没到达时,就一直阻塞住,避免轮询占用资源。也可以实现类似于抢票的功能。

brpop ticket 30 # 0表示一直等待
lpush ticket home

怎么样使用 Redis ?

Redis持久化的两种方式

RDB

RDB持久化是把当前进程数据生成快照保存到硬盘的过程,触发RDB持
久化过程分为手动触发(save)和自动触发(bgsave)。

save 900 1  # 900秒之内,如果超过1个key被修改,则发起快照保存;
save 300 10 # 300秒内,如果超过10个key被修改,则发起快照保存;
save 60 10000  # 1分钟之内,如果1万个key被修改,则发起快照保存;

优点:

  1. RDB是一个紧凑压缩的二进制文件,代表Redis在某个时间点上的数据快照。非常适用于备份,全量复制等场景。比如每6小时执行bgsave备份,并把RDB文件拷贝到远程机器或者文件系统中(如hdfs),用于灾难恢复。
  2. Redis加载RDB恢复数据远远快于AOF的方式。

缺点:

  1. RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运
    行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高。
  2. RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题。
    针对RDB不适合实时持久化的问题,Redis提供了AOF持久化方式来解决。

AOF持久化

AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的。
AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式。

aof 工作流程:
1、将命令转换成协议文本。
2、所有的写入命令会追加到aof_buf(缓冲区)中。
3、AOF缓冲区根据对应的策略向硬盘做同步操作 everysec
4、随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。

  1. REWRITE: 在主线程中重写AOF,会阻塞工作线程,在生产环境中很少使用,处于废弃状态;
  2. BGREWRITE: 在后台(子进程)重写AOF, 不会阻塞工作线程,能正常服务,此方法最常用 bgrewriteaof

4)当Redis服务器重启时,可以加载AOF文件进行数据恢复。

开启AOF,通过修改redis.conf配置文件

appendonly yes    ##默认不开启。
# appendfsync always
appendfsync everysec ## 默认每秒同步一次
# appendfsync no

AOF文件名通过appendfilename配置设置,默认文件名appendonly.aof。保存路径同RDB持久化方式一致,通过dir配置指定。

AOF后台Rewrite解决方案:

  • 官方解决方案

主要思路是AOF重写期间,主进程跟子进程通过管道通信,主进程实时将新写入的数据发送给子进程,子进程从管道读出数据交缓存在buffer中,子进程等待存量数据全部写入AOF文件后,将缓存数据追加到AOF文件中,此方案只是解决阻塞工作线程问题,但占用内存过多问题并没有解决。

  • 新解决方案

主要思路是AOF重写期间,主进程创建一个新的aof_buf,新的AOF文件用于接收新写入的命令,sync策略保持不变,在AOF重写期间,系统需要向两个aof_buf,两个AOF文件同时追加新写入的命令。当主进程收到子进程重写AOF文件完成后,停止向老的aof_buf,AOF文件追加命令,然后删除旧的AOF文件(流程跟原来保持一致);将将子进程新生成的AOF文件重命名为appendonly.aof.last,具体流程如下:

  1. 停止向旧的aof_buf,AOF文件追加命令;
  2. 删除旧的的appendonly.aof.last文件;
  3. 交换两个aof_buf,AOF文件指针;
  4. 回收旧的aof_buf,AOF文件;
  5. 重命令子进程生成的AOF文件为appendonly.aof.last;

系统运行期间同时存在两个AOF文件,一个是当前正在写的AOF,另一个是存量的AOF数据文件。因此需要修改数据库恢复相关逻辑,加载AOF时先要加载存量数据appendonly.aof.last,再加载appendonly.aof。

最多丢失1秒还是2秒的数据?

主从复制

在主从复制的概念中,数据库分为两类,一类是主数据库(master),另一类是从数据库[1] (slave)。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。

优点:

  1. 实现了对master数据的备份,一旦master出现故障,slave节点可以提升为新的master,顶替旧的master继续提供服务(需要手动切换)
  2. 实现读扩展。使用主从复制架构, 一般都是为了实现读扩展。Master主要实现写功能, Slave实现读的功能

缺点:

  1. 当maser宕机后,需要人工参与

    1)在Slave1上执slaveof no one命令提升Slave1为新的Master节点。

    2)在Slave1上配置为可写,这是因为大多数情况下,都将slave配置只读。

    3)告诉Client端(也就是连接Redis的程序)新的Master节点的连接地址(改代码)。

    4)配置Slave2从新的Master进行数据复制。 ==> (sentinel )

  2. 虽然实现了读写分离,但是主节点的性能会成为主要瓶颈 ==>(集群 cluster)

Sentinel 哨兵机制

`Redis Sentinel` 为 `Redis` 提供了高可用方案。从实践方面来说,使用 `Redis Sentinel` 可以创建一个无需人为干预就可以预防某些故障的 `Redis` 环境。实现自动切换。在部署 `Sentinel` 的时候,建议使用奇数个 `Sentinel` 节点,最少三个 `Sentinel` 节点。

功能:
1、 监控,Sentinel 会不断的检查 masterslave 是否像预期那样正常运行。
2、通知,通过 APISentinel 能够通知系统管理员、程序监控的 Redis 实例出现了故障。
3、自动故障转移,如果 master 不像预想中那样正常运行,Sentinel 可以启动故障转移过程,其中的一个 slave 会提成为 master,其它 slave 会重新配置来使用新的 master,使用Redis 服务的应用程序,当连接时,也会被通知使用新的地址。
4、配置提供者,Sentinel 可以做为客户端服务发现的认证源:客户端连接 Sentinel 来获取目前负责给定服务的 Redis master 地址。如果发生故障转移,Sentinel 会报告新的地址。

3个定时任务

1、每 10 秒每个 sentinelmasterslave 执行 info replication,以发现新的 slave 节点并确认主从关系。
2、每 2 秒每个 sentinel 通过 master 节点的 channel__sentinel__:hello)交换信息,以交换对节点的”看法“和自身信息。
2、每 1 秒每个 sentinel 对其他 sentinel 和主、从节点发送 ping 命令来做心跳检测。

故障切换实现原理

1、Sentinel 之间进行选举,选举出一个 leader ,由选举出的 leader 进行 failover
2、Sentinel leader 选取 slave 节点中的一个 slave 作为新的 Master 节点。对 slave 选举需要对 slave 进行选举的方法如下:
2.1、与 master 断开时间,如果与 master 断开的时间超过 down-after-milliseconds (sentinel配置) * 10秒加上从 sentinel 判定 master 不可用到 sentinel 开始执行故障转移之间的时间,就认为该 slave 不适合提升为 master
2.2、slave 优先级,每个 slave 都有优先级,保存在 redis.conf 配置文件里。如果优先级相同,则继续进行。
2.3、复制偏移位置,复制偏移纪录着从master复制数据复制到哪里,复制偏移越大表明从master接受的数据越多,如果复制偏移量也一样,继续进行选举。
2.4、Run ID,选举具有最小 Run IDSlave 作为新的 Master
3、Sentinel leader 会在上一步选举的新 master 上执行 slaveof no one 操作,将其提升为 master 节点。
4、Sentinel leader 向其它 slave 发送命令,让剩余的 slave 成为新的 master 节点的 slave
5、Sentinel leader 会让原来的 master 降级为 slave,当恢复正常工作,Sentinel leader 会发送命令让其从新的 master 进行复制。

配置文件说明

daemonize yes #以后台进程模式运行
 
port 27000 # 哨兵的端口号,该端口号默认为26379。

#redis-master

sentinel monitor redis-master 192.168.1.51 7000 2

# redis-master是主数据的别名,考虑到故障恢复后主数据库的地址和端口号会发生变化,哨兵提供了命令可以通过别名获取主数据库的地址和端口号。
# 192.168.1.51 7000为初次配置时主数据库的地址和端口号,当主数据库发生变化时,哨兵会自动更新这个配置,不需要我们去关心。
# 2,该参数用来表示执行故障恢复操作前至少需要几个哨兵节点同意,一般设置为N/2+1(N为哨兵总数)。

sentinel down-after-milliseconds redis-master 5000
#如果master在多少秒内无反应哨兵会开始进行master-slave间的切换,使用“选举”机制

sentinel failover-timeout redis-master 900000
# 如果master 在 900000 秒后恢复,则把它加入到 slave 中

logfile "/data/bd/redis/sentinel/sentinel.log" #log文件所在地

docker+redis实例

设置配置

# redis-master.conf
port 7000
daemonize yes
pidfile /var/run/redis.pid
logfile "7000.log"

sed ‘s/7000/7001/g‘ redis-master.conf > redis-slave1.conf
sed ‘s/7000/7002/g‘ redis-master.conf > redis-slave2.conf

echo "slaveof redis-master 7000" >> redis-slave1.conf
echo "slaveof redis-master 7000" >> redis-slave2.conf

启动redis-master容器

# 启动redis-master容器
sudo docker run -d -p 7000:7000 -v /home/summer/homework/redis/config/redis-master.conf:/etc/redis/redis.conf --name redis-master redis redis-server /etc/redis/redis.conf

启动 redis-slave 容器

# 启动redis-slave1容器
sudo docker run -d -p 7001:7001 -v /home/summer/homework/redis/config/redis-slave1.conf:/etc/redis/redis.conf --name redis-slave1 --link redis-master:master redis  redis-server /etc/redis/redis.conf

# 启动redis-slave2容器
sudo docker run -d -p 7002:7002 -v /home/summer/homework/redis/config/redis-slave2.conf:/etc/redis/redis.conf --name redis-slave2 --link redis-master:master redis redis-server /etc/redis/redis.conf

注意问题

1、禁止线上执行 keys *、mset、mget、hmget、hmset 等操作,会造成阻塞。
2、避免同一时间缓存大面积失效,造成缓存雪崩。
3、 内存数据库,键名长度影响有限内存空间,所以命名应该控制长度,简短易懂。
4、大小写规范。
5、根据业务命名,相同业务统一的 Key 前缀。

常用命令

debug sleep 10

config set xx yy

config  get  xx*

info replication  # 查看主从信息

info server

info sentinel

参考资料

Redis 的简单介绍

原文:https://www.cnblogs.com/yxhblogs/p/12714374.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!