当前位置: 首页 > news >正文

Redis性能提升秘籍:大Key与热点Key优化实战

引言

在当今的数字化时代,随着互联网应用的快速发展和数据量的爆发式增长,高效的数据存储和处理成为了构建高性能应用架构的关键。Redis,作为一款基于内存的高性能键值对存储数据库,以其出色的读写速度、丰富的数据结构和灵活的应用场景,在现代应用架构中占据了举足轻重的地位。它不仅被广泛应用于缓存、消息队列、分布式锁等常见场景,还在实时数据分析、排行榜系统等复杂业务中发挥着重要作用,极大地提升了应用的响应速度和用户体验。

然而,在享受 Redis 带来的高性能和便捷性的同时,我们也不可避免地会遇到一些挑战,其中大 Key 和热点 Key 问题尤为突出。大 Key,通常是指那些占用大量内存空间或者包含大量元素的数据项。当对这些大 Key 进行操作时,比如读取、删除或者更新,往往会耗费大量的系统资源,导致 Redis 实例的性能急剧下降。这不仅会使操作本身的响应时间大幅增加,还可能因为阻塞了 Redis 的单线程事件循环,影响到其他请求的正常处理,进而引发连锁反应,降低整个系统的吞吐量和可用性。

热点 Key 则是指在某段时间内被频繁访问的 Key。这些热点 Key 承载了大量的读写请求,会导致 Redis 服务器的 CPU 使用率飙升,内存资源紧张。在 Redis 集群环境中,热点 Key 还可能导致负载不均衡,部分节点压力过大,而其他节点却处于低负载状态,严重影响集群的整体性能和稳定性。此外,热点 Key 的存在还可能引发缓存击穿问题,当热点 Key 的缓存过期时,大量请求直接穿透到后端数据库,瞬间给数据库带来巨大压力,甚至可能导致数据库崩溃,引发系统雪崩。

由此可见,大 Key 和热点 Key 问题对系统性能的影响不容小觑,它们就像是隐藏在系统中的定时炸弹,随时可能引发严重的故障。因此,深入理解并有效优化 Redis 中的大 Key 和热点 Key 问题,对于保障系统的高性能、高可用性和稳定性至关重要,这不仅是提升用户体验的关键,也是确保业务持续健康发展的必要条件。

Redis 大 Key 问题剖析

大 Key 的定义与表现形式

在 Redis 的世界里,大 Key 并没有一个绝对的标准定义,它是一个相对的概念,通常是指那些占用大量内存空间,或者包含大量元素,从而在操作时会耗费较多系统资源的数据项。一般而言,如果一个 Key 对应的 Value 占用的内存超过 1MB,或者对于 List、Hash、Set、ZSet 等集合类型,其包含的元素数量超过 5000 个 ,就可能被视为大 Key。当然,这个阈值并非一成不变,具体还需结合实际的业务场景和 Redis 服务器的配置来综合判断。

不同的数据结构下,大 Key 有着不同的表现形式。对于 String 类型,当它存储的是长文本、大 JSON 对象或者二进制数据(如图片、文件的二进制表示)时,就容易形成大 Key。例如,在一个内容管理系统中,如果将一篇几万字的长篇文章直接以 String 类型存储在 Redis 中,这个 Key 就极有可能成为大 Key。在电商场景中,若将一个包含商品详细信息(如商品 ID、名称、描述、价格、库存、图片 URL 等)的大 JSON 对象作为 String 类型的 Value 存储,随着商品信息的丰富和更新,也可能导致大 Key 的产生。

而对于 Hash 类型,当其中包含大量的字段和值时,就会出现大 Key 的情况。以用户信息管理为例,如果一个 Hash 结构存储了用户的基本资料、关系图谱计数、发 feed 统计等众多信息,且每个用户的信息不断丰富和扩展,随着时间的推移,这个 Hash 类型的 Key 就可能变得很大。

在社交类应用中,List 类型的大 Key 也较为常见。比如用于存储用户聊天记录的 List,随着用户之间交流的频繁进行,聊天记录不断增加,List 的长度持续变长,最终形成大 Key。当我们想要获取用户的聊天记录时,操作这个大 Key 就会变得非常耗时。

Set 类型通常用于存储一些不重复的元素集合,在统计类业务中,如果要记录每个用户每天的登录情况,以 Set 类型存储每个用户的登录日期,当用户量巨大且统计时间跨度较长时,Set 中的元素数量会不断增多,从而导致大 Key 的产生。

ZSet 类型因为其有序的特性,常用于排行榜等场景。假设一个游戏应用中的玩家排行榜,使用 ZSet 存储玩家的分数和排名信息,随着玩家数量的不断增加,以及游戏的持续运营,ZSet 中的元素越来越多,就会形成大 Key,对排行榜的更新和查询操作带来性能挑战。

大 Key 产生的原因

大 Key 的产生往往不是单一因素导致的,而是多种因素相互作用的结果。程序设计不当是导致大 Key 出现的常见原因之一。在开发过程中,开发者如果没有充分考虑到业务数据的增长趋势和访问模式,就可能会采用不合理的数据结构或存储方式。在一个文件存储系统中,开发者错误地选择使用 String 类型来存储大文件的二进制数据,随着文件数量的增加和文件大小的变化,这些 String 类型的 Key 就会占用大量内存,形成大 Key。在设计缓存结构时,如果没有对数据进行合理的分片或拆分,将大量相关数据存储在一个 Key 中,也会导致大 Key 的产生。

未及时清理垃圾数据也是大 Key 产生的一个重要因素。在 Redis 中,数据会随着业务的运行不断写入和更新,如果没有建立有效的数据清理机制,过期的或不再需要的数据就会持续占用内存空间。在一个哈希表中,随着业务逻辑的变化,某些键值对可能已经不再使用,但由于没有及时删除,这些无用的键值对不断积累,使得哈希表的大小不断膨胀,最终形成大 Key。对于一些时效性较强的数据,如临时的统计数据、缓存的过期页面等,如果没有设置合理的过期时间,也会导致内存占用不断增加,产生大 Key 问题。

数据结构选择不合理同样会引发大 Key 问题。每种数据结构都有其适用的场景和特点,如果在实际应用中选择了不合适的数据结构,就可能无法充分发挥 Redis 的性能优势,甚至导致大 Key 的产生。在需要统计独立 IP 访问量的场景中,如果使用 List 而不是更适合的 HyperLogLog 来存储 IP 地址,随着访问量的增加,List 中的元素会越来越多,占用大量内存,形成大 Key。因为 List 是一个列表结构,它会完整地存储每个元素,而 HyperLogLog 则是一种概率性数据结构,能够以极小的内存占用实现大规模数据的基数统计。

