• [技术干货] Redis中的两种持久化方案:RDB与AOF
    前言Redis,作为一种高性能的键值对数据库,被广泛应用于各种场景中。然而,由于其数据存储在内存中,一旦Redis服务器发生宕机或故障,数据将会丢失。为了解决这个问题,Redis提供了两种持久化方案:RDB(Redis DataBase)和AOF(Append Only File)。本文将深入探讨这两种持久化方案的区别,并给出何时使用哪种的建议。1. RDB持久化RDB持久化通过生成数据快照的方式,将某一时刻的Redis数据持久化到磁盘中。Redis会定期(如每隔一段时间或当数据达到一定数量)执行一次快照操作,生成一个二进制的RDB文件。当Redis重启时,会加载这个RDB文件来恢复数据。优势:RDB文件紧凑,适合用于数据备份和传输。数据恢复速度快,因为Redis只需要加载RDB文件到内存中即可。在某些场景下,如数据集较大且变化不频繁时,RDB能提供较好的性能。劣势:RDB是定时生成快照的,因此可能会丢失最近一次快照之后的数据。在数据频繁更新的场景下,RDB可能会带来较大的性能开销,因为每次生成快照都需要遍历整个数据集。2. AOF持久化AOF持久化通过记录服务器接收到的所有写操作命令,将这些命令以文本形式追加到一个AOF文件中。当Redis重启时,会重新执行AOF文件中的命令来恢复数据。优势:AOF提供了更高的数据安全性,因为即使Redis宕机,也只会丢失最近一次写入命令之后的数据。AOF文件易于理解和解析,方便进行故障排查和数据恢复。在数据频繁更新的场景下,AOF的性能开销相对较小,因为它只记录变化的命令,而不是整个数据集。劣势:AOF文件通常比RDB文件大,因为AOF记录了所有的写操作命令。数据恢复速度相对较慢,因为Redis需要逐条执行AOF文件中的命令来恢复数据。何时使用RDB,何时使用AOF?选择哪种持久化方案,取决于你的具体需求和场景。以下是一些建议:如果你更关心数据的安全性,希望尽可能减少数据丢失的风险,那么AOF是一个更好的选择。因为AOF记录了所有的写操作命令,即使Redis宕机,也只会丢失最近一次写入命令之后的数据。如果你更关心数据恢复的速度,希望在Redis重启时能够尽快恢复数据,那么RDB可能更适合你。因为RDB文件紧凑,加载速度快,可以更快地恢复数据。你也可以同时开启RDB和AOF两种持久化方式,以实现更高的数据安全性和更快的数据恢复速度。但需要注意的是,这会增加磁盘空间的占用和性能开销。总之,RDB和AOF各有优缺点,选择哪种持久化方案取决于你的具体需求和场景。在实际应用中,建议根据实际情况进行选择和调整,以达到最佳的数据安全性和性能平衡。
  • [技术干货] 如何防止Redis数据丢失
    Redis定义Redis是一种高性能的键值对数据库,广泛应用于各种场景,如缓存、消息队列、计数器等。然而,由于其内存存储的特性,数据丢失的风险始终存在。为了保障数据安全,我们需要采取一系列措施来防止Redis数据丢失。1. 持久化Redis提供了两种持久化方式:RDB(Redis DataBase)和AOF(Append Only File)。RDB:通过定时快照的方式,将某一时刻的数据持久化到磁盘。这种方式在数据恢复时速度较快,但可能会丢失最近一次快照之后的数据。AOF:记录服务器接收到的所有写操作命令,并在服务器启动时,通过重新执行这些命令来恢复数据。这种方式数据安全性更高,但恢复速度较慢。在实际应用中,我们可以结合两种持久化方式,既保证数据的安全性,又尽量提高数据恢复的速度。2. 备份与恢复除了Redis自身的持久化机制外,我们还需要定期进行数据备份,以防止硬件故障、自然灾害等不可预见因素导致的数据丢失。备份时,应将RDB和AOF文件都进行备份,并存储在安全可靠的地方。当数据丢失时,可以通过以下步骤进行恢复:停止Redis服务。清除当前的数据文件(谨慎操作,以防误删)。从备份中恢复RDB和AOF文件。重新启动Redis服务,数据将自动从备份文件中恢复。3. 监控与告警为了确保Redis的稳定运行,我们需要实时监控Redis的各项指标,如内存使用情况、连接数、命令执行速度等。当发现异常时,应及时进行排查和处理。同时,我们可以设置告警机制,当Redis的关键指标超过预设阈值时,自动发送告警通知给管理员,以便及时处理问题。4. 灾备与容灾为了应对可能发生的灾难性事件,我们需要制定灾备和容灾策略。这包括在不同地理位置部署多个Redis实例,实现数据的冗余备份和容错处理。当某个实例发生故障时,可以迅速切换到其他实例,保障业务的连续性。5. 安全防护为了防止恶意攻击或误操作导致的数据丢失,我们需要加强Redis的安全防护。这包括设置强密码、限制访问权限、禁用不必要的命令等。同时,定期审计和检查Redis的访问日志,及时发现和处理潜在的安全风险。总之,防止Redis数据丢失需要我们从多个方面入手,包括持久化、备份与恢复、监控与告警、灾备与容灾以及安全防护等。只有综合考虑这些因素,才能确保Redis数据的安全性和可靠性。
  • [技术干货] Redis 缓存雪崩的原因以及解决方案总结
    缓存雪崩缓存雪崩是指大量的应用请求无法在 Redis 缓存中进行处理,紧接着,应用将大量请求发送到数据库层,导致数据库层的压力激增。两个原因:原因一、缓存中有大量数据同时过期,导致大量请求无法得到处理。 具体来说,当数据保存在缓存中,并且设置了过期时间时,如果在某一个时刻,大量数据同时过期,此时,应用再访问这些数据的话,就会发生缓存缺失。紧接着,应用就会把请求发送给数据库,从数据库中读取数据。如果应用的并发请求量很大,那么数据库的压力也就很大,这会进一步影响到数据库的其他正常业务请求处理。 一般来说,一个 Redis 实例可以支持数万级别的请求处理吞吐量,而单个数据库可能只能支持数千级别的请求处理吞吐量,它们两个的处理能力可能相差了近十倍。由于缓存雪崩,Redis 缓存失效,所以,数据库就可能要承受近十倍的请求压力,从而因为压力过大而崩溃。解决方法:1、避免给大量的数据设置相同的过期时间,设置随机的过期时间 可以避免给大量的数据设置相同的过期时间。如果业务层的确要求有些数据同时失效,你可以在用 EXPIRE 命令给每个数据设置过期时间时,给这些数据的过期时间增加一个较小的随机数(例如,随机增加 1~3 分钟),这样一来,不同数据的过期时间有所差别,但差别又不会太大,既避免了大量数据同时过期,同时也保证了这些数据基本在相近的时间失效,仍然能满足业务需求2、通过服务降级(针对不同的数据采取不同的处理方式) 当业务应用访问的是非核心数据(例如电商商品属性)时,暂时停止从缓存中查询这些数据,而是直接返回预定义信息、空值或是错误信息;当业务应用访问的是核心数据(例如电商商品库存)时,仍然允许查询缓存,如果缓存缺失,也可以继续通过数据库读取。原因二、Redis 缓存实例发生故障宕机了解决方法:1、在业务系统中实现服务熔断(暂停业务应用对缓存系统的接口访问) 所谓的服务熔断,是指在发生缓存雪崩时,为了防止引发连锁的数据库雪崩,甚至是整个系统的崩溃,我们暂停业务应用对缓存系统的接口访问。再具体点说,就是业务应用调用缓存接口时,缓存客户端并不把请求发给 Redis 缓存实例,而是直接返回,等到 Redis 缓存实例重新恢复服务后,再允许应用请求发送到缓存系统。这样一来,我们就避免了大量请求因缓存缺失,而积压到数据库系统,保证了数据库系统的正常运行。2、请求限流机制(请求入口前端控制每秒进入系统的请求数) 服务熔断虽然可以保证数据库的正常运行,但是暂停了整个缓存系统的访问,对业务应用的影响范围大。为了尽可能减少这种影响,我们也可以进行请求限流。这里说的请求限流,就是指,我们在业务系统的请求入口前端控制每秒进入系统的请求数,避免过多的请求被发送到数据库。我给你举个例子。假设业务系统正常运行时,请求入口前端允许每秒进入系统的请求是 1 万个,其中,9000 个请求都能在缓存系统中进行处理,只有 1000 个请求会被应用发送到数据库进行处理。一旦发生了缓存雪崩,数据库的每秒请求数突然增加到每秒 1 万个,此时,我们就可以启动请求限流机制,在请求入口前端只允许每秒进入系统的请求数为 1000 个,再多的请求就会在入口前端被直接拒绝服务。所以,使用了请求限流,就可以避免大量并发请求压力传递到数据库层。使用服务熔断或是请求限流机制,来应对 Redis 实例宕机导致的缓存雪崩问题,是属于“事后诸葛亮”,也就是已经发生缓存雪崩了,我们使用这两个机制,来降低雪崩对数据库和整个业务系统的影响。3、事前预防(Redis缓存主从集群) 通过主从节点的方式构建 Redis 缓存高可靠集群。如果 Redis 缓存的主节点故障宕机了,从节点还可以切换成为主节点,继续提供缓存服务,避免了由于缓存实例宕机而导致的缓存雪崩问题。缓存雪崩是发生在大量数据同时失效的场景下,而接下来我要向你介绍的缓存击穿,是发生在某个热点数据失效的场景下。和缓存雪崩相比,缓存击穿失效的数据数量要小很多,应对方法也不一样,我们来看下。
  • [其他] redis内长过期时间的key对redis的性能影响
     **大量设置长过期时间的key会对Redis的性能产生影响**。  首先,当Redis中有大量设置了长过期时间的key时,Redis需要定期检查这些key是否到期,以便及时清理。这个过程会占用额外的CPU和内存资源,因为Redis需要在后台执行过期检查和清理操作。如果key的数量非常多,这种检查和清理可能会变得频繁,从而对Redis的性能造成影响。  其次,长过期时间的key在存储时会占用更多的内存空间。这是因为每个key都需要维护一些元数据,包括过期时间等信息。如果key的长度很长,那么它们占用的内存也会更多,这可能导致内存使用效率降低,进而影响Redis的性能。  再者,在删除过期key时,Redis采用了一种采样删除的策略。默认情况下,即使过期key的数量超过了总数的25%,也不会完全阻塞Redis服务器。但是,这种采样删除会导致周期性的短暂阻塞,例如每25毫秒阻塞5毫秒来执行删除操作,这也可能在一定程度上影响Redis的响应时间和性能。  最后,大量的key不仅会影响过期检查和清理的性能,还会影响Redis执行其他操作时的性能。例如,当Redis需要对数据库中的键进行查找、更新或删除操作时,键的数量越多,所需的时间就越长,这可能导致性能下降。  综上所述,为了优化性能,可以考虑定期清理不再需要的key,或者使用更合理的数据结构来减少单个key的大小和数量。此外,合理配置Redis的内存回收策略和过期策略也是提高性能的重要措施。 
  • [其他] redis的putobject和removeobject的效率对比
     Redis是一种高性能的键值存储系统,广泛应用于缓存、数据库、消息队列等场景。在Redis中,putObject和removeObject是两个常用的操作,分别用于向Redis中添加数据和删除数据。本文将对这两个操作的效率进行对比分析。  1. putObject操作  putObject操作用于向Redis中添加数据。当执行putObject操作时,Redis会将数据存储在内存中,并根据配置将其持久化到磁盘上。putObject操作的时间复杂度为O(1),即无论数据量大小,执行时间都保持不变。  2. removeObject操作  removeObject操作用于从Redis中删除数据。当执行removeObject操作时,Redis会从内存中移除对应的数据,并根据配置将其从磁盘上删除。removeObject操作的时间复杂度同样为O(1),即无论数据量大小,执行时间都保持不变。  3. 效率对比  从上述分析可以看出,putObject和removeObject操作的时间复杂度都是O(1),因此它们的执行效率相差无几。然而,在实际使用中,它们的效率可能会受到以下因素的影响:  - 内存占用:putObject操作需要将数据存储在内存中,而removeObject操作则需要从内存中移除数据。因此,如果内存资源有限,putObject操作可能会导致内存不足,从而影响其效率。相反,removeObject操作可以释放内存资源,提高系统性能。  - 磁盘I/O:根据配置,putObject和removeObject操作都可能涉及到磁盘I/O。磁盘I/O通常比内存操作慢得多,因此,如果磁盘I/O成为瓶颈,它们的效率可能会受到影响。  - 网络延迟:如果Redis服务器和客户端之间的网络延迟较大,那么putObject和removeObject操作的效率可能会受到网络延迟的影响。  综上所述,putObject和removeObject操作在时间复杂度上相差无几,但在实际使用中,它们的效率可能会受到内存占用、磁盘I/O和网络延迟等因素的影响。为了提高Redis的性能,可以采取以下措施:  - 优化内存使用:合理分配内存资源,避免内存不足的情况发生。  - 优化磁盘I/O:选择合适的持久化策略,平衡性能和数据安全性。  - 优化网络连接:尽量将Redis服务器部署在离客户端较近的位置,减小网络延迟。 
  • [专题汇总] 12月份CodeArts板块技术干货合集,提前放送。
     随着鸿蒙系统的愈发壮大,鸿蒙知识的学习也就变得越发迫切。本月给大家带来的技术干货合集就包括鸿蒙,spring,springboot,java,nginx,redis等众多内容,希望可以帮到大家。  1.Centos搭建KMS(vlmcsd)激活服务器的步骤【转】 https://bbs.huaweicloud.com/forum/thread-0251137381864278028-1-1.html  2.鸿蒙极速入门(一)-HarmonyOS简介 https://bbs.huaweicloud.com/forum/thread-0217137149871563005-1-1.html  3.鸿蒙极速入门(二)-开发准备和HelloWorld https://bbs.huaweicloud.com/forum/thread-0212137150055432004-1-1.html  4.鸿蒙极速入门(三)-TypeScript语言简介 https://bbs.huaweicloud.com/forum/thread-0217137150099699006-1-1.html  5.鸿蒙极速入门(四)-通过登录Demo了解ArkTS https://bbs.huaweicloud.com/forum/thread-0212137150569231005-1-1.html  6.鸿蒙极速入门(五)-路由管理(Router) https://bbs.huaweicloud.com/forum/thread-0217137150812608007-1-1.html      7.HarmonyOS 实战项目 https://bbs.huaweicloud.com/forum/thread-0298137147742910003-1-1.html  8.HarmonyOS 高级特性 https://bbs.huaweicloud.com/forum/thread-0263137147693666005-1-1.html  9.HarmonyOS应用开发 https://bbs.huaweicloud.com/forum/thread-02125137147620736007-1-1.html  10.HarmonyOS UI 开发 https://bbs.huaweicloud.com/forum/thread-0251137147552748004-1-1.html  11.准备HarmonyOS开发环境 https://bbs.huaweicloud.com/forum/thread-0217137147386754003-1-1.html  12.Nginx服务器安装配置SSL证书流程(实现HTTPS安装) https://bbs.huaweicloud.com/forum/thread-0224137038725392014-1-1.html  13.几款值得选的SSH客户端软件 https://bbs.huaweicloud.com/forum/thread-02107137038433887013-1-1.html  14.8个站酷免费字体且可商用 不担心字体版权 https://bbs.huaweicloud.com/forum/thread-0235137038369347011-1-1.html  15.7个加速搜索引擎收录网站实用有效方法 https://bbs.huaweicloud.com/forum/thread-02112137038294122014-1-1.html  16. Java 算法篇-深入理解递归(递归实现:青蛙爬楼梯)-转载 https://bbs.huaweicloud.com/forum/thread-0213136969605169006-1-1.html  17.【内网穿透】搭建我的世界Java版服务器,公网远程联机-转载 https://bbs.huaweicloud.com/forum/thread-02112136969569637009-1-1.html  18.【Nginx篇】Nginx轻松上手-转载 https://bbs.huaweicloud.com/forum/thread-0213136969448107005-1-1.html  19.【SpringBoot(IDEA)+Vue(Vscode)前后端交互】-转载 https://bbs.huaweicloud.com/forum/thread-02107136969215321009-1-1.html  20.【SpringBoot】| SpringBoot 集成 Redis-转载 https://bbs.huaweicloud.com/forum/thread-0240136952804547006-1-1.html  21.基于SpringBoot的个人博客管理系统的设计与实现 毕业设计开题报告-转载 https://bbs.huaweicloud.com/forum/thread-02112136952724159004-1-1.html  22.【SpringBoot篇】Spring_Task定时任务框架-转载 https://bbs.huaweicloud.com/forum/thread-02127136952689543005-1-1.html  23.【SpringCloud】Eureka基于Ribbon负载均衡的调用链路流程分析-转载 https://bbs.huaweicloud.com/forum/thread-0224136952632959005-1-1.html  24. Spring Boot单元测试-转载 https://bbs.huaweicloud.com/forum/thread-0213136952529904002-1-1.html  25.【Springboot系列】SpringBoot整合Jpa-转载 https://bbs.huaweicloud.com/forum/thread-0240136952500264005-1-1.html  26. Spring Boot + MyBatis-Plus实现数据库读写分离 -转载 https://bbs.huaweicloud.com/forum/thread-02107136952426802006-1-1.html 
  • [技术干货] Redis 搭建集群
    redis集群的优缺点Redis集群的优点包括:数据分散:Redis集群可以将数据分散到多个节点上,提高了数据的可靠性和可用性。扩展性强:Redis集群可以方便地进行扩展,通过增加节点的方式来提高系统的性能和容量,以满足不断增长的业务需求。读写分离:Redis集群可以实现读写分离,提高系统的并发作业能力和性能。高可用性:Redis集群可以避免单节点故障的问题,当一个节点宕机时,系统仍然可以正常运行。Redis集群的缺点包括:复杂性:Redis集群的搭建和管理相对复杂,需要专业的知识和技能。数据一致性:Redis集群的数据复制是异步的,不保证数据的强一致性,可能会存在数据不一致的情况。客户端实现:Redis集群的客户端实现相对复杂,需要处理节点的连接和通信等问题。资源隔离性:Redis集群的节点之间数据共享,不能很好地进行资源隔离,可能会互相影响。负载均衡:Redis集群的负载均衡机制不够完善,可能会存在某些节点负载较重的情况。redis集群的搭建方式要搭建Redis集群,可以按照以下步骤进行:创建几台虚拟机(集群模式最少要三个主节点),或者在同一台主机上创建多个Redis实例,使用不同的端口模拟不同的主机。这些实例将作为Redis集群的节点。在每台主机上安装Redis,并确保配置文件中的cluster-enabled选项已启用,表示开启集群模式。此外,还需要根据实际情况修改其他配置项,如超时时间等。Redis安装完成后,需要执行创建集群的命令。具体的命令可能会因Redis版本而异。对于Redis 5,可以使用redis-cli --cluster命令来创建集群。在此之前,需要通过redis-trib.rb工具进行集群创建和配置。创建集群时,需要指定主节点的数量和对应的从节点数量。例如,可以创建一个包含三个主节点和每个主节点对应一个从节点的集群,以确保高可用性。在集群运行过程中,如果一个主节点宕机,Redis会自动将从节点升级为主节点,以保证整个集群的可用性。redis集群监控工具Redis集群监控工具可以使用Redis自带的命令行工具redis-cli以及第三方的监控工具,比如:Redis自带的命令行工具redis-cli:可以用来监控Redis集群的状态和运行情况,包括查看集群节点数量、连接情况、内存使用情况等等。RedisLive:是一款免费开源的基于Python和tornado的Redis监控工具,以WEB的形式展现出Redis中的内存、key、实例数据等信息。Redis-Sentinel:是Redis的作者Antirez在今年6月份完成的,因为Redis实例在各个大公司的应用,每个公司都需要一个Redis集群的管理工具,被迫都自己写管理工具来管理Redis集群,Antirez考虑到社区的急迫需要,花了几个星期写出了Redis-Sentinel。Redis-Sentinel的三大功能:监测、通知、自动故障恢复。
  • [技术干货] Redis 入门(附Python和Java操作redis教程)
    什么是MybatisRedis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。它通常被称为数据结构服务器,因为值(value)可以是 字符串(string)、哈希(Hash)、列表(list)、集合(sets)、有序集合(sorted sets)等类型。redis 与传统数据库的区别Redis与传统关系型数据库的区别如下:数据存储方式:Redis是基于内存的数据库,数据存储在内存中,读写速度快,但容量受限于内存大小。而关系型数据库通常将数据存储在磁盘中,读写速度较慢,但容量可以扩展到很大。数据结构:Redis支持多种数据结构,如字符串、哈希表、列表、集合和有序集合等,对程序员透明,且对原子性操作提供了更为复杂的数据结构。相比之下,关系型数据库通常只支持表格结构。应用场景:Redis主要用于缓存、队列、计数器等,而关系型数据库主要用于存储关系型数据。Python 操作redisPython操作Redis需要使用Redis模块,常用的Redis模块包括redis-py和redis-py-cluster。以下是使用redis-py模块操作Redis的基本步骤:安装redis-py模块:使用pip安装redis-py模块,命令为pip install redis。导入Redis模块:在Python脚本中导入Redis模块,import redis。连接Redis服务器:使用Redis模块中的Redis()函数创建Redis连接对象,指定Redis服务器的地址和端口号,例如r = redis.Redis(host='localhost', port=6379)。操作Redis数据库:使用Redis连接对象调用相应的方法操作Redis数据库,例如r.set('key', 'value')设置键值对,r.get('key')获取键对应的值。以下是一个简单的示例,演示如何使用Python操作Redis:import redis # 创建Redis连接对象 r = redis.Redis(host='localhost', port=6379) # 设置键值对 r.set('name', 'Alice') r.set('age', 25) # 获取键对应的值 name = r.get('name') age = r.get('age') # 输出获取的值 print('name:', name.decode('utf-8')) print('age:', age.decode('utf-8'))在上面的示例中,我们首先创建了一个Redis连接对象,然后设置了两个键值对,分别存储了一个人的姓名和年龄。接着使用get()方法获取键对应的值,并使用decode()方法将字节串解码为字符串。最后输出获取的值。Java 操作redis在Java中操作Redis,我们通常需要使用一个库,例如Jedis或Lettuce。以下是使用Jedis库进行基本操作的示例。首先,你需要在你的项目中添加Jedis的依赖。如果你使用Maven,你可以在你的pom.xml文件中添加以下依赖:<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.7.0</version> <!-- 版本可能会有所变化,取决于你查看此答案时的最新版本 --> </dependency>然后你可以使用以下Java代码进行基本的Redis操作:import redis.clients.jedis.Jedis; public class Main { public static void main(String[] args) { // 创建连接对象 Jedis jedis = new Jedis("localhost"); System.out.println("Connection to server successfully"); // 设置数据 jedis.set("foo", "bar"); System.out.println("Data set in Redis: " + jedis.get("foo")); // 获取存储的数据并输出 System.out.println("Stored data in Redis: " + jedis.get("foo")); } }这是一个非常基础的例子,它连接到了在本地运行的Redis服务器(这是默认设置),然后设置了一个键值对("foo", "bar"),并从Redis获取这个值然后打印出来。在实际应用中,你可能需要处理更复杂的情况,例如使用密码保护你的连接、从远程服务器获取数据、处理并发问题等。在这些情况下,你可能需要使用更高级的库,例如Redisson,它提供了更多的功能和更好的抽象。
  • [技术干货] Redis实战:Redis在Java中的基本使用-转载
     1、使用jedis操作redis 1.1、Jedis简介 Jedis 是 Java 语言开发的 Redis 客户端工具包,用于 Java 语言与 Redis 数据进行交互。 Jedis 在 github 官网地址:https://github.com/redis/jedis#readme Jedis 只是对 Redis 命令的封装,掌握 Redis 命令便可轻易上手 Jedis。 Jedis 遵循 RESP 协议规范开发,具有良好的通用性与可读性。  1.2、引入jedis的Maven依赖         <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->         <dependency>             <groupId>redis.clients</groupId>             <artifactId>jedis</artifactId>             <version>4.4.3</version>         </dependency> 1.2、获取连接 对于许多应用程序,需要连接到 Redis 时。最好使用连接池。可以像这样实例化一个 Jedis 连接池:  JedisPool pool = new JedisPool("localhost", 6379); 1 对于 JedisPool 实例,可以使用 try-With-resources 块获得连接并运行 Redis 命令(这种方式无须自己手动 close)。  try (Jedis jedis = pool.getResource()) {     // 运行单个 SET 命令   jedis.set("clientName", "Jedis"); } 1.3、使用实例 编写以下代码:      public static void main(String[] args) {         // Redis服务端IP和端口号         JedisPool pool = new JedisPool("127.0.0.1", 6379);         try (Jedis jedis = pool.getResource()) {             // 使用相关Redis的命令             // 选择第0个数据库进行操作             jedis.select(0);             // 向0号数据库写入,字符串数据             jedis.set("Java", "best");             jedis.set("PHP", "good");             // 查询是否写入             System.out.println(jedis.get("Java"));             System.out.println(jedis.get("PHP"));         }     } 运行测试用例:  看原文链接Jedis 实例实现了大多数 Redis 命令,这些命令可以在 https://www.javadoc.io/doc/redis.clients/jedis/latest/redis/clients/jedis/Jedis.htmlApI 中查询命令对应的方法。 2、对于JedisPooled的使用 2.1、使用JedisPooled 对每个命令使用 try-with-resources 块可能比较麻烦,因此我们可以考虑使用 JedisPooled。  JedisPooled jedis = new JedisPooled("localhost", 6379); 1 详细代码:      public static void main(String[] args) {         JedisPooled pool = new JedisPooled("127.0.0.1", 6379, null, null);         pool.set("Java", "best");         pool.set("PHP", "good");         System.out.println(pool.get("Java"));         System.out.println(pool.get("PHP"));     } 运行效果: 看原文链接 2.2、关于连接池 使用链接池是官方推荐的使用方式,通过连接池可以更好的使用 Jedis 的,我们可以通过 GenericObjectPoolConfig 对连接池进行相关配置,GenericObjectPoolConfig API 文档:https://commons.apache.org/proper/commons-pool/apidocs/org/apache/commons/pool2/impl/GenericObjectPoolConfig.html  通过 GenericObjectPoolConfig 对象对连接池进行配置,具体代码如下:      public static void main(String[] args) {         GenericObjectPoolConfig config = new JedisPoolConfig();         // 设置连接池中最多允许放100个Jedis对象         config.setMaxTotal(100);         // 设置连接池中最大允许空闲连接         config.setMaxIdle(100);         // 设置连接池中最小允许的连接数         config.setMinIdle(10);         // 借出连接的时候是否测试有效性,推荐false         config.setTestOnBorrow(false);         // 归还时是否测试,推荐false         config.setTestOnReturn(false);         // 创建时是否测试有效  开发的时候设置为false,实践运行的时候设置为true         config.setTestOnCreate(false);         // 当连接池内jedis无可用资源时,是否等待资源,true         config.setBlockWhenExhausted(true);         // 没有获取资源时最长等待1秒,1秒后没有还没有的话就报错         config.setMaxWaitMillis(1000);          JedisPool pool = new JedisPool(config, "127.0.0.1", 6379);          try (Jedis jedis = pool.getResource()) {             // 使用相关Redis的命令             // 选择第0个数据库进行操作             jedis.select(0);             // 向0号数据库写入,字符串数据             jedis.set("Java", "best");             jedis.set("PHP", "good");              // 查询是否写入             System.out.println(jedis.get("Java"));             System.out.println(jedis.get("PHP"));         }     } 运行效果: 看原文链接 3、SpringBoot下使用Redis 3.1、引入Maven依赖 首先,需要在 pom.xml 文件中添加 Redis 依赖:  <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-data-redis</artifactId> 这个依赖包含了 Spring Data Redis,以及 Jedis 和 Lettuce 这两种 Redis 客户端的实现。  3.2、配置Redis连接 在 SpringBoot 项目中,可以通过在 application.properties 或 application.yml 文件中配置 Redis 连接信息。以下是一个示例:  spring:   data:     redis:       timeout: 3000       database: 0       password: password       port: 6379       host: localhost 其中,host 和 port 分别是 Redis 服务器的地址和端口号,password 是 Redis的密码(如果没有密码,可以不填),timeout 是 Redis 连接超时时间,jedis.pool 是连接池的相关设置。  3.3、创建RedisTemplate 使用 Spring Data Redis 操作 Redis,通常会使用 RedisTemplate 类。为了方便起见,我们可以创建一个工具类来管理 RedisTemplate 的创建和使用。以下是一个示例:  import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Component;  import javax.annotation.Resource; import java.util.Set; import java.util.concurrent.TimeUnit;  @Component public class RedisUtils {      /**      * 如果使用 @Autowired 注解完成自动装配 那么      * RedisTemplate要么不指定泛型,要么泛型 为<Stirng,String> 或者<Object,Object>      * 如果你使用其他类型的 比如RedisTemplate<String,Object>      * 那么请使用 @Resource 注解      * */     @Resource     private RedisTemplate<String,Object> redisTemplate;      private Logger logger = LoggerFactory.getLogger(this.getClass());      /**      * 缓存value      *      * @param key -      * @param value -      * @param time -      * @return -      */     public boolean cacheValue(String key, Object value, long time) {         try {             ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();             valueOperations.set(key, value);             if (time > 0) {                 // 如果有设置超时时间的话                 redisTemplate.expire(key, time, TimeUnit.SECONDS);             }             return true;         } catch (Throwable e) {             logger.error("缓存[" + key + "]失败, value[" + value + "] " + e.getMessage());         }         return false;     }      /**      * 缓存value,没有设置超时时间      *      * @param key -      * @param value -      * @return -      */     public boolean cacheValue(String key, Object value) {         return cacheValue(key, value, -1);     }      /**      * 判断缓存是否存在      *      * @param key      * @return      */     public boolean containsKey(String key) {         try {             return redisTemplate.hasKey(key);         } catch (Throwable e) {             logger.error("判断缓存是否存在时失败key[" + key + "]", "err[" + e.getMessage() + "]");         }         return false;     }      /**      * 根据key,获取缓存      *      * @param key -      * @return -      */     public Object getValue(String key) {         try {             ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();             return valueOperations.get(key);         } catch (Throwable e) {             logger.error("获取缓存时失败key[" + key + "]", "err[" + e.getMessage() + "]");         }         return null;     }      /**      * 移除缓存      *      * @param key -      * @return -      */     public boolean removeValue(String key) {         try {             redisTemplate.delete(key);             return true;         } catch (Throwable e) {             logger.error("移除缓存时失败key[" + key + "]", "err[" + e.getMessage() + "]");         }         return false;     }      /**      * 根据前缀移除所有以传入前缀开头的key-value      *      * @param pattern -      * @return -      */     public boolean removeKeys(String pattern) {         try {             Set<String> keySet = redisTemplate.keys(pattern + "*");             redisTemplate.delete(keySet);             return true;         } catch (Throwable e) {             logger.error("移除key[" + pattern + "]前缀的缓存时失败", "err[" + e.getMessage() + "]");         }         return false;     }  } 在这个示例中,我们使用 @Resource 注解自动注入了一个 RedisTemplate<String, Object> 对象。然后,我们提供了三个方法来对 Redis 进行操作:cacheValue 方法用于缓存数据,getValue 方法用于获取缓存数据,removeValue 方法用于删除缓存数据。这些方法都是通过 redisTemplate 对象来实现的。  需要注意的是,在使用 RedisTemplate 时,需要指定键值对的类型。在这个示例中,我们指定了键的类型为 String,值的类型为 Object。  3.4、使用RedisTemplate 在上面的示例中,我们已经创建了一个 RedisTemplate 对象,并提供了一些方法来对 Redis 进行操作。现在,我们可以在 SpringBoot 项目中的任何地方使用这个工具类来进行缓存操作。以下是一个示例:  import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController;  @RestController public class UserController {      @Autowired     private RedisUtils redisUtils;      @Autowired     private UserService userService;      @GetMapping("/users/{id}")     public User getUserById(@PathVariable Long id) {          String key = "user_" + id;         User user = (User) redisUtils.getValue(key);         if (user == null) {             user = userService.getUserById(id);             redisUtils.cacheValue(key, user);         }         return user;     } } 在这个示例中,我们创建了一个 UserController 类,用于处理 HTTP 请求。在 getUserById 方法中,我们首先构造了一个缓存的 key,然后使用 redisUtils.getValue 方法从 Redis 中获取缓存数据。如果缓存中没有数据,我们调用 userService.getUserById 方法从数据库中获取数据,并使用 redisUtils.cacheValue 方法将数据存入Redis缓存中。最后,返回获取到的数据。  通过这个示例,我们可以看到,在S pringBoot 项目中使用 Redis 作为缓存的流程。我们首先需要添加 Redis 依赖,然后在配置文件中配置 Redis 连接信息。接着,我们创建了一个 RedisUtil s工具类来管理 RedisTemplate 的创建和使用。最后,我们在控制器中使用 RedisUtils 来对 Redis 进行缓存操作。 ———————————————— 版权声明:本文为CSDN博主「栗筝i」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_45187434/article/details/132373648 
  • [技术干货] 拼多多社招面经:Redis是重点,讲一讲redis的内存模型【转】
    1、简单做一下自我介绍把,为什么这么快就想换工作。简单说下你简历中的项目。2、看你在项目中用了redis,我们先聊聊redis吧,常用的数据结构有哪几种,在你的项目中用过哪几种,以及在业务中使用的场景,redis的hash怎么实现的,rehash过程讲一下和JavaHashMap的rehash有什么区别?redis cluster有没有了解过,怎么做到高可用的?3redis集群和哨兵机制有什么区别?redis的持久化机制了解吗?你们在项目中是怎么做持久化的?遇到过redis的hotkey吗?怎么处理的?4redis是单线程的吗?单线程为什么还这么快?讲一讲redis的内存模型?5.我看你还用了RabbitMQ,简单说一下RabbitMQ的工作原理?如何保证消息的顺序执行?Kafka了解吗?和RabbitMQ有什么区别?你为啥不用kafka来做,当时怎么考虑的?6、我看你简历里说熟悉计算机网络,来聊一聊计算机网络吧。了不了解tcp/udp,简单说下两者的tcp和udp的区别?tcp为什么要三次握手和四次挥手?两次握手可以不?会有什么问题?tcp怎么保证有序传输的,讲下tcp的快速重传和拥塞机制,知不知道time_wait状态,这个状态出现在什么地方,有什么用?7、http与https有啥区别?https是怎么做到安全的?8、有没有了解过协程?说下线程和协程的区别?用过哪些linux命令?如查看内存使用、网络情况?9、你了解哪些设计模式啊。挑一个熟悉的讲讲?(除了单例模式)在项目中有用过设计模式吗?讲讲你怎么用的?简单说一下适配器模式和装饰器模式?10、你们数据库有没有用到分库分表,怎么做的?分库分表以后全局id怎么生成的?11、索引的常见实现方式有哪些,有哪些区别?MySQL的存储引擎有哪些,有哪些区别?InnoDB使用的是什么方式实现索引,怎么实现的?说下聚簇索引和非聚簇索引的区别?12、看你简历提到了raft算法,讲下raft算法的基本流程?raft算法里面如果出现脑裂怎么处理?有没有了解过paxos和zookeeper的zab算法,他们之前有啥区别?13、聊聊java基础吧,如果我是想一个人的姓名一样就认为他们equal,能现场写下我们怎么重写equals吗?如果两个对象,一个是cat,一个是dog,我们认为他们的name属性一样就一样,怎么重写equals14,还有点时间,写个题吧leetcode406. 根据身高重建队列假设有打乱顺序的一群人站成一个队列。 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数。 编写一个算法来重建这个队列。注意:总人数少于1100人。示例输入:[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]输出:[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]一:看你在项目中用了redis,我们先聊聊redis吧,常用的数据结构有哪几种,在你的项目中用过哪几种,以及在业务中使用的场景,redis的hash怎么实现的,rehash过程讲一下和JavaHashMap的rehash有什么区别?redis cluster有没有了解过,怎么做到高可用的?(1)常用的数据结构:字符串(String),散列/哈希(hash),列表(list),无序集合类型(set),有序集合类型(sorted set)(2)Redis的hash 实现:Redis散列/哈希是键值对的集合。Redis散列/哈希是字符串字段和字符串值之间的映射,但字段值只能是字符串,不支持其他类型。因此,他们用于表示对象。应用场景:存储用户的基本信息,等等、(3)redis cluster:redis最开始使用主从模式做集群,若master宕机需要手动配置slave转为master;后来为了高可用提出来哨兵模式,该模式下有一个哨兵监视master和slave,若master宕机可自动将slave转为master,但它也有一个问题,就是不能动态扩充;所以在3.x提出cluster集群模式。Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。其结构特点:1、所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。2、节点的fail是通过集群中超过半数的节点检测失效时才生效。3、客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。4、redis-cluster把所有的物理节点映射到[0-16383]slot上(不一定是平均分配),cluster 负责维护node<->slot<->value。5、Redis集群预分好16384个桶,当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384的值,决定将一个key放到哪个桶中。二.redis集群和哨兵机制有什么区别?redis的持久化机制了解吗?你们在项目中是怎么做持久化的?遇到过redis的hotkey吗?怎么处理的?(1)redis集群和哨兵机制有什么区别?谈到Redis服务器的高可用,如何保证备份的机器是原始服务器的完整备份呢?这时候就需要哨兵和复制。哨兵(Sentinel):可以管理多个Redis服务器,它提供了监控,提醒以及自动的故障转移的功能。复制(Replication):则是负责让一个Redis服务器可以配备多个备份的服务器。Redis正是利用这两个功能来保证Redis的高可用。哨兵是Redis集群架构中非常重要的一个组件,哨兵的出现主要是解决了主从复制出现故障时需要人为干预的问题。Redis哨兵主要功能(1)集群监控:负责监控Redis master和slave进程是否正常工作(2)消息通知:如果某个Redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员(3)故障转移:如果master node挂掉了,会自动转移到slave node上(4)配置中心:如果故障转移发生了,通知client客户端新的master地址Redis哨兵的高可用原理:当主节点出现故障时,由Redis Sentinel自动完成故障发现和转移,并通知应用方,实现高可用性。1.哨兵机制建立了多个哨兵节点(进程),共同监控数据节点的运行状况。2.同时哨兵节点之间也互相通信,交换对主从节点的监控状况。3.每隔1秒每个哨兵会向整个集群:Master主服务器+Slave从服务器+其他Sentinel(哨兵)进程,发送一次ping命令做一次心跳检测。这个就是哨兵用来判断节点是否正常的重要依据,涉及两个新的概念:主观下线和客观下线。主观下线:一个哨兵节点判定主节点down掉是主观下线。2.客观下线:只有半数哨兵节点都主观判定主节点down掉,此时多个哨兵节点交换主观判定结果,才会判定主节点客观下线。原理:基本上哪个哨兵节点最先判断出这个主节点客观下线,就会在各个哨兵节点中发起投票机制Raft算法(选举算法),最终被投为领导者的哨兵节点完成主从自动化切换的过程。Redis 复制Redis为了解决单点数据库问题,会把数据复制多个副本部署到其他节点上,通过复制,实现Redis的高可用性,实现对数据的冗余备份,保证数据和服务的高度可靠性。1.数据复制原理(执行步骤)①从数据库向主数据库发送sync(数据同步)命令。②主数据库接收同步命令后,会保存快照,创建一个RDB文件。③当主数据库执行完保持快照后,会向从数据库发送RDB文件,而从数据库会接收并载入该文件。④主数据库将缓冲区的所有写命令发给从服务器执行。⑤以上处理完之后,之后主数据库每执行一个写命令,都会将被执行的写命令发送给从数据库。注意:在Redis2.8之后,主从断开重连后会根据断开之前最新的命令偏移量进行增量复制Redis 主从复制、哨兵和集群这三个有什么区别主从复制是为了数据备份,哨兵是为了高可用,Redis主服务器挂了哨兵可以切换,集群则是因为单实例能力有限,搞多个分散压力,简短总结如下:主从模式:备份数据、负载均衡,一个Master可以有多个Slaves。sentinel发现master挂了后,就会从slave中重新选举一个master。cluster是为了解决单机Redis容量有限的问题,将数据按一定的规则分配到多台机器。sentinel着眼于高可用,Cluster提高并发量。1.主从模式:读写分离,备份,一个Master可以有多个Slaves。2.哨兵sentinel:监控,自动转移,哨兵发现主服务器挂了后,就会从slave中重新选举一个主服务器。3.集群:为了解决单机Redis容量有限的问题,将数据按一定的规则分配到多台机器,内存/QPS不受限于单机,可受益于分布式集群高扩展性。4:redis是单线程的吗?单线程为什么还这么快?讲一讲redis的内存模型?Redis内存划分:Redis作为内存数据库,在内存中存储的内容主要是数据(键值对);通过前面的叙述可以知道,除了数据以外,Redis的其他部分也会占用内存。Redis的内存占用主要可以划分为以下几个部分:1、数据作为数据库,数据是最主要的部分;这部分占用的内存会统计在used_memory中。Redis使用键值对存储数据,其中的值(对象)包括5种类型,即字符串、哈希、列表、集合、有序集合。这5种类型是Redis对外提供的,实际上,在Redis内部,每种类型可能有2种或更多的内部编码实现;此外,Redis在存储对象时,并不是直接将数据扔进内存,而是会对对象进行各种包装:如redisObject、SDS等;这篇文章后面将重点介绍Redis中数据存储的细节。2、进程本身运行需要的内存Redis主进程本身运行肯定需要占用内存,如代码、常量池等等;这部分内存大约几兆,在大多数生产环境中与Redis数据占用的内存相比可以忽略。这部分内存不是由jemalloc分配,因此不会统计在used_memory中。补充说明:除了主进程外,Redis创建的子进程运行也会占用内存,如Redis执行AOF、RDB重写时创建的子进程。当然,这部分内存不属于Redis进程,也不会统计在used_memory和used_memory_rss中。3、缓冲内存缓冲内存包括客户端缓冲区、复制积压缓冲区、AOF缓冲区等;其中,客户端缓冲存储客户端连接的输入输出缓冲;复制积压缓冲用于部分复制功能;AOF缓冲区用于在进行AOF重写时,保存最近的写入命令。在了解相应功能之前,不需要知道这些缓冲的细节;这部分内存由jemalloc分配,因此会统计在used_memory中。4、内存碎片内存碎片是Redis在分配、回收物理内存过程中产生的。例如,如果对数据的更改频繁,而且数据之间的大小相差很大,可能导致redis释放的空间在物理内存中并没有释放,但redis又无法有效利用,这就形成了内存碎片。内存碎片不会统计在used_memory中。内存碎片的产生与对数据进行的操作、数据的特点等都有关;此外,与使用的内存分配器也有关系:如果内存分配器设计合理,可以尽可能的减少内存碎片的产生。后面将要说到的jemalloc便在控制内存碎片方面做的很好。如果Redis服务器中的内存碎片已经很大,可以通过安全重启的方式减小内存碎片:因为重启之后,Redis重新从备份文件中读取数据,在内存中进行重排,为每个数据重新选择合适的内存单元,减小内存碎片。5. 我看你还用了RabbitMQ,简单说一下RabbitMQ的工作原理?如何保证消息的顺序执行?Kafka了解吗?和RabbitMQ有什么区别?你为啥不用kafka来做,当时怎么考虑的?组成部分说明如下:Broker:消息队列服务进程,此进程包括两个部分:Exchange和Queue。Exchange:消息队列交换机,按一定的规则将消息路由转发到某个队列,对消息进行过虑。Queue:消息队列,存储消息的队列,消息到达队列并转发给指定的消费方。Producer:消息生产者,即生产方客户端,生产方客户端将消息发送到MQ。Consumer:消息消费者,即消费方客户端,接收MQ转发的消息。消息发布接收流程:-----发送消息-----1、生产者和Broker建立TCP连接。2、生产者和Broker建立通道。3、生产者通过通道消息发送给Broker,由Exchange将消息进行转发。4、Exchange将消息转发到指定的Queue(队列)----接收消息-----1、消费者和Broker建立TCP连接2、消费者和Broker建立通道3、消费者监听指定的Queue(队列)4、当有消息到达Queue时Broker默认将消息推送给消费者。5、消费者接收到消息。6、我看你简历里说熟悉计算机网络,来聊一聊计算机网络吧。了不了解tcp/udp,简单说下两者的区别?tcp为什么要三次握手和四次挥手?两次握手可以不?会有什么问题?tcp怎么保证有序传输的,讲下tcp的快速重传和拥塞机制,知不知道time_wait状态,这个状态出现在什么地方,有什么用?(1)区别:1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信5、TCP首部开销20字节;UDP的首部开销小,只有8个字节6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道两次握手为什么不可以?采用三次握手是为了防止失效的连接请求报文段突然又传送到主机B,因而产生错误。失效的连接请求报文段是指:主机A发出的连接请求没有收到主机B的确认,于是经过一段时间后,主机A又重新向主机B发送连接请求,且建立成功,顺序完成数据传输。考虑这样一种特殊情况,主机A第一次发送的连接请求并没有丢失,而是因为网络节点导致延迟达到主机B,主机B以为是主机A又发起的新连接,于是主机B同意连接,并向主机A发回确认,但是此时主机A根本不会理会,主机B就一直在等待主机A发送数据,导致主机B的资源浪费。为什么要四次挥手?TCP关闭链接四次握手原因在于tpc链接是全双工通道,需要双向关闭。client向server发送关闭请求,表示client不再发送数据,server响应。此时server端仍然可以向client发送数据,待server端发送数据结束后,就向client发送关闭请求,然后client确认。tcp怎么保证有序传输的:1)应用数据被分割成TCP认为最适合发送的数据块。2)超时重传:当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。3)TCP给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。4)校验和:TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。5)TCP的接收端会丢弃重复的数据。6)流量控制:TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的我数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP使用的流量控制协议是可变大小的滑动窗口协议。7)拥塞控制:当网络拥塞时,减少数据的发送。time_wait状态,这个状态出现在什么地方,有什么用?1.为实现TCP全双工连接的可靠释放当服务器先关闭连接,如果不在一定时间内维护一个这样的TIME_WAIT状态,那么当被动关闭的一方的FIN到达时,服务器的TCP传输层会用RST包响应对方,这样被对方认为是有错误发生,事实上这只是正常的关闭连接工程,并没有异常2.为使过期的数据包在网络因过期而消失在这条连接上,客户端发送了数据给服务器,但是在服务器没有收到数据的时候服务器就断开了连接现在数据到了,服务器无法识别这是新连接还是上一条连接要传输的数据,一个处理不当就会导致诡异的情况发生下面讲讲大量的TIME_WAIT产生需要的条件:1.高并发2.服务器主动关闭连接如果服务器不主动关闭连接,那么TIME_WAIT就是客户端的事情了7、http与https有啥区别?https是怎么做到安全的?https安全的原因:https把http消息进行加密之后再传送,这样就算坏人拦截到了,得到消息之后也看不懂,这样就做到了安全,具体来说,https是通过对称加密和非对称加密和hash算法共同作用,来在性能和安全性上达到一个平衡,加密是会影响性能的,尤其是非对称加密,因为它的算法比较复杂,那么加密了就安全了吗?不是的,https除了对消息进行了加密以外还会对通信的对象进行身份验证。https并不是一种新的协议,而是使用了一种叫做TLS(Transport layer secure)的安全层,这个安全层提供了数据加密的支持,让http消息运行在这个安全层上,就达到了安全,而运行在这个安全层上的http就叫做https(http secure)8,还有点时间,写个题吧leetcode406. 根据身高重建队列假设有打乱顺序的一群人站成一个队列。 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数。 编写一个算法来重建这个队列。注意:总人数少于1100人。示例输入:[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]输出:[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]思路:第一步排序:people:[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]排序后:[[7,0], [7,1], [6,1], [5,0], [5,2], [4,4]]然后从数组people第一个元素开始,放入到数组result中,放入的位置就是离result开始位置偏移了元素第二个数字后的位置。如下:people: [7,0]插入到离开始位置偏移了0个距离的位置。result: [[7,0]]people: [7,1]插入到离开始位置偏移了1个距离的位置,即插入到[7,0]的后面。result: [[7,0], [7,1]]people: [6,1]插入到离开始位置偏移了1个距离的位置,即插入到[7,0]的后面。result: [[7,0], [6,1], [7,1]]people: [5,0]插入到离开始位置偏移了0个距离的位置,即插入到[7,0]的前面。result: [[5,0], [7,0], [6,1], [7,1]]people: [5,2]插入到离开始位置偏移了2个距离的位置,即插入到[7,0]的后面。result: [[5,0], [7,0], [5,2], [6,1], [7,1]]people: [4,4]插入到离开始位置偏移了4个距离的位置,即插入到[6,1]的后面。result: [[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]这种算法体现了元素第二个数字与其插入位置的关系,所以通过简单的一个for循环就可以搞定。
  • Redis常见面试题
    redis的特点:  redis本质上是一个key-value类型的内存数据库,整个数据库系统加载在内存当中操作,定期通过异步操作把数据库数据flash硬盘上进行保存。因为是纯内存操作,redis的性能非常出色,每秒可以处理超过10万次读写操作,是已知的最快的key-value数据库。redis出色的不仅仅是性能,还支持保存多种数据结构,并且单个value可以存储的最大限制是1GB,另外redis还可以对key-value设置过期时间  缺点是:数据库容易收到物理内存的限制,不能做海量数据的高性能读写,只适用于较小数据量的高性能操作何运算的场景。1.redis为什么这么快  基于内存  单线程减少上下文切换,同时保证原子性  使用多路I/O复用模型,非阻塞IO;   多种数据结构(hash、跳表等)2.为什么使用单线程  因为 Redis 是基于内存的操作,CPU 不会成为 Redis 的瓶颈,而最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且 CPU 不会成为瓶颈,那就顺理成章地采用单线程的方案了。3.redis缓存三大问题  1.缓存穿透是指客户端请求的数据再缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。不断发起这样的请求,给数据库带来压力    缓存空对象、布隆过滤、 增强id的复杂度,避免被猜测id规律      做好数据的基础格式校验、加强用户权限校验、做好热点参数的限流  2.缓存雪崩是指在同一时间段大量的key同时失效或者redis服务器宕机,导致大量请求到达数据库,带来巨大压力。    给不同的key的TTL添加随机值    利用redis集群提高服务的可用性    给缓存业务添加降级限流策略    给业务添加多级缓存  3.缓存击穿问题也叫热点key问题,就是一个被高并发访问并且缓存重建业务比较复杂的key突然失效,无数的请求访问会在瞬间给数据库带来巨大的冲击    逻辑过期    互斥锁4.先删后写还是先写后删  先删除缓存再更新数据库 脏读问题:两个线程,一个更新操作,一个查询操作,当更新操作删除数据缓存后,这个时候查询操作没有找到缓存,就从数据库中找到旧数据,并将旧数据写入到缓存中,此时更新操作再把数据库中数据更新,导致数据不一致。脏读。  先更新数据库再删除缓存  脏读几率小:更新和查询,缓存失效,查询操作查询缓存没有,就找到数据库中的旧数据,此时,更新操作来了,删除缓存,并更新数据库,最后查询操作将查询的值缓存到缓存中,出现数据不一致问题。但是几率很小,往往数据库的读操作的速度远快于写操作。解决:1)缓存设置过期时间,实现最终一致性;2)使用 Cannel 等中间件监听 binlog 进行异步更新;3)通过 2PC 或 Paxos 协议保证一致性。5.redis如何保证原子性:需求:两个客户端同时对[key1]执行自增操作,不会相互影响操作:下面两个客户端并发操作会导致[key1]输出结果与预期不一致[客户端一]读取[key1],值为[1][客户端二]读取[key1],值为[1][客户端一]将[key1]自增1,值为[2][客户端二]将[key1]自增1,值为[2][客户端一]输出[key1],值为[2][客户端二]输出[key2],值为[2]解决思路[客户端一]、[客户端二]的R(读)、M(自增)、W(写)三个操作作为一个原子操作执行[客户端]对RMW整个操作过程加锁,加锁期间其它客户端不能对[key1]执行写操作Lua脚本思路一:单命令操作1. 概念    Redis 提供了 INCR/DECR/SETNX 命令,把RMW三个操作转变为一个原子操作    Redis 是使用单线程串行处理客户端的请求来操作命令,所以当 Redis 执行某个命令操作时,其他命令是无法执行的,这相当于命令操作是互斥执行的思路二:加锁1. 概念     加锁主要是将多客户端线程调用相同业务方法转换为串行化处理,比如多个客户端调用同一个方法对某个键自增(这里不考虑其它方法或业务会对该键同时执行自增操作)        调用SETNX命令对某个键进行加锁(如果获取锁则执行后续RMW操作,否则直接返回未获取锁提示)     执行RMW业务操作     调用DEL命令删除锁2. 加锁风险一     假如某个客户端在执行了SETNX命令加锁之后,在后面操作业务逻辑时发生了异常,没有执行 DEL 命令释放锁。     该锁就会一直被这个客户端持有,其它客户端无法拿到锁,导致其它客户端无法执行后续操作。     解决思路:给锁变量设置一个过期时间,到期自动释放锁SET key value [EX seconds | PX milliseconds] [NX]3. 加锁风险二     如果客户端 A 执行了 SETNX 命令加锁后,客户端 B 执行 DEL 命令释放锁,此时,客户端 A 的锁就被误释放了。如果客户端 C 正好也在申请加锁,则可以成功获得锁。     解决思路:加锁操作时给每个客户端设置一个唯一值(比如UUID),唯一值可以用来标识当前操作的客户端。                       在释放锁操作时,客户端判断当前锁变量的值是否和唯一标识相等,只有在相等的情况下,才能释放锁。(同一客户端线程中加锁、释放锁) SET lock_key unique_value NX PX 10000思路三:Lua脚本1. 概念多个操作写到一个 Lua 脚本中(Redis 会把整个 Lua 脚本作为一个整体执行,在执行的过程中不会被其他命令打断,从而保证了 Lua 脚本中操作的原子性)2. 需求限制所有客户端在一定时间范围内对某个方法(键)的访问次数。客户端 IP 作为 key,某个方法(键)的访问次数作为 value3. 脚本local current current = redis.call("incr",KEYS[1]) if tonumber(current) == 1 then redis.call("expire",KEYS[1],60) end4. 调用执行redis-cli --eval lua.script keys , args
  • Redis常见的数据类型以及应用场景
    Redis支持多种数据类型,每种数据类型都有其独特的特点和应用场景。以下是Redis常见的数据类型以及它们的应用场景:字符串(String):存储单个值或对象的序列化数据。应用场景:缓存、计数器、分布式锁等。哈希表(Hash):存储多个字段和值的散列数据结构,可以看作是一个关联数组。应用场景:存储用户信息、对象属性、配置项等。列表(List):有序的字符串集合,可以在两端进行插入和删除操作。应用场景:消息队列、最新动态、时间线等。集合(Set):无序的唯一字符串集合,支持交集、并集和差集等集合操作。应用场景:标签、点赞用户、共同好友等。有序集合(Sorted Set):类似于集合,但每个元素都有一个关联的分数,根据分数排序。应用场景:排行榜、Top N、优先级队列等。地理空间索引(GeoSpatial):存储地理位置信息的数据类型,支持距离计算和位置查询。应用场景:附近的人、地理位置搜索等。Redis的灵活数据类型和高性能特性使其在各种场景下都有广泛的应用。需要根据具体的业务需求来选择合适的数据类型,以提高系统的性能和可扩展性。
  • [技术干货] Redis中的IO多路复用
    在Redis中,也使用了I/O多路复用来实现高效的网络通信。Redis是一个高性能的键值存储数据库,其主要通过使用非阻塞I/O和I/O多路复用来处理并发连接,提高网络通信的性能。Redis使用了多个I/O多路复用模型,根据不同的操作系统平台和版本,可能采用select、epoll、kqueue等不同的机制。在Linux系统上,一般使用epoll来实现I/O多路复用。具体来说,Redis的I/O多路复用工作流程如下:客户端连接:当Redis服务器启动时,它会监听指定的端口,等待客户端连接。多路复用:一旦有客户端连接到服务器,Redis会使用I/O多路复用机制(如epoll)来同时监听多个客户端连接的读写事件。事件处理:当有I/O事件就绪时,Redis会根据事件类型执行相应的操作,如接收客户端发送的命令、读取数据、执行命令、将结果返回给客户端等。非阻塞处理:在处理客户端请求的过程中,Redis会尽可能地避免使用阻塞式I/O,而是使用非阻塞I/O来保证高效的并发处理。通过使用I/O多路复用,Redis能够高效地处理大量并发连接,而不会因为阻塞式I/O而导致性能下降。这使得Redis成为了一个优秀的高性能、高并发的数据库引擎,尤其适用于缓存和实时数据处理等场景。
  • [技术干货] Redis中的过期策略
    redis过期策略定时过期、惰性过期、定期过期问题:使用expire key 60,在key60s之后key就会过期,之后如何清除key定时过期每个设置过期时间的key都创建一个定时器,到时间就会对key进行清除。该策略可以立即清除过期key,对内存友好,但是需要消耗大量的cpu时间去清理过期数据,从而影响响应时间和吞吐量。惰性过期只有当访问一个key时,才会判断这个key是否过期,过期就清除。该策略可以节省cpu资源,但是对内存不够友好。极端情况下会出现大量的过期key没有被清理,占用内存。定期过期每隔一段时间,扫描一定数量数据库中的expires字典中的key,清除过期的key。该策略是前两种的一个折中策略,cpu和内存达到最优平衡。expires字典:当我们在 Redis 中设置一个键的过期时间时,该键会被添加到 expires 字典中,并在指定的过期时间后自动从数据库中删除。expires 字典以键为索引,值为键的过期时间戳。当 Redis 中的某个键过期时,Redis 服务器会在后台自动将其从数据库中删除。Redis 服务器使用定时器来监视 expires 字典中键的过期时间,定期检查键是否已过期。当键过期时,定时器会将其标记为即将删除,并在适当的时候释放该键所占用的内存空间。redis采用的了惰性过期和定期过期两种过期策略,定期过期。极端情况:定期过期漏掉了很多过期key,又没有走惰性过期。就会有很多过期key在内存中。就需要淘汰策略。内存淘汰机制Redis的内存淘汰策略是为了解决内存不足的问题,当Redis的内存使用达到上限时,需要根据事先设置的内存淘汰策略来选择一些键,使其被删除以释放内存空间。Redis提供了多种内存淘汰策略,可以根据实际需求进行配置。以下是Redis常见的内存淘汰策略:LRU(Least Recently Used,最近最少使用):淘汰最近最少被使用的键。当内存不足时,会优先删除最近最少使用的键来释放空间。LFU(Least Frequently Used,最不经常使用):淘汰最不经常被使用的键。当内存不足时,会优先删除访问频率最低的键来释放空间。TTL(Time To Live,生存时间):淘汰设置了过期时间的键。当键的过期时间到期时,会被自动删除。Random(随机):随机选择一些键进行删除。这种策略简单且随机性较高,但不保证删除的是最不重要的键。Allkeys-LRU:除了设置了过期时间的键外,其他键按照LRU策略进行淘汰。Allkeys-Random:除了设置了过期时间的键外,其他键按照随机策略进行淘汰。noeviction:在Redis中,默认的内存淘汰策略是noeviction,即当内存不足时,不会自动淘汰键,而是拒绝新写入的操作,直到有足够的内存空间。但为了保证数据存储的稳定性和可用性,一般建议配置合适的内存淘汰策略,避免因为内存不足而导致系统出现问题。可以通过配置Redis的maxmemory-policy参数来设置所需的内存淘汰策略,根据实际情况选择适合的策略来满足应用的需求。
  • [技术干货] MySQL数据同步到 Redis 缓存的几种方法
    将 MySQL 数据同步到 Redis 缓存时,可以采用以下几种常见的方法,并为每种方法提供一个示例:1.定时任务同步示例:使用 Node.js 编写一个定时任务程序,每隔一段时间将 MySQL 中的数据同步到 Redis 缓存中。const mysql = require('mysql');const redis = require('redis');// 创建 MySQL 连接const mysqlConnection = mysql.createConnection({ host: 'localhost', user: 'root', password: 'password', database: 'my_database',});// 创建 Redis 连接const redisClient = redis.createClient();// 从 MySQL 中获取数据并同步到 Redisfunction syncData() { mysqlConnection.query('SELECT * FROM my_table', (error, results) => { if (error) throw error; // 将数据同步到 Redis redisClient.set('my_data', JSON.stringify(results)); });}// 每隔一段时间执行同步任务setInterval(syncData, 60000); // 每分钟同步一次2.数据库触发器示例:在 MySQL 数据库中创建触发器,在数据变更时自动将变更的数据同步到 Redis 缓存。CREATE TRIGGER sync_to_redis AFTER INSERT ON my_tableFOR EACH ROWBEGIN SET @data = JSON_OBJECT( 'id', NEW.id, 'name', NEW.name, 'age', NEW.age -- 其他字段 ); SET @key = CONCAT('my_data:', NEW.id); -- 将数据同步到 Redis CALL redis_command('SET', @key, @data);END;3.双写模式示例:在应用层代码中对 MySQL 和 Redis 进行双写操作,保持数据的实时性和一致性。import pymysqlimport redis# 创建 MySQL 连接mysql_connection = pymysql.connect( host='localhost', user='root', password='password', database='my_database')# 创建 Redis 连接redis_client = redis.Redis()# 数据插入操作示例def insert_data(data): # 在 MySQL 中执行插入操作 with mysql_connection.cursor() as cursor: sql = 'INSERT INTO my_table (id, name, age) VALUES (%s, %s, %s)' cursor.execute(sql, (data['id'], data['name'], data['age'])) mysql_connection.commit() # 同步数据到 Redis redis_client.set(f"my_data:{data['id']}", json.dumps(data))# 调用示例data = {'id': 1, 'name': 'Alice', 'age': 25}insert_data(data)4.客户端代理示例:使用 Redis Proxy 工具将 MySQL 和 Redis 进行代理,并实现数据的同步和转发# 配置文件示例listen: - port: 6379 backend: mysql backend_config: host: localhost port: 3306 user: root password: password database: my_database - port: 6380 backend: redis backend_config: host: localhost port: 6379以上示例提供了不同的方法来将 MySQL 数据同步到 Redis 缓存中。根据实际需求和技术栈选择合适的方法,并根据具体情况进行配置和调整。
总条数:384 到第
上滑加载中