Redis的KEYS命令引起RDS数据库雪崩,RDS发生两次宕机,造成几百万的资金损失

最近的互联网线上事故发生比较频繁,20180919顺丰发生了一起线上删库事件,在这里就不介绍了。

在这里讲述一下最近发生在我公司的事故,以及如何避免,并且如何处理优化。

间接原因还有很多,技术跟不上业务的发展,由每日百万量到千万级是一个大的跨进,公司对于系统优化的处理优先级不高,技术开发人手的短缺

第一次宕机
20180913某个点,公司某服务化项目的RDS实例连接飙升,CPU升到100%,拒绝了其他应用的所有请求服务
整个过程如下:

  1. 监控报警,显示RDS的CPU使用率达到80%以上,DBA介入,准备KILL慢SQL
  2. 1分钟内,没有发现明显阻塞的SQL,CPU持续上升到99%

3 .5分钟内,大量应用报警,并且拒绝服务,RDS的监控显示出现大量慢SQL,联系服务器数据库提供商进行协助

  1. 8分钟内,进行数据库主备切换(业务会受损,但是也没办法,没有定位到问题)
  2. 9分钟内,部分业务恢复,但是一些业务订单的回调消息堆积超过20w,备库的CPU使用率也持续上升
  3. 15分钟内,备库CPU使用率超过97%,业务再次中断,进行切回主库,并进行限流
  4. 20分钟内,关闭一些次要应用的流量入口
  5. 25分钟内,主库CPU使用率恢复正常
  6. 30分钟内,逐步开启关闭的限流应用
  7. 35分钟内,所有应用恢复正常
  8. 接下来就是与服务器数据库提供商成立应急小组紧急优化可能出现的慢SQL,虽然说可能解决了一些慢SQL,但此次并没有定位到具体的问题,也就为几天后再次发生宕机事件埋下了伏笔

事故影响
某服务化项目服务不可用几十分钟,造成订单数减少几十万笔,损失百万资金

原因分析
当时是没有定位到具体的原因的,但是下面的原因也是一部分可能引起宕机的情况。
某服务化项目的业务增速非常快,在高峰期,数据库QPS突破35000,系统处于高负荷状态。
在高峰期如果同时执行几个全表扫描的SQL,会造成数据库压力急剧上升,应用超时增多,前端应用超时,用户重试,流量飙升,形成了雪崩效应。
主要原因在与一些老项目的SQL查询性能较差,并且使用的主库,对数据库影响较大。数据库QPS太高,但是缓存方案因为人手原因一直没有落地,慢SQL的问题处理优先级应该提升

改进方案

  1. 针对每个应用建一个数据库账号,严格按照规范使用
  2. 缓存优化方案即时落地,慢SQL问题优先处理,集中处理目前已经发现的慢SQL(查询时间超过1S)
  3. 升级数据库配置
  4. 迁移非核心业务到新的RDS实例中去

第二次宕机
由于上一次的宕机原因未找到,所以此次的宕机是可以预见的
20180919,还是一样的"配方",还是原来的"味道"。同一个RDS,CPU飙升至100%,接下来就是拒绝服务,宕机。当然,有了第一次的经验,直接主从切换,在几十秒左右就恢复了所有业务,但还是严重影响了公司的业务和形象

原因分析
恢复业务后,公司紧急召开了紧急事故研究会议,当然,我的级别是参与不了的。公司的高管,高层技术架构、DBA、各个项目的主负责人一起进行了会议。
在此次会议中,经过查看各个项目的日志,后台的监控数据,发现在那台RDS数据库CPU飙升时,有一台Redis数据库内存将近100%,然后急剧下降。联系第一次的宕机情况,也是类似的。
接下来就是联系服务器数据库提供商,将那台Redis最近一周的命令全部调用出来,最后发现,在那个时间点运行了一条keys ...命令。公司的一个工程师执行keys模糊的匹配命令是为了清理没用的键,但是没有考虑到keys *进行模糊匹配引发Redis锁,造成Redis锁住,CPU飙升,引起了所有调用链路的超时并且卡住,等Redis锁的那几秒结束,所有的请求流量全部请求到RDS数据库中,使数据库产生了雪崩,使数据库宕机。