不同的业务场景也容易产生大 Key。在社交类业务场景中,以微博为例,一个拥有大量粉丝的明星或网红用户,其粉丝列表可能会包含数百万甚至上千万的粉丝 ID。如果将这个粉丝列表直接存储在一个 Redis 的 Set 或 List 中,就会形成一个非常大的 Key。当其他用户想要获取该明星的粉丝列表时,对这个大 Key 的操作就会变得极其缓慢,严重影响系统的响应速度。

统计类业务场景也常常面临大 Key 的困扰。比如在一个电商平台中,为了统计每个用户在一段时间内的购买行为,可能会将用户的购买记录存储在一个 Hash 类型的 Key 中,每个字段代表一次购买行为的详细信息。随着用户购买次数的增加和统计时间的延长,这个 Hash 中的字段数量会不断增多,最终形成大 Key。在进行数据分析或报表生成时,对这个大 Key 的操作会耗费大量的时间和资源,影响业务的正常运行。

缓存类业务场景同样容易出现大 Key 问题。在电商应用中,商品详情页的数据通常会被缓存到 Redis 中,以提高页面的加载速度。如果将商品的所有信息,包括商品名称、描述、图片、价格、库存、评论等全部存储在一个大 JSON 对象或大 Hash 中,当商品信息非常复杂或包含大量字段时,这个 Key 就可能变得非常大。当用户频繁访问商品详情页时,对这个大 Key 的读取操作会占用大量的内存和网络资源,导致系统性能下降。

大 Key 带来的问题

大 Key 的存在就像是一颗隐藏在系统中的定时炸弹,会给 Redis 的性能和稳定性带来诸多负面影响。大 Key 首先会占用过高的内存空间。Redis 是基于内存的数据库,内存资源非常宝贵。当大 Key 占用了大量内存时,会导致 Redis 实例的内存使用率迅速上升,可用于其他正常数据存储的内存空间就会相应减少。在极端情况下,如果内存被大 Key 耗尽,Redis 可能会触发内存淘汰策略,将一些重要的数据从内存中删除,以腾出空间给新的数据。然而,这种内存淘汰策略可能会导致业务数据的丢失或不一致,影响业务的正常运行。在一个在线交易系统中,如果与交易相关的关键数据因为内存淘汰而被删除,可能会导致交易失败、数据不一致等严重问题,给用户和企业带来巨大的损失。

大 Key 还会导致内存碎片的产生。由于大 Key 占用的内存块通常较大,当这些大 Key 被删除或修改时,会在内存中留下一些不连续的空闲内存块,即内存碎片。随着大 Key 的不断操作,内存碎片会越来越多,这会降低 Redis 的内存使用效率。即使系统中存在足够的空闲内存,由于内存碎片的存在,Redis 可能无法为新的数据分配足够的连续内存空间,从而导致内存分配失败。这进一步加剧了内存紧张的情况,可能导致更多的数据被淘汰,影响系统的性能和稳定性。

Redis 采用的是单线程模型,所有的命令都由一个主线程串行执行。当对大 Key 进行读写操作时,由于需要处理大量的数据,这些操作往往会变得非常耗时。在删除一个包含大量元素的 Hash 类型大 Key 时,Redis 需要逐个删除其中的键值对,这个过程会占用大量的 CPU 时间,导致主线程被阻塞。在主线程被阻塞期间,Redis 无法及时处理其他客户端的请求,客户端可能会因此遇到超时问题,导致服务体验下降。在一个高并发的 Web 应用中,如果大量客户端同时请求 Redis,而此时 Redis 正在处理一个大 Key 的操作,就会导致其他请求被阻塞,用户在浏览器中看到的页面加载缓慢甚至无法响应,严重影响用户体验。

网络拥塞也是大 Key 带来的一个严重问题。每次获取大 Key 时,都会产生较大的网络流量。如果大 Key 的访问频率很高,那么网络带宽可能会被迅速占满。在一个分布式系统中,Redis 实例与其他服务之间通过网络进行通信,当大 Key 导致网络拥塞时,不仅会影响 Redis 自身的性能,还会波及其他服务,导致整个系统的性能下降。在一个微服务架构的电商系统中,如果 Redis 中的大 Key 导致网络拥塞,那么订单服务、库存服务、支付服务等各个微服务之间的通信都会受到影响,可能会出现服务调用超时、数据同步失败等问题,进而影响整个电商系统的正常运行。

在 Redis 集群或主从复制环境中,大 Key 的同步可能会导致主从同步延迟。由于大 Key 占用较多的内存和数据量较大,在主从同步过程中,需要传输大量的数据。主节点在将大 Key 的更新同步到从节点时,可能会因为网络带宽限制、数据传输量大等原因,导致从节点无法及时接收到最新的数据,从而产生主从同步延迟。这种延迟可能会导致数据不一致的问题,影响系统的可靠性。在一个读写分离的数据库架构中,如果主从同步延迟较大,读操作从从节点获取的数据可能不是最新的,这对于一些对数据实时性要求较高的业务场景来说是无法接受的。

Redis 热点 Key 问题剖析

热点 Key 的定义与识别

热点 Key,从字面意思理解,就是在某段时间内被频繁访问的 Key。这些 Key 承载了大量的读写请求,其访问频率远远高于其他普通 Key。在电商平台的促销活动期间,热门商品的库存信息、价格信息等对应的 Key 就可能成为热点 Key。因为在促销活动时,大量用户会同时查询这些热门商品的相关信息,导致这些 Key 被频繁访问。在社交媒体平台上,当某个话题成为热门话题时,与该话题相关的帖子、评论等数据对应的 Key 也会成为热点 Key,大量用户会浏览、点赞、评论这些内容,使得这些 Key 的访问量急剧增加。

识别热点 Key 对于解决相关问题至关重要。在业务场景中,我们可以凭借对业务的深入理解和经验来初步判断热点 Key。在电商业务中,我们知道那些参与限时秒杀、折扣力度大的商品,以及品牌知名度高、销量一直名列前茅的商品,它们的相关数据 Key 极有可能成为热点 Key。在新闻资讯应用中,突发的重大新闻、热门事件的报道对应的 Key 也容易成为热点 Key。通过对业务数据的实时监测和分析,我们可以更准确地识别热点 Key。我们可以统计每个 Key 在一段时间内的访问次数,设置一个访问频率阈值,当某个 Key 的访问次数超过这个阈值时,就将其判定为热点 Key。

