执行过程按顺序执行,不会同时执行多个操作,保证操作的原子性,省去了很多上下文切换线程的时间,不必考虑资源竞争和可能出现死锁
为什么使用单线程 ?
官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了
官方数据:Redis能读的速度是110000次/s,写的速度是81000次/s。这是以为redis的命令操作都是在内存中进行,可以在纳秒级别就处理完,使用了epoll非阻塞IO。epoll是目前最好的多路复用技术,减少网络IO的时间消耗
注:这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程
redis支持的数据类型:string(字符串),hash(哈希),list(列表),set(集合),zset(有序集合)
扩展类型 HyperLogLog,geo,bitmap
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 上线的总天数。
热点数据缓存,秒杀,计数器,并发锁,消息队列,分布式session,限时/限数(验证码过期,错误次数等),防爬虫(请求次数在单位时间内超过制定数组则可认为被爬虫采集)。除此之外,redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案。
Redis 事务的执行并不是原子性的。事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
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 # 取消监视
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(); //提交管道里操作命令
redis 提供的发布/订阅功能只是一个低配版的,无法对消息持久化存储,一旦消息被发送,如果没有订阅者接收,那么消息就会丢失,Redis并没有提供消息传输保障。所以,如果系统中已经有了Redis,并且只需要基本的发布订阅功能,对消息的安全性没有高要求,那就可以使用redis的发布/订阅功能
subscribe news
publish news hello
# unsubscribe 取消订阅
使用系统提供的阻塞原语,在新元素到达时立即进行处理,而新元素还没到达时,就一直阻塞住,避免轮询占用资源。也可以实现类似于抢票的功能。
brpop ticket 30 # 0表示一直等待
lpush ticket home
RDB持久化是把当前进程数据生成快照保存到硬盘的过程,触发RDB持
久化过程分为手动触发(save)和自动触发(bgsave)。
save 900 1 # 900秒之内,如果超过1个key被修改,则发起快照保存;
save 300 10 # 300秒内,如果超过10个key被修改,则发起快照保存;
save 60 10000 # 1分钟之内,如果1万个key被修改,则发起快照保存;
优点:
缺点:
AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的。
AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式。
aof 工作流程:
1、将命令转换成协议文本。
2、所有的写入命令会追加到aof_buf(缓冲区)中。
3、AOF缓冲区根据对应的策略向硬盘做同步操作 everysec。
4、随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。
- REWRITE: 在主线程中重写AOF,会阻塞工作线程,在生产环境中很少使用,处于废弃状态;
- 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,具体流程如下:
系统运行期间同时存在两个AOF文件,一个是当前正在写的AOF,另一个是存量的AOF数据文件。因此需要修改数据库恢复相关逻辑,加载AOF时先要加载存量数据appendonly.aof.last,再加载appendonly.aof。
最多丢失1秒还是2秒的数据?
在主从复制的概念中,数据库分为两类,一类是主数据库(master),另一类是从数据库[1] (slave)。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。
优点:
缺点:
当maser宕机后,需要人工参与
1)在Slave1上执slaveof no one命令提升Slave1为新的Master节点。
2)在Slave1上配置为可写,这是因为大多数情况下,都将slave配置只读。
3)告诉Client端(也就是连接Redis的程序)新的Master节点的连接地址(改代码)。
4)配置Slave2从新的Master进行数据复制。 ==> (sentinel )
虽然实现了读写分离,但是主节点的性能会成为主要瓶颈 ==>(集群 cluster)
`Redis Sentinel` 为 `Redis` 提供了高可用方案。从实践方面来说,使用 `Redis Sentinel` 可以创建一个无需人为干预就可以预防某些故障的 `Redis` 环境。实现自动切换。在部署 `Sentinel` 的时候,建议使用奇数个 `Sentinel` 节点,最少三个 `Sentinel` 节点。
功能:
1、 监控,Sentinel 会不断的检查 master 和 slave 是否像预期那样正常运行。
2、通知,通过 API,Sentinel 能够通知系统管理员、程序监控的 Redis 实例出现了故障。
3、自动故障转移,如果 master 不像预想中那样正常运行,Sentinel 可以启动故障转移过程,其中的一个 slave 会提成为 master,其它 slave 会重新配置来使用新的 master,使用Redis 服务的应用程序,当连接时,也会被通知使用新的地址。
4、配置提供者,Sentinel 可以做为客户端服务发现的认证源:客户端连接 Sentinel 来获取目前负责给定服务的 Redis master 地址。如果发生故障转移,Sentinel 会报告新的地址。
1、每 10 秒每个 sentinel 对 master 和 slave 执行 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 ID 的 Slave 作为新的 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文件所在地
# 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容器
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
原文:https://www.cnblogs.com/yxhblogs/p/12714374.html