首页 > 应用开发 > Java > java开发面试之Redis高并发处理
Redis 常见性能问题和解决方案如下:
因为内存的空间是有限的,所以 Redis 淘汰机制主要为了解决在某个时间点,Redis 中存在很多过期键,定期删除策略 随机抽查时没有抽查到,并且也没有走惰性删除策略时,大量过期键占用内存的问题。如果内存只能存 20w 数据,而我们需要存储 2000w 的数据时,自然就需要对多出来的数据进行删除或覆盖,保证内存中存储的数据都是热数据。所以当 Redis 内存数据集的大小上升到一定数量时,就会执行数据淘汰策略。
我们可以使用 keys 命令和 scan 命令,但是会发现使用 scan 更好。
「1. 使用 keys 命令」
直接使用 keys 命令查询,但是如果是在生产环境下使用会出现一个问题,keys 命令是遍历查询的,查询的时间复杂度为 O(n),数据量越大查询时间越长。而且 Redis 是单线程,keys 指令会导致线程阻塞一段时间,会导致线上 Redis 停顿一段时间,直到 keys 执行完毕才能恢复。这在【关注尚硅谷,轻松学IT】生产环境是不允许的。除此之外,需要注意的是,这个命令没有分页功能,会一次性查询出所有符合条件的 key 值,会发现查询结果非常大,输出的信息非常多。所以不推荐使用这个命令。
「2. 使用 scan 命令」
scan 命令可以实现和 keys 一样的匹配功能,但是 scan 命令在执行的过程不会阻塞线程,并且查找的数据可能存在重复,需要客户端操作去重。因为 scan 是通过游标方式查询的,所以不会导致 Redis 出现假死的问题。Redis 查询过程中会把游标返回给客户端,单次返回空值且游标不为 0,则说明遍历还没结束,客户端继续遍历查询。scan 在检索的过程中,被删除的元素是不会被查询出来的,但是如果在迭代过程中有元素被修改,scan 不能保证查询出对应元素。相对来说,scan 指令查找花费的时间会比 keys 指令长。
大量的请求瞬时涌入系统,而这个数据在 Redis 中不存在,所有的请求都落到了数据库上把数据库打死。造成这种情况的原因有系统设计不合理、缓存数据更新不及时,或爬虫等恶意攻击。解决办法有:
「1. 使用布隆过滤器」
将查询的参数都存储到一个 bitmap 中,在查询缓存前,再找个新的 bitmap,在里面对参数进行验证。如果验证的 bitmap 中存在,则进行底层缓存的数据查询,如果 bitmap 中不存在查询参数,则进行拦截,不再进行缓存的数据查询。
「2. 缓存空对象」
如果从数据库查询的结果为空,依然把这个结果进行缓存,那么当用 key 获取数据时,即使数据不存在,Redis 也可以直接返回结果,避免多次访问数据库。
但是缓存空值的缺点是:
缓存雪崩是指当大量缓存失效时,大量的请求访问直接请求数据库,导致数据库服务器无法抗住请求或挂掉的情况。这时网站常常会出现 502 错误,导致网站不可用问题。
在预防缓存雪崩时,建议遵守以下几个原则:
如果有大量的 key 在同一时间过期,那么可能同一秒都从数据库获取数据,给数据库造成很大的压力,导致数据库崩溃,系统出现 502 问题。也有可能同时失效,那一刻不用都访问数据库,压力不够大的话,那么 Redis 会出现短暂的卡顿问题。所以为了预防这种问题的发生,最好给数据的过期时间加一个随机值,让过期时间更加分散。
Redis 的哨兵作用是管理多个 Redis 服务器,提供了监控、提醒以及自动的故障转移的功能。哨兵可以保证当主服务器挂了后,可以从从服务器选择一台当主服务器,把别的从服务器转移到读新的主机。Redis 哨兵的主要功能有:
Redis 的集群的功能是为了解决单机 Redis 容量有限的问题,将数据按一定的规则分配到多台机器,对内存的每秒访问不受限于单台服务器,可受益于分布式集群高扩展性。
哨兵是 Redis 集群架构中非常重要的一个组件,主要功能如下:
Redis 产生阻塞的原因主要有内部和外部两个原因导致:
「1. 内部原因」
「2. 外部原因」
外部原因主要是服务器的原因,例如服务器的 CPU 线程在切换过程中竞争过大,内存出现问题、网络问题等。
Redis 可以使用主从同步、从从同步。第一次同步时,主节点做一次 bgsave,并同时将后续修改操作记录到内存 buffer,待完成后将 RDB 文件全量同步到复制节点,复制节点接受完成后将 RDB 镜像加载到内存。
加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。
对于 big key 先分析业务存在大键值是否合理,如果不合理我们可以把它们拆分为多个小的存储。或者看是否可以使用别的存储介质存储这种 big key 解决占用内存空间大的问题。
对于 hot key 我们可以在其他机器上复制这个 key 的备份,让请求进来的时候,去随机的到各台机器上访问缓存。所以剩下的问题就是如何做到让请求平均的分布到各台机器上。
可以从以下两方面准备:
「1. 使用 Redis 自身监控系统」
使用 Redis 自身监控系统,可以对 CPU、内存、磁盘、命令耗时等阻塞问题进行监控,当监控系统发现各个监控指标存在异常的时候,发送报警。
「2. 使用应用服务监控」
当 Redis 存在阻塞时,应用响应时间就会延长,应用可以感知发现问题,并发送报警给管理人员。
客户端分区就是在客户端就已经决定数据会被存储到哪个 Redis 节点或者从哪个 Redis 节点读取。大多数客户端已经实现了客户端分区。
代理分区意味着客户端将请求发送给代理,然后代理决定去哪个节点写数据或者读数据。代理根据分区规则决定请求哪些 Redis 实例,然后根据 Redis 的响应结果返回给客户端。Redis 和 Memcached 的一种代理实现就是 Twemproxy。
查询路由(Query routing)的意思是客户端随机地请求任意一个 Redis 实例,然后由 Redis 将请求转发给正确的 Redis 节点。
Redis Cluster 实现了一种混合形式的查询路由,但并不是直接将请求从一个 Redis 节点转发到另一个 Redis 节点,而是在客户端的帮助下直接 redirected 到正确的 Redis 节点。
需要知道 Redis 的复制方式前,需要知道主从复制(Master-Slave Replication)的工作原理,具体为:
整个执行的过程都是使用异步复制的方式进行复制。
当一个数据需要更新时,因为不可能做到同时更新数据库和缓存,那么此时读取数据的时候就一定会发生数据不一致问题,而数据不一致问题在金融交易领域的系统中是肯定不允许的。
解决办法:读的时候先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。更新的时候,先更新数据库,然后再删除缓存。
分区可以让 Redis 管理更大的内存,Redis 将可以使用所有机器的内存。如果没有分区,你最多只能使用一台机器的内存。
分区使 Redis 的计算能力通过简单地增 加计算机得到成倍提升,Redis 的网络带宽也会随着计算机和网卡的增加而成倍增长。
为防止以后扩容增加难度,最好的办法就是一开始就使用分布式。即便只有一台服务器,也可以一开始就让 Redis 以分布式的方式运行,使用分区,在同一台服务器上启动多个实例。
刚开始操作时比较麻烦,但是当数据不断增长,需要更多的 Redis 服务器时,只需要将 Redis 实例从一台服务迁移到另外一台服务器即可,而不用考虑重新分区的问题。一旦添加了另一台服务器,只需要将一半的 Redis 实例从第一台机器迁移到第二台机器。
如果大量的 key 过期时间设置的过于集中,到过期的那个时间点,redis 可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在单机或者单进程环境下,多线程并发的情况下,使用锁来保证一个代码块在同一时间内只能由一个线程执行。比如 Java 的 Synchronized 关键字和 Reentrantlock 类。
分布式锁的作用是当多个进程不在同一个系统中,用分布式锁可以控制多个进程对资源的访问。
分布式锁实现需要保证以下几点:
文章来源:YP小站
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70010293/viewspace-2852207/ ,如需转载,请注明出处,否则将追究法律责任。