性能指标也是识别热点 Key 的重要依据。当 Redis 服务器的 CPU 占用率过高时,我们需要警惕是否存在热点 Key。因为大量对热点 Key 的请求会使 Redis 花费大量的 CPU 时间来处理这些请求,从而导致 CPU 占用率飙升。我们可以通过监控工具(如 Prometheus、Grafana 等)实时监测 Redis 服务器的 CPU 使用率。当发现 CPU 使用率持续居高不下,且排除了其他可能的原因(如大 Key 操作、内存不足等)后,就需要进一步排查是否是热点 Key 导致的。查看 Redis 的慢查询日志,如果某个 Key 的操作频繁出现在慢查询日志中,说明对该 Key 的操作耗时较长,这也可能是因为该 Key 是热点 Key,被大量请求访问,导致处理时间增加。通过分析这些性能指标,我们能够及时发现热点 Key,为后续的优化和处理提供依据。

热点 Key 产生的原因

热点 Key 的产生往往是多种因素共同作用的结果,其中预期之外的访问量陡增是一个主要原因。在电商领域,当一款商品成为爆款时,它的访问量会在短时间内呈现爆发式增长。这可能是由于商品本身的品质优秀、价格极具吸引力,或者是受到了广告宣传、社交媒体推荐等因素的影响。在某电商平台的 “618” 大促活动中,一款新推出的智能手机凭借其出色的性能和优惠的价格,吸引了大量用户的关注。活动开始后,该商品的浏览量、下单量瞬间飙升,与该商品相关的库存查询、价格获取等操作对应的 Key 成为了热点 Key。这些热点 Key 承载了大量的读写请求,给 Redis 服务器带来了巨大的压力。

社交媒体平台上,热点新闻的传播也会导致热点 Key 的产生。当一则重大新闻事件发生时,如明星绯闻、自然灾害、重大政策发布等,用户会迅速在社交媒体上搜索相关信息、发表评论和观点。以某明星的绯闻事件为例,事件曝光后,社交媒体平台上与该明星相关的话题、帖子、评论等数据的访问量急剧增加,这些数据对应的 Key 成为了热点 Key。大量用户的访问请求使得 Redis 服务器需要处理海量的读写操作,容易引发性能问题。

此外,程序中的代码逻辑问题也可能导致部分 Key 被频繁访问,从而形成热点 Key。在某些情况下,程序可能存在高频轮询的操作,例如定时任务每隔一段时间就会查询某个特定的 Key,以获取最新的数据。如果这个定时任务的执行频率过高,且涉及的 Key 没有进行合理的优化,就会导致该 Key 被频繁访问,成为热点 Key。在一个实时数据监控系统中,为了实时获取某个关键指标的数据,程序设置了每秒钟查询一次 Redis 中对应的 Key。随着时间的推移,这个 Key 的访问量不断累积,最终成为了热点 Key,影响了 Redis 服务器的性能。

还有一种情况是,程序中存在代码死循环或者不合理的算法,导致对某个 Key 的操作陷入无限循环或者需要大量的计算资源。在一个推荐系统中,如果算法设计不合理,在生成推荐结果时,需要频繁地查询 Redis 中某个用户的偏好数据 Key,并且这个查询操作在循环中不断执行,就会导致该 Key 被大量访问,形成热点 Key。这种由于代码逻辑问题导致的热点 Key,往往具有隐蔽性,需要开发人员仔细排查代码才能发现。

热点 Key 带来的问题

热点 Key 就像一颗隐藏在系统中的定时炸弹,一旦出现,会给系统带来一系列严重的问题。热点 Key 会导致 CPU 过载。由于 Redis 是单线程模型,所有的命令都由主线程串行执行。当大量请求集中在热点 Key 上时,Redis 需要不断地处理这些请求,包括读取数据、计算和返回结果等操作,这会占用大量的 CPU 时间片。在电商促销活动中,热门商品的库存查询请求频繁访问热点 Key,Redis 服务器的 CPU 使用率可能会迅速飙升到 90% 以上 。此时,CPU 资源被热点 Key 的请求大量消耗,其他非热点 Key 的请求无法及时得到处理,导致系统的整体性能下降,响应时间变长,用户在操作时会明显感觉到卡顿。

请求排队与阻塞也是热点 Key 带来的常见问题。当大量请求同时针对热点 Key 时,如果 Redis 的处理能力有限,这些请求会在队列中排队等待处理。在高并发场景下,热点 Key 的请求量可能远远超过 Redis 的处理能力,导致请求队列不断增长。在一个抢购活动中,大量用户同时请求购买热门商品,这些购买请求对应的热点 Key 操作会在 Redis 中排队。由于热点 Key 的处理占用了大量资源,其他非热点 Key 的请求也可能会被阻塞,无法及时得到处理。后续的请求可能会因为等待时间过长而超时,用户会收到请求超时的错误提示,严重影响用户体验。

热点 Key 还会导致响应延迟,这几乎是上述问题的必然结果。由于 CPU 过载和请求阻塞,系统对所有请求的响应时间都会显著增加。用户在访问应用时,原本快速加载的页面可能需要等待数秒甚至数十秒才能显示出来,点击按钮后也需要很长时间才能得到响应。在一个在线视频平台中,当某个热门视频成为热点时,大量用户同时请求播放该视频,与视频相关的热点 Key 操作会导致 Redis 响应延迟。用户在点击播放按钮后,视频可能需要很长时间才能开始播放,甚至出现加载失败的情况,这会让用户对平台的满意度大幅下降。

更严重的是,热点 Key 可能引发缓存与数据库被击穿,甚至导致系统雪崩。当热点 Key 的缓存过期时,大量请求会直接穿透到后端数据库。由于热点 Key 的访问量巨大,瞬间会给数据库带来巨大的压力,可能导致数据库崩溃。在一个电商系统中,热门商品的库存信息缓存过期后,大量的库存查询请求直接访问数据库。如果数据库无法承受如此高的并发请求,就会出现响应变慢、连接超时等问题,甚至可能直接宕机。一旦数据库宕机,依赖数据库的其他服务也会受到影响,进而引发连锁反应,导致整个系统崩溃,这就是所谓的系统雪崩。这种情况会对业务造成极大的影响,不仅会导致用户流失,还可能给企业带来巨大的经济损失。

