执行过程按顺序执行,不会同时执行多个操作,保证操作的原子性,省去了很多上下文切换线程的时间,不必考虑资源竞争和可能出现死锁
为什么使用单线程 ?
官方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