改进方案

  1. 所有线上操作,全部要经过运维通过后方可执行,运维部门逐步快速收回各项权限
  2. 新增Redis实例,进行分离
  3. 如果有使用类似keys正则命令需求,使用scan命令代替

总结
该事件中出现的两次事故,完全是由于人为操作引起的,如果那位工程师,看过Redis的开发规范,会发现是建议禁用keys命令的。另外,有线上的命令操作,一定要经过运维评估后方可进行操作,估计那个工程师是老员工吧,有权限,然后直接就进行操作了
另外,公司的业务发展确实很快,技术跟不上,这是非常非常危险的,极大的增加了宕机的概率
在业务量不大的情况下,那位工程师的操作是完全没什么问题的,毕竟并发也不大,但是现在,随着公司的发展,业务量的成倍成倍增加,技术的扩展却没有随着增长那么快
公司的技术人手不足也是一方面,绝大多数人都是边维护老项目边做新功能,但是对于项目的重构优化,人手却少了很多,项目优化的优先级不高,这也是很大的一个原因,极有可能出现类似的情况,新服务化构建迫在眉睫

最后的最后,线上操作的任何一条命令,再小心也不为过
因为由于你的一个符号而引起的事故可能是你所承担不起的

Redis开发建议
最后附上Redis的一些开发规范和建议

1.冷热数据分离,不要将所有数据全部都放到Redis中
虽然Redis支持持久化,但是Redis的数据存储全部都是在内存中的,成本昂贵。建议根据业务只将高频热数据存储到Redis中【QPS大于5000】,对于低频冷数据可以使用MySQL/ElasticSearch/MongoDB等基于磁盘的存储方式,不仅节省内存成本,而且数据量小在操作时速度更快、效率更高!

2.不同的业务数据要分开存储
不要将不相关的业务数据都放到一个Redis实例中,建议新业务申请新的单独实例。因为Redis为单线程处理,独立存储会减少不同业务相互操作的影响,提高请求响应速度;同时也避免单个实例内存数据量膨胀过大,在出现异常情况时可以更快恢复服务! 在实际的使用过程中,redis最大的瓶颈一般是CPU,由于它是单线程作业所以很容易跑满一个逻辑CPU,可以使用redis代理或者是分布式方案来提升redis的CPU使用率。

3.存储的Key一定要设置超时时间
如果应用将Redis定位为缓存Cache使用,对于存放的Key一定要设置超时时间!因为若不设置,这些Key会一直占用内存不释放,造成极大的浪费,而且随着时间的推移会导致内存占用越来越大,直到达到服务器内存上限!另外Key的超时长短要根据业务综合评估,而不是越长越好!

4.对于必须要存储的大文本数据一定要压缩后存储
对于大文本【+超过500字节】写入到Redis时,一定要压缩后存储!大文本数据存入Redis,除了带来极大的内存占用外,在访问量高时,很容易就会将网卡流量占满,进而造成整个服务器上的所有服务不可用,并引发雪崩效应,造成各个系统瘫痪!

5.线上Redis禁止使用Keys正则匹配操作
Redis是单线程处理,在线上KEY数量较多时,操作效率极低【时间复杂度为O(N)】,该命令一旦执行会严重阻塞线上其它命令的正常请求,而且在高QPS情况下会直接造成Redis服务崩溃!如果有类似需求,请使用scan命令代替!

6.可靠的消息队列服务
Redis List经常被用于消息队列服务。假设消费者程序在从队列中取出消息后立刻崩溃,但由于该消息已经被取出且没有被正常处理,那么可以认为该消息已经丢失,由此可能会导致业务数据丢失,或业务状态不一致等现象发生。
为了避免这种情况,Redis提供了RPOPLPUSH命令,消费者程序会原子性的从主消息队列中取出消息并将其插入到备份队列中,直到消费者程序完成正常的处理逻辑后再将该消息从备份队列中删除。同时还可以提供一个守护进程,当发现备份队列中的消息过期时,可以重新将其再放回到主消息队列中,以便其它的消费者程序继续处理。

7.谨慎全量操作Hash、Set等集合结构
在使用HASH结构存储对象属性时,开始只有有限的十几个field,往往使用HGETALL获取所有成员,效率也很高,但是随着业务发展,会将field扩张到上百个甚至几百个,此时还使用HGETALL会出现效率急剧下降、网卡频繁打满等问题【时间复杂度O(N)】,此时建议根据业务拆分为多个Hash结构;或者如果大部分都是获取所有属性的操作,可以将所有属性序列化为一个STRING类型存储!同样在使用SMEMBERS操作SET结构类型时也是相同的情况!

8.根据业务场景合理使用不同的数据结构类型
目前Redis支持的数据库结构类型较多:字符串(String),哈希(Hash),列表(List),集合(Set),有序集合(Sorted Set), Bitmap, HyperLogLog和地理空间索引(geospatial)等,需要根据业务场景选择合适的类型。
常见的如:String可以用作普通的K-V、计数类;Hash可以用作对象如商品、经纪人等,包含较多属性的信息;List可以用作消息队列、粉丝/关注列表等;Set可以用于推荐;Sorted Set可以用于排行榜等!

9.命名规范
虽然说Redis支持多个数据库(默认32个,可以配置更多),但是除了默认的0号库以外,其它的都需要通过一个额外请求才能使用。所以用前缀作为命名空间可能会更明智一点。
另外,在使用前缀作为命名空间区隔不同key的时候,最好在程序中使用全局配置来实现,直接在代码里写前缀的做法要严格避免,这样可维护性实在太差了。
如:系统名:业务名:业务数据:其他
但是注意,key的名称不要过长,尽量清晰明了,容易理解,需要自己衡量

10.线上禁止使用monitor命令
禁止生产环境使用monitor命令,monitor命令在高并发条件下,会存在内存暴增和影响Redis性能的隐患

11.禁止大string
核心集群禁用1mb的string大key(虽然redis支持512MB大小的string),如果1mb的key每秒重复写入10次,就会导致写入网络IO达10MB;

12.redis容量
单实例的内存大小不建议过大,建议在10~20GB以内。
redis实例包含的键个数建议控制在1kw内,单实例键个数过大,可能导致过期键的回收不及时。

13 可靠性
需要定时监控redis的健康情况:使用各种redis健康监控工具,实在不行可以定时返回redis 的 info信息。
客户端连接尽量使用连接池(长链接和自动重连)

原文地址:http://blog.51cto.com/13961945/2178688

时间: 2024-10-28 15:48:44

Redis的KEYS命令引起RDS数据库雪崩,RDS发生两次宕机,造成几百万的资金损失的相关文章

Redis架构之防雪崩设计:网站不宕机背后的兵法

Redis架构之防雪崩设计:网站不宕机背后的兵法 原创: 付磊,张益军 高可用架构 2017-03-24 导读:互联网系统中不可避免要大量用到缓存,在缓存的使用过程中,架构师需要注意哪些问题?本文以 Redis 为例,详细探讨了最关键的 3 个问题. 一.缓存穿透预防及优化 缓存穿透是指查询一个根本不存在的数据,缓存层和存储层都不会命中,但是出于容错的考虑,如果从存储层查不到数据则不写入缓存层,如图 11-3 所示整个过程分为如下 3 步: 缓存层不命中 存储层不命中,所以不将空结果写回缓存 返

面试官:Redis 数据库内存数据满了,会宕机吗?有内存回收?