Redis 大 Key 和热点 Key 的排查方法

大 Key 的排查方法

  1. 使用 SCAN + MEMORY USAGE 命令:Redis 提供的 SCAN 命令可以用于迭代数据库中的键,它是一个渐进式的命令,不会像 KEYS 命令那样一次性返回所有匹配的键,从而避免阻塞 Redis 的主线程。通过 SCAN 命令,我们可以逐步获取数据库中的键,然后结合 MEMORY USAGE 命令来获取每个键的内存占用情况。具体操作时,首先使用 SCAN 0 命令开始迭代,其中 0 是游标,表示从第一个键开始扫描,每次执行 SCAN 命令会返回一批键以及下一次扫描的游标。例如,执行 SCAN 0 MATCH user:* COUNT 100,其中 MATCH user:* 表示只匹配以 user: 开头的键,COUNT 100 表示每次返回最多 100 个键。然后,对于返回的每个键,执行 MEMORY USAGE {key} 命令,就可以得到该键的内存占用字节数。通过这种方式,我们可以遍历整个数据库,找出内存占用大的键。这种方法的优点是不需要额外的工具,直接利用 Redis 内置的命令即可实现,而且对 Redis 的性能影响较小,因为 SCAN 命令是渐进式的,不会长时间阻塞主线程。然而,它也存在一些缺点,由于 MEMORY USAGE 命令有一定的计算开销,在键数量非常多的情况下,扫描和计算内存占用的过程会比较耗时。在生产环境中使用时,需要谨慎选择扫描的频率和每次返回的键数量,以免影响 Redis 的正常运行。

  2. 使用 Redis - RDB - Tools 工具:Redis - RDB - Tools 是一个专门用于分析 Redis RDB 文件的工具。RDB 文件是 Redis 的一种持久化文件格式,它以快照的形式保存了 Redis 数据库中的数据。通过使用 Redis - RDB - Tools,我们可以将 RDB 文件解析,统计出每个键的类型、大小等信息,从而快速定位大 Key。使用时,首先需要导出 Redis 的 RDB 文件,这可以通过在 Redis 配置文件中设置 save 参数,让 Redis 按照指定的规则自动生成 RDB 文件,或者使用 SAVE 或 BGSAVE 命令手动生成。然后,运行类似 redis - rdb - tools -c memory /path/to/dump.rdb 的命令,其中 - c memory 表示按照内存占用情况进行统计,/path/to/dump.rdb 是 RDB 文件的路径。执行该命令后,工具会生成一份内存占用统计报告,其中会列出每个键的内存占用情况,我们可以根据报告轻松找到大 Key。这种方法的优势在于可以快速对整个 Redis 数据库的数据进行分析,一次性得到所有键的内存占用统计信息,而且对 Redis 服务器的运行状态影响较小,因为它是离线分析 RDB 文件,而不是直接在运行的 Redis 实例上进行操作。但是,它也有一些局限性,它依赖于 RDB 文件,如果 Redis 没有开启 RDB 持久化或者 RDB 文件不是最新的,那么分析结果可能不准确。此外,导出和分析 RDB 文件可能需要占用一定的磁盘空间和时间,对于生产环境中数据量非常大的 Redis 实例,操作起来可能不太方便。

  3. 其他工具和方法:除了上述两种常用方法外,还有一些其他工具和方法可以用于排查大 Key。一些云服务提供商(如阿里云、腾讯云等)提供的 Redis 管理控制台,通常会有可视化的界面来展示 Redis 实例的内存使用情况、键值对统计信息等,通过这些界面,我们可以直观地看到哪些键占用的内存较大。一些监控系统(如 Prometheus、Grafana 等)与 Redis 结合使用时,也可以实时监控 Redis 的内存使用情况,并设置阈值告警,当某个键的内存占用超过设定的阈值时,系统会发出告警通知,我们就可以进一步排查该键是否为大 Key。这些方法的优点是操作简单、直观,能够实时监控和及时发现大 Key 问题。但它们也需要一定的配置和部署工作,而且可能需要依赖特定的环境或服务。

热点 Key 的排查方法

  1. 通过内置统计信息(INFO keyspace):Redis 提供的 INFO 命令可以获取 Redis 服务器的各种信息,其中 INFO keyspace 可以获取键空间的统计数据。通过分析这些统计数据,我们可以得到每个数据库中键的数量、过期键的数量等信息,还可以通过计算命中率等指标来推断哪些 Key 可能是热点 Key。具体来说,我们可以执行 INFO keyspace 命令,得到类似如下的输出:

    # Keyspace
    db0:keys=1000,expires=100,avg_ttl=123456

其中 keys 表示数据库中键的数量,expires 表示过期键的数量,avg_ttl 表示平均生存时间。虽然 INFO keyspace 命令本身没有直接给出每个 Key 的访问频率信息,但我们可以结合其他工具或方法来间接分析热点 Key。例如,我们可以使用 MONITOR 命令实时监控 Redis 服务器接收到的命令,然后编写程序统计每个 Key 的访问次数,再结合 INFO keyspace 中的键数量等信息,计算出每个 Key 的命中率,命中率较高的 Key 就有可能是热点 Key。这种方法的优点是直接利用 Redis 内置的命令,不需要额外安装其他工具,而且可以获取到 Redis 服务器的整体统计信息。缺点是需要手动编写程序来统计和分析数据,操作相对复杂,而且对于高并发场景下的热点 Key 检测,可能会因为数据量过大而导致统计不准确。

2. 客户端采样:在应用程序代码中记录每次访问 Redis 时所涉及的 Key 及其频次,是一种简单有效的检测热点 Key 的方法。具体实现时,我们可以在 Redis 客户端代码中添加一段逻辑,每次执行 Redis 操作(如 GET、SET 等)时,将对应的 Key 记录下来,并增加该 Key 的访问计数。在 Java 中使用 Jedis 客户端时,可以在 Jedis 的操作方法中添加如下代码:

