redis更新数据失败进异常吗_Redis-经验谈

前言

本文主要是对Redis使用过程中遇到的问题进行总结。

ca198aa6956979d0e3ea1305751869fd.png

Redis是使用 C 语言写成的 一个开源的,基于内存的结构化数据存储媒介,可以作为数据库、缓存服务或消息服务使用。Redis支持多种数据结构,包括字符串、哈希表、链表、集合、有序集合等。Redis具备LRU淘汰、事务实现、以及不同级别的硬盘持久化等能力,并且支持副本集和通过Redis Sentinel实现的高可用方案,同时还支持通过Redis Cluster实现的数据自动分片能力。

特点

Redis的主要功能都基于单线程模型实现,也就是说Redis使用一个线程来服务所有的客户端请求,同时Redis采用了非阻塞式IO,并精细地优化各种命令的算法时间复杂度,这些信息意味着:

  • Redis是线程安全的(因为只有一个线程),其所有操作都是原子的,不会因并发产生数据异常。
  • Redis的速度非常快(因为使用非阻塞式IO,且大部分命令的算法时间复杂度都是O(1))。
  • 使用高耗时的Redis命令是很危险的,会占用唯一的一个线程的大量处理时间,导致所有的请求都被拖慢 。

过期策略

采用的 定期删除+惰性删除

定期删除: redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。

惰性删除: 也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。

采用定期删除+惰性删除就完美了吗? 考虑一种情况,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制

淘汰策略

在32位OS中,Redis最大使用3GB的内存,在64位OS中则没有限制。

  1. volatile-lru:从已设置过期时间的数据集(淘汰上次使用时间最早的,且使用次数最少的key )中挑选最近最少使用的数据淘汰
  2. volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰
  3. volatile-random:从已设置过期时间的数据集中任意选择数据淘汰
  4. allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
  5. allkeys-random:从数据集中任意选择数据淘汰
  6. no-enviction(驱逐):不进行数据淘汰

持久化

Redis提供了将数据定期自动持久化至硬盘的能力,包括RDB和AOF两种方案,两种方案分别有其长处和短板,可以配合起来同时运行,确保数据的稳定性。

RDB

采用RDB持久方式,Redis会定期保存数据快照至一个rbd文件中,并在启动时自动加载rdb文件,恢复之前保存的数据。

优点:

  • 对性能影响最小。如前文所述,Redis在保存RDB快照时会fork出子进程进行,几乎不影响Redis处理客户端请求的效率。
  • 每次快照会生成一个完整的数据快照文件,所以可以辅以其他手段保存多个时间点的快照(例如把每天0点的快照备份至其他存储媒介中),作为非常可靠的灾难恢复手段。
  • 使用RDB文件进行数据恢复比使用AOF要快很多。

缺点:

  • 快照是定期生成的,所以在Redis crash时或多或少会丢失一部分数据。
  • 如果数据集非常大且CPU不够强(比如单核CPU),Redis在fork子进程时可能会消耗相对较长的时间(长至1秒),影响这期间的客户端请求。

AOF

采用AOF持久方式时,Redis会把每一个写请求都记录在一个日志文件里。在Redis重启时,会把AOF文件中记录的所有写操作顺序执行一遍,确保数据恢复到最新。

AOF提供了三种fsync配置,always/everysec/no,通过配置项[appendfsync]指定:

  • appendfsync no:不进行fsync,将flush文件的时机交给OS决定,速度最快。
  • appendfsync always:每写入一条日志就进行一次fsync操作,数据安全性最高,但速度最慢。
  • appendfsync everysec:折中的做法,交由后台线程每秒fsync一次。

优点:

  • 最安全,在启用appendfsync always时,任何已写入的数据都不会丢失,使用在启用appendfsync everysec也至多只会丢失1秒的数据。
  • AOF文件在发生断电等问题时也不会损坏,即使出现了某条日志只写入了一半的情况,也可以使用redis-check-aof工具轻松修复。
  • AOF文件易读,可修改,在进行了某些错误的数据清除操作后,只要AOF文件没有rewrite,就可以把AOF文件备份出来,把错误的命令删除,然后恢复数据。

缺点

  • AOF文件通常比RDB文件更大。
  • 性能消耗比RDB高。
  • 数据恢复速度比RDB慢。

数据类型

  • String 常规的set/get操作,value可以是String也可以是数字
  • hash value存放的是结构化的对象
  • list 使用List的数据结构
  • set 类似Java set集合,存放的是不重复值的集合
  • sorted set 权重参数score,集合中的元素能够按score进行排列。

事务

通过MULTI和EXEC命令

Redis在接收到MULTI命令后便会开启一个事务,这之后的所有读写命令都会保存在队列中但并不执行,直到接收到EXEC命令后,Redis会把队列中的所有命令连续顺序执行,并以数组形式返回每个命令的返回结果。

注意:Redis事务不支持回滚

CAS

Redis提供了WATCH命令与事务搭配使用,实现CAS乐观锁的机制。

WATCH的机制:在事务EXEC命令执行时,Redis会检查被WATCH的key,只有被WATCH的key从WATCH起始时至今没有发生过变更,EXEC才会被执行。如果WATCH的key在WATCH命令到EXEC命令之间发生过变化,则EXEC命令会返回失败。

Script

通过EVAL与EVALSHA命令,可以让Redis执行LUA脚本。 Redis官方推荐使用LUA Script来代替事务 ,请参考官方文档 。

Redis为什么这么快

  1. 纯内存操作
  2. 单线程操作,避免了频繁的上下文切换
  3. 采用了非阻塞I/O多路复用机制 ,只有1个线程,通过跟踪每个I/O流的状态来管理多个I/O流 。

实战问题

数据库双写一致性问题

一致性问题是分布式常见问题,还可以再分为最终一致性和强一致性。数据库和缓存双写,就必然会存在不一致的问题。如果对数据有强一致性要求,不能放缓存。我们所做的一切,只能保证最终一致性。另外,我们所做的方案其实从根本上来说是降低不一致发生的概率,无法完全避免。因此,有强一致性要求的数据,不能放缓存。

采取正确更新策略,先更新数据库,再删缓存。其次,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。

缓存穿透

去请求缓存中不存在的数据,导致所有的请求压力都到数据库上,从而数据库连接异常或者崩溃。

  1. 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试
  2. 采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
  3. 提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。

缓存雪崩

缓存同一时间大面积的失效,恰好这时候又来了一波请求,请求压力都到数据库上,从而导致数据库连接异常。

  1. 给缓存的失效时间,加上一个随机值,避免集体失效。
  2. 使用互斥锁,但是该方案可能会导致吞吐量明显下降了。
  3. 采用双缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间 。

并发竞争

Redis为单进程单线程模式,采用队列模式将并发访问变为串行访问。

Redis本身没有锁的概念,Redis对于多个客户端连接并不存在竞争,但是在Jedis客户端对Redis进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是由于客户端连接混乱造成。

解决方法:

  1. 客户端角度,为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。
  2. 服务器角度,利用setnx实现锁。

当同时有多个子系统去set一个key。这时候可能会发生问题。

  1. redis事务机制, 如果是redis集群环境 ,这多个key不一定都存储在同一个redis-server上。该方案会失效。
  2. 分布式锁,利用setnx实现锁
  3. 利用队列,将set方法变成串行访问。

大量数据插入

客户端执行大量数据插入时候,一个个的插入会有大量的时间浪费在每一个命令往返时间上。

可以使用Redis提供的pipelining功能来实现在一次交互中执行多条命令。

常用应用场景

  • 排行榜类的应用,取TOP N操作,前面操作以时间为权重,这个是以某个条件为权重,比如按顶的次数排序计数器应用
  • 存储关系:比如社交关系,比如Tag等
  • 获取某段时间所有数据排重值,使用set,比如某段时间访问的用户ID,或者是客户端IP
  • 构建队列系统,List可以构建栈和队列,使用zset可以构建优先级队列

Redis与Memcached

比较:

466f793bf688b20e66364716adbff1d4.png
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值