Redis 数据库内存数据满了,会宕机吗?答案是:不会让他出现存满的情况,在使用Redis的时候我们要配置Redis能使用的最大的内存大小,存到一定容量的时候还有Redis的内存淘汰策略呢,还有LRU算法进行淘汰,等等...接下来就跟着作者一起探讨,Redis的内存淘汰策略. Redis占用内存大小 我们知道Redis是基于内存的key-value数据库,因为系统的内存大小有限,所以我们在使用Redis的时候可以配置Redis能使用的最大的内存大小. 1.通过配置文件配置 通过在Redis安装目

由Redis的hGetAll函数所引发的一次服务宕机事件

昨晚通宵生产压测,终于算是将生产服务宕机的原因定位到了,心累.这篇博客,算作一个复盘和记录吧... 先来看看Redis的缓存淘汰算法思维导图: 说明:当实际占用的内存超过Redis配置的maxmemory时,Redis就会根据用户选择淘汰策略清除被选中的key. 业务场景:用户通过微信入口来访问一个页面: 测试场景:通过多线程模拟定量的并发来访问页面服务: 涉及架构:springsession+Redis集群,容器部署: 问题描述:固定并发数压测10分钟,压测开始后半小时,Redis连接数激增,

Redis学习笔记之五:redis keys命令 (24个)

Redis 版本:3.2.100 Redis 命令 Redis 命令用于在 redis 服务上执行操作. 要在 redis 服务上执行命令需要一个 redis 客户端. 一.客户端连接服务器 基本语法: redis-cli [-h host -p port -a password] 不带参数,则默认连接本地,带上三个参数,则连接远程服务器. 举例: 如何连接到主机为 127.0.0.1,端口为 6379 ,密码为 mypass 的 redis 服务上. redis-cli -h 127.0.0.

redis keys 命令

## 删除存在的key del key ## 序列体弱给定key,并返回被序列化的值 dump key ## 检查key是否存在 exists key ## 为给定key设置过期时间 expire key seconds ## EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置过期时间. 不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp). EXPIREAT key timestamp ##设置 key 的过期时间以毫秒计 PE

二、Redis KEY相关命令

Redis官网:https://redis.io 参考书籍:<Redis实战-红丸出品> 参考网址:http://redisdoc.com/index.html 1.查询当前数据库中的KEY个数 192.168.246.154>dbsize 2.查询当前数据库中的KEY 192.168.246.154:6379>keys pattren keys命令可以指定匹配模式,会返回所有符合给定模式pattern的KEY.常用的有: keys "*",当前数据库下的所有K

NoSQL之Redis高级实用命令详解--安全和主从复制

Android IOS JavaScript HTML5 CSS jQuery Python PHP NodeJS Java Spring MySQL MongoDB Redis NOSQL Vim C++ C# JSON Ruby Linux Nginx Docker 所有分类  >  数据库相关  >  NoSQL数据库 NoSQL之Redis高级实用命令详解--安全和主从复制 NoSQL Redis   2014-01-09 22:52:47 发布 您的评价:       0.0   收

redis 模糊查找keys

Redis入门教程可参考:超强.超详细Redis数据库入门教程 Redis操作命令可参考:Redis操作命令总结 redis可以通过命令Keys Match来进行键值的模糊匹配,借助StackExchange.Redis时,由于Dll内部没有实现Keys Match的操作,不过,StackExchange.Redis提供了直接执行Lua语句的入口: RedisResult ScriptEvaluate(LoadedLuaScript script, object parameters = nul

redis运维命令及参数整理

redis作为一个服务,和普通的oracle,mysql服务器运维维护和系统调优是一样的. 关注内存,CPU,网络IO,阻塞等.由于不会涉及复杂的SQL业务逻辑,缺少SQL调优这一块. 1.通过INFO命令,要清楚了解服务器的现状.参照:http://redisdoc.com/server/info.html section server 记录了 Redis 服务器的信息 clients 记录了已连接客户端的信息(确认并发情况,繁忙程度) memory 记录了服务器的内存信息   (确认内存消耗