import redis.clients.jedis.Jedis;
import java.util.HashMap;
import java.util.Map;public class HotKeyDetector {private static final Map<String, Integer> keyCountMap = new HashMap<>();private Jedis jedis;public HotKeyDetector(Jedis jedis) {this.jedis = jedis;}public String get(String key) {// 记录访问计数keyCountMap.put(key, keyCountMap.getOrDefault(key, 0) + 1);return jedis.get(key);}// 其他操作方法类似,都需要添加记录访问计数的代码public Map<String, Integer> getKeyCountMap() {return keyCountMap;}
}

然后,我们可以定期(如每隔一段时间或在系统负载较低时)分析 keyCountMap,找出访问频次较高的 Key,这些 Key 就可能是热点 Key。这种方法的好处是实现简单,对业务代码的侵入性较小,而且可以准确地统计出每个 Key 在应用程序中的实际访问频次。但是,它也有一些不足之处,它依赖于应用程序代码的修改,如果应用程序中使用了多个 Redis 客户端或者有多种访问 Redis 的方式,可能需要在多个地方添加统计代码,维护成本较高。此外,统计的数据仅反映了应用程序自身的访问情况,对于一些通过其他途径(如缓存穿透、恶意攻击等)访问 Redis 的情况,可能无法准确检测到。

3. AOP 切面编程:AOP(Aspect - Oriented Programming,面向切面编程)是一种编程思想,它可以将一些横切关注点(如日志记录、事务管理、性能监控等)从业务逻辑中分离出来,以提高代码的可维护性和可扩展性。在检测热点 Key 时,我们可以利用 AOP 切面编程,为 Redis 调用添加环绕增强逻辑,收集热点 Key 的相关信息。以 Spring 框架为例,首先定义一个切面类:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;import java.util.HashMap;
import java.util.Map;@Aspect
@Component
public class RedisHotKeyAspect {private static final Map<String, Integer> keyCountMap = new HashMap<>();@Around("execution(* redis.clients.jedis.Jedis.*(..)) && args(key,..)")public Object monitorRedisOperation(ProceedingJoinPoint joinPoint, String key) throws Throwable {// 记录访问计数keyCountMap.put(key, keyCountMap.getOrDefault(key, 0) + 1);return joinPoint.proceed();}public Map<String, Integer> getKeyCountMap() {return keyCountMap;}
}

在上述代码中,通过 @Around 注解定义了一个环绕通知,当调用 Jedis 的任何方法且方法参数包含 Key 时,都会执行 monitorRedisOperation 方法,在该方法中记录 Key 的访问计数。然后,我们可以通过调用 RedisHotKeyAspect 的 getKeyCountMap 方法来获取统计结果,分析热点 Key。这种方法的优势在于对业务代码的侵入性最小,不需要在每个 Redis 操作的地方添加统计代码,只需要定义一个切面类即可实现对所有 Redis 调用的监控。而且,它基于 AOP 框架,具有良好的扩展性和维护性。然而,它也依赖于 AOP 框架的支持,如果项目没有使用 AOP 框架或者使用的框架不支持切面编程,就无法使用这种方法。此外,与客户端采样类似,它也只能统计应用程序内部通过正常途径访问 Redis 的情况,对于外部异常访问可能无法检测。 4. 专用监控工具(如 RedisInsight):RedisInsight 是 Redis 官方提供的一款图形化管理工具,它提供了丰富的功能来管理和监控 Redis 实例。在检测热点 Key 方面,RedisInsight 可以实时展示 Redis 实例的各种性能指标,包括每个 Key 的访问频率。通过 RedisInsight 的界面,我们可以直观地看到哪些 Key 被频繁访问,从而快速定位热点 Key。使用 RedisInsight 时,首先需要将其连接到目标 Redis 实例,然后在界面中找到相关的监控面板,通常在 “Overview” 或 “Performance” 等选项卡中可以找到关于 Key 访问频率的统计信息。除了 RedisInsight,还有一些其他的专用监控工具,如 Datadog、New Relic 等,它们也提供了对 Redis 的监控功能,可以检测热点 Key。这些工具通常具有更强大的数据分析和可视化功能,能够生成各种报表和图表,帮助我们更全面地了解 Redis 的运行状态和热点 Key 的情况。专用监控工具的优点是操作简单、直观,能够实时、准确地检测热点 Key,而且提供了丰富的数据分析和可视化功能,便于我们进行性能优化和问题排查。缺点是一些工具可能需要付费使用,而且需要一定的配置和学习成本,对于一些小型项目或简单应用场景来说,可能不太适用。

Redis 大 Key 和热点 Key 的优化策略

大 Key 的优化策略

  1. 数据模型优化:重构数据模型是解决大 Key 问题的关键一步。我们可以将大型数据集拆分成多个小部分,避免单个 Key 过于庞大。在电商商品信息缓存场景中,假设我们原来将商品的所有信息(包括商品 ID、名称、描述、价格、库存、图片 URL、评论等)都存储在一个大的 Hash 类型 Key 中,随着商品信息的不断丰富和更新,这个 Key 可能会变得非常大,导致操作效率低下。我们可以将商品信息进行拆分,将基本信息(如商品 ID、名称、价格)存储在一个 Hash 中,将商品描述存储在一个 String 类型的 Key 中,将库存信息存储在一个单独的 Key 中,将评论存储在 List 或 Sorted Set 中。这样,每个 Key 的大小都得到了有效控制,操作起来更加高效。例如,对于商品 ID 为 1001 的商品,我们可以创建以下几个 Key:

商品基本信息:product:1001:base
商品描述:product:1001:description
商品库存:product:1001:stock
商品评论:product:1001:comments

通过这种方式,不仅降低了单个 Key 的大小,还提高了数据的可维护性和扩展性。当我们需要更新商品的库存信息时,只需要操作product:1001:stock这个 Key,而不会影响到其他信息。当查询商品信息时,也可以根据实际需求选择性地获取相关信息,减少不必要的数据传输和处理。 2. 渐进式删除 / 更新:对于需要删除或更新的大 Key,采用分批处理的方式可以有效减轻即时压力。在删除一个包含大量元素的 Hash 类型大 Key 时,如果直接使用 DEL 命令,可能会导致 Redis 阻塞,影响其他请求的处理。我们可以使用 HSCAN 命令分批获取 Hash 中的字段和值,然后逐步删除。以 Python 代码为例:

import redisr = redis.StrictRedis(host='localhost', port=6379, db=0)
big_key = "large_hash_key"
cursor = "0"while cursor != "0":cursor, data = r.hscan(big_key, cursor=cursor, count=100)for field, value in data.items():r.hdel(big_key, field)

在上述代码中,我们使用hscan命令每次获取 100 个字段和值,然后使用hdel命令逐个删除这些字段。通过这种方式,将大 Key 的删除操作分散到多个小操作中,避免了一次性删除大量数据导致的阻塞问题。同样,在更新大 Key 时,也可以采用类似的方式,先获取部分数据进行更新,然后逐步完成整个大 Key 的更新。

3. 定期清理:设定合理的过期时间对于自动清除不再需要的大 Key、释放内存空间至关重要。在 Redis 中,可以使用 EXPIRE 命令为 Key 设置过期时间。对于一些临时的统计数据、缓存的过期页面等,我们可以根据业务需求设置较短的过期时间,如几分钟或几小时。在一个新闻资讯应用中,缓存的新闻详情页面可能只需要保留 1 小时,超过这个时间后,用户再次访问时可能会获取新的内容。我们可以这样设置过期时间:

SET news:12345 "新闻详情内容"
EXPIRE news:12345 3600  # 设置过期时间为1小时(3600秒)

在 Redis 配置文件中,可以通过maxmemory-policy参数设置内存淘汰策略,当内存达到最大限制时,Redis 会根据设置的策略自动删除一些 Key,以释放内存空间。常见的内存淘汰策略有volatile-lru(从设置了过期时间的键中选择最近最少使用的键进行删除)、allkeys-lru(从所有键中选择最近最少使用的键进行删除)等。根据业务场景选择合适的内存淘汰策略,可以确保在内存紧张时,优先删除那些不太重要或过期的大 Key,保证系统的正常运行。

4. 压缩存储:对于大对象,可以使用压缩技术(如 GZIP)来节省内存。在 Java 中,使用 GZIP 进行压缩和解压缩的示例代码如下:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;public class CompressionUtil {public static byte[] compress(byte[] data) throws IOException {ByteArrayOutputStream bos = new ByteArrayOutputStream();GZIPOutputStream gzip = new GZIPOutputStream(bos);gzip.write(data);gzip.close();return bos.toByteArray();}public static byte[] decompress(byte[] compressedData) throws IOException {ByteArrayInputStream bis = new ByteArrayInputStream(compressedData);GZIPInputStream gis = new GZIPInputStream(bis);ByteArrayOutputStream bos = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len;while ((len = gis.read(buffer)) != -1) {bos.write(buffer, 0, len);}gis.close();bos.close();return bos.toByteArray();}
}

在将大对象存储到 Redis 之前,先调用compress方法对数据进行压缩,然后将压缩后的数据存储到 Redis 中。当从 Redis 中读取数据时,再调用decompress方法进行解压缩。通过这种方式,可以显著减少大对象在 Redis 中占用的内存空间,提高内存使用效率。但需要注意的是,压缩和解压缩操作会带来一定的 CPU 开销,在选择是否使用压缩存储时,需要综合考虑内存节省和 CPU 性能之间的平衡。

热点 Key 的优化策略

  1. 流量整形:设置限流器可以有效地限制单位时间内针对特定 Key 的最大请求数量,防止过载。Guava RateLimiter 是一个基于令牌桶算法的限流器,使用非常方便。在 Java 中使用 Guava RateLimiter 的示例代码如下:

import com.google.common.util.concurrent.RateLimiter;public class HotKeyRateLimiter {private static final RateLimiter rateLimiter = RateLimiter.create(10); // 每秒最多处理10个请求public static boolean tryAcquire() {return rateLimiter.tryAcquire();}
}

在上述代码中,RateLimiter.create(10)表示每秒生成 10 个令牌,tryAcquire方法用于尝试获取一个令牌。如果获取到令牌,则表示请求可以继续处理;如果没有获取到令牌,则表示请求被限流,需要进行相应的处理(如返回错误信息、提示用户稍后再试等)。通过这种方式,可以将针对热点 Key 的请求流量控制在一个合理的范围内,避免因请求过多导致 Redis 服务器过载。除了 Guava RateLimiter,还可以使用基于 Redis 和 Lua 脚本的分布式限流方案,通过原子操作确保多个节点之间的限流一致性,适用于分布式系统中的热点 Key 限流场景。

2. 预取机制:提前加载预计会变成热 Key 的数据是减少初次访问延迟的有效方法。在电商平台的促销活动前,我们可以根据历史数据和业务经验,预测哪些商品可能会成为热门商品,然后提前将这些商品的相关信息(如库存、价格、详情等)加载到 Redis 中。可以在活动开始前的一段时间内,通过定时任务或者手动触发的方式,将预测的热点数据从数据库中读取出来,存储到 Redis 中。在 Java 中,使用定时任务进行预取的示例代码如下:

import java.util.Timer;
import java.util.TimerTask;
import redis.clients.jedis.Jedis;public class HotKeyPrefetch {private static final String[] HOT_KEYS = {"product:1001", "product:1002", "product:1003"};private static final Jedis jedis = new Jedis("localhost", 6379);public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {for (String key : HOT_KEYS) {// 从数据库中读取数据String data = loadDataFromDB(key);jedis.set(key, data);}}}, 0, 60 * 1000); // 每分钟执行一次预取操作}private static String loadDataFromDB(String key) {// 模拟从数据库中读取数据的操作return "模拟数据";}
}

通过预取机制,当用户访问热点 Key 时,数据已经在 Redis 中,大大减少了初次访问的延迟,提高了用户体验。同时,还可以根据实际业务情况,动态调整预取的时间间隔和数据范围,以适应不同的业务需求。

3. 多副本部署:创建热 Key 的多个副本来分散读写压力,可以提高系统的容错性和可用性。在分布式系统中,可以使用一致性哈希算法将热点 Key 分布到多个 Redis 节点上,每个节点存储热点 Key 的一个副本。当客户端请求热点 Key 时,根据一致性哈希算法计算出对应的 Redis 节点,然后访问该节点上的副本。这样,多个客户端的请求可以被分散到不同的节点上,避免了所有请求都集中在一个节点上导致的压力过大问题。在电商平台中,对于热门商品的库存查询请求,可以将库存信息的热点 Key 分布到多个 Redis 节点上。当用户查询库存时,请求会被均匀地分配到不同的节点上,提高了系统的并发处理能力。同时,如果某个节点出现故障,其他节点上的副本仍然可以提供服务,保证了系统的可用性。为了保证多个副本之间的数据一致性,可以使用 Redis 的主从复制机制或者分布式事务来实现数据的同步更新。

4. 智能路由:根据实时负载动态调整请求路径,使请求均匀分布于不同 Redis 实例间,是一种高效的热点 Key 优化策略。可以通过一个负载均衡器(如 Nginx、HAProxy 等)来实现智能路由。负载均衡器实时监控各个 Redis 实例的负载情况(如 CPU 使用率、内存使用率、请求队列长度等),当有请求到达时,根据负载均衡算法(如轮询、加权轮询、最小连接数等)将请求转发到负载较轻的 Redis 实例上。在一个高并发的 Web 应用中,使用 Nginx 作为负载均衡器,配置如下:

upstream redis_cluster {server redis1.example.com:6379 weight=3;server redis2.example.com:6379 weight=2;server redis3.example.com:6379 weight=1;
}server {listen       80;server_name  example.com;location /redis {proxy_pass http://redis_cluster;}
}

在上述配置中,upstream定义了一个 Redis 集群,包含三个 Redis 实例,每个实例设置了不同的权重。server部分定义了一个虚拟主机,当客户端请求/redis路径时,Nginx 会根据权重将请求转发到不同的 Redis 实例上。通过这种智能路由方式,可以有效地将热点 Key 的请求均匀分布到不同的 Redis 实例上,提高系统的整体性能和稳定性。 5. 分布式架构:采用一致性哈希或其他算法分散热点数据,避免所有请求都指向同一台服务器,是解决热点 Key 问题的重要手段。一致性哈希算法通过将 Key 映射到一个固定的哈希环上,然后根据节点在哈希环上的位置来确定数据存储在哪个节点上。当有新的节点加入或节点故障时,一致性哈希算法可以保证只有少量的数据需要重新分布,从而减少了数据迁移的开销。在一个分布式缓存系统中,使用一致性哈希算法将热点数据分布到多个缓存节点上。假设我们有三个缓存节点 A、B、C,当一个热点 Key 被请求时,首先通过一致性哈希算法计算出该 Key 在哈希环上的位置,然后找到距离该位置最近的缓存节点,将请求转发到该节点上。如果节点 A 出现故障,一致性哈希算法会自动调整哈希环,将原本存储在节点 A 上的数据重新分布到其他节点上,保证系统的正常运行。除了一致性哈希算法,还可以使用其他分布式算法(如哈希槽算法)来实现热点数据的分散存储和访问,根据实际业务场景选择合适的算法可以提高系统的性能和可扩展性。

案例分析

实际项目中的大 Key 和热点 Key 问题

在我参与的一个电商项目中,Redis 主要用于缓存商品信息、用户购物车和订单数据等,以提高系统的响应速度和并发处理能力。在业务高峰期,系统出现了响应时间明显变长的情况,部分用户反馈页面加载缓慢,甚至出现超时错误。经过深入排查,发现了大 Key 和热点 Key 问题。

在商品信息缓存模块,我们最初将商品的所有信息,包括商品 ID、名称、描述、价格、库存、图片 URL、详细参数、用户评论等,都存储在一个大的 Hash 类型 Key 中。随着业务的发展,商品信息不断丰富,这个 Hash 类型的 Key 变得越来越大,部分热门商品的 Key 大小甚至超过了 10MB。当用户查询商品详情时,对这些大 Key 的操作非常耗时,严重影响了系统的响应速度。在一次大型促销活动中,某个热门商品的查询请求频繁出现超时,导致大量用户无法正常查看商品信息,影响了用户的购买决策。

热点 Key 问题也给系统带来了巨大的挑战。在促销活动期间,热门商品的库存信息对应的 Key 成为了热点 Key。大量用户同时查询商品库存和下单,导致对这个热点 Key 的访问量急剧增加,QPS 峰值达到了 5 万以上 。这使得 Redis 服务器的 CPU 使用率迅速飙升至 90% 以上,服务器负载过高,响应延迟从原本的几毫秒增加到了几百毫秒,甚至出现了部分请求超时的情况。由于热点 Key 的访问过于集中,还导致了 Redis 集群的负载不均衡,承载热点 Key 的节点压力过大,而其他节点则处于低负载状态。

优化方案的实施与效果评估

针对大 Key 问题,我们采取了数据模型优化的策略。将商品信息进行拆分存储,把商品的基本信息(如商品 ID、名称、价格)存储在一个 Hash 中,商品描述存储在一个 String 类型的 Key 中,库存信息存储在一个单独的 Key 中,用户评论存储在 List 或 Sorted Set 中。这样,每个 Key 的大小都得到了有效控制,操作效率大大提高。我们还对需要删除或更新的大 Key 采用了渐进式删除 / 更新的方式。在删除商品信息时,使用 HSCAN 命令分批获取 Hash 中的字段和值,然后逐步删除,避免了一次性删除大量数据导致的阻塞问题。

对于热点 Key 问题,我们首先设置了限流器来限制单位时间内针对热点 Key 的最大请求数量。使用 Guava RateLimiter,将热门商品库存查询的请求限制为每秒最多处理 1000 个,有效防止了过载。我们采用了多副本部署的方式,创建热点 Key 的多个副本来分散读写压力。通过一致性哈希算法将热点 Key 分布到多个 Redis 节点上,每个节点存储热点 Key 的一个副本。当客户端请求热点 Key 时,根据一致性哈希算法计算出对应的 Redis 节点,然后访问该节点上的副本。这样,多个客户端的请求可以被分散到不同的节点上,避免了所有请求都集中在一个节点上导致的压力过大问题。

优化前后系统性能指标的对比如下:

性能指标

优化前

优化后

CPU 使用率

90% 以上

稳定在 50% 左右

响应时间

几百毫秒,部分请求超时

平均响应时间降低至 10 毫秒以内

吞吐量

受热点 Key 和大 Key 影响,吞吐量较低

吞吐量提升了 3 倍以上

通过实施这些优化方案,系统的性能得到了显著提升。CPU 使用率得到了有效控制,稳定在一个合理的范围内,避免了因 CPU 过载导致的系统性能下降。响应时间大幅缩短,用户能够快速获取商品信息和完成订单操作,提高了用户体验。吞吐量的提升使得系统能够处理更多的并发请求,满足了业务高峰期的需求。优化方案还解决了 Redis 集群负载不均衡的问题,提高了系统的稳定性和可用性。在后续的业务运营中,系统再也没有出现因大 Key 和热点 Key 问题导致的性能瓶颈,保障了电商业务的持续稳定发展。

总结与展望

总结优化要点

在 Redis 的使用过程中,大 Key 和热点 Key 问题犹如隐藏在暗处的礁石,时刻威胁着系统的性能和稳定性。大 Key,以其占用大量内存空间、操作耗时的特点,不仅会导致内存资源紧张,还可能引发内存碎片化、阻塞单线程以及网络拥塞等一系列问题。而热点 Key,凭借高频的访问量,使得 CPU 资源被过度占用,进而导致请求排队、响应延迟,甚至可能引发缓存与数据库被击穿,造成系统雪崩。

为了有效地排查大 Key,我们可以借助 SCAN + MEMORY USAGE 命令,逐步遍历数据库中的键并获取其内存占用情况;也可以使用 Redis - RDB - Tools 工具,对 RDB 文件进行解析分析,从而快速定位大 Key。对于热点 Key 的排查,通过 INFO keyspace 获取键空间的统计数据,结合客户端采样、AOP 切面编程以及专用监控工具(如 RedisInsight)等方式,能够准确地识别出热点 Key。

在优化策略上,针对大 Key,我们可以从数据模型优化入手,将大型数据集拆分成多个小部分,避免单个 Key 过于庞大;采用渐进式删除 / 更新的方式,减轻即时压力;设定合理的过期时间,定期清理不再需要的大 Key,释放内存空间;对于大对象,还可以使用压缩存储技术,节省内存。对于热点 Key,流量整形通过设置限流器,限制单位时间内针对特定 Key 的最大请求数量,防止过载;预取机制提前加载预计会变成热 Key 的数据,减少初次访问延迟;多副本部署创建热 Key 的多个副本来分散读写压力,提高系统的容错性和可用性;智能路由根据实时负载动态调整请求路径,使请求均匀分布于不同 Redis 实例间;分布式架构采用一致性哈希或其他算法分散热点数据,避免所有请求都指向同一台服务器。

在实际应用中,我们需要综合考虑业务场景、系统架构以及性能需求等各种因素,灵活选择合适的优化策略。只有这样,才能确保 Redis 在高负载和复杂业务场景下高效运行,为用户提供优质的服务体验。

未来发展趋势

随着 Redis 技术的持续发展和应用场景的日益复杂,大 Key 和热点 Key 问题也将面临新的挑战和机遇。从硬件层面来看,随着内存技术的不断进步,内存容量的不断增大和成本的逐渐降低,可能会在一定程度上缓解大 Key 对内存资源的压力。但这并不意味着大 Key 问题会消失,因为即使内存充足,大 Key 带来的操作耗时、阻塞单线程等问题依然存在。而且,随着数据量的持续增长和业务复杂度的不断提高,大 Key 的规模和影响可能会进一步扩大。

在软件层面,Redis 自身的版本更新和功能改进也将对大 Key 和热点 Key 问题产生影响。未来的 Redis 版本可能会提供更高效的数据结构和命令,以优化对大 Key 和热点 Key 的处理。在数据结构方面,可能会出现更紧凑、更适合存储大规模数据的数据结构,减少大 Key 的内存占用和操作开销;在命令方面,可能会增加一些针对大 Key 和热点 Key 的专门操作命令,使得处理这些问题更加便捷和高效。随着云计算和分布式技术的不断发展,Redis 在分布式环境中的应用将更加广泛。这将使得大 Key 和热点 Key 问题在分布式场景下变得更加复杂,需要我们进一步研究和探索分布式环境下的优化策略,如如何在分布式系统中更有效地进行大 Key 的拆分和热点 Key 的分流,如何保证分布式系统中数据的一致性和可用性等。

人工智能和机器学习技术的发展也可能为解决大 Key 和热点 Key 问题提供新的思路和方法。通过机器学习算法对 Redis 的访问模式和数据分布进行分析和预测,我们可以提前发现潜在的大 Key 和热点 Key,并采取相应的预防措施。利用深度学习算法对大 Key 的数据进行压缩和编码,提高数据存储和传输的效率,或者通过人工智能技术实现对热点 Key 的智能路由和负载均衡,进一步优化系统性能。未来,大 Key 和热点 Key 问题的研究方向将围绕着如何结合新技术、新架构,从多个维度深入探索更加高效、智能的优化策略,以满足不断变化的业务需求和日益增长的数据处理挑战。

http://www.dtcms.com/a/557496.html

相关文章:

  • 大专物流管理专业职业发展指南
  • 徐州网站制作机构做猎头需要用到的网站
  • 石家庄做网站制作公司做公司点评的网站
  • Git指令集
  • 基于边缘信息提取的遥感图像开放集飞机检测方法
  • 前端基础知识---Promise
  • Java 基础——函数式编程
  • webkitx(Android WebView 最佳实践库)
  • 调查网站做调查不容易过横栏建设网站
  • 勐海县住房和城乡建设局网站南昌做网站费用
  • 感知上下文并可解释地预测合成致死药物靶点的大语言模型研究
  • AI研究-117 特斯拉 FSD 视觉解析:多摄像头 - 3D占用网络 - 车机渲染,盲区与低速复杂路况安全指南
  • 二级域名可以做网站吗免费个人博客网站模板下载
  • 复原大唐3d项目测试版
  • 2024年MySQL 下载、安装及启动停止教程(非常
  • 兰州百度网站建设百度网站关键词优化在哪里做
  • Redis——Windows安装
  • 微信网站开发视频教程免费的黄金软件
  • 【高级机器学习】0. Machine Learning 介绍
  • 昆明城乡和住房建设局网站网站做5级分销合法吗
  • .NETCore、.NET 7 和 RabbitMQ 的发布-订阅模式
  • Crashpad 在windows下编译和使用指南
  • 基于SpringBoot+Vue的农产品销售系统【协同过滤推荐算法+可视化统计】
  • 基于flet的一款windows桌面应用,实现了浏览图片、音乐、小说、各种资源的功能
  • 【开题答辩过程】以《基于微信小程序的线上讲座管理系统》为例,不会开题答辩的可以进来看看
  • 怎么做好网站建设新手怎么开传媒公司
  • 2025年8月AGI月评|AI开源项目全解析:从智能体到3D世界,技术边界再突破
  • CSP-J/S 2025 游记
  • 深入洞察:业务流程从概念到实践
  • realloc用法