什么是大Key?如何解决大Key问题?

360影视 2025-02-06 15:00 3

摘要:所谓的大Key问题与热Key问题一样,通常会出现在分布式系统、数据库或者是缓存中,尤其是在进行一些大批量的数据处理的时候,由于单个Key值对应的数据量较大,可能会导致在查询、存储以及数据传输过程中耗费大量的资源,尤其是当某些请求集中访问某个大Key的时候,会导

所谓的大Key问题与热Key问题一样,通常会出现在分布式系统、数据库或者是缓存中,尤其是在进行一些大批量的数据处理的时候,由于单个Key值对应的数据量较大,可能会导致在查询、存储以及数据传输过程中耗费大量的资源,尤其是当某些请求集中访问某个大Key的时候,会导致系统性能急剧下降,甚至会出现延迟超时等情况。

  另外就是在通过Redis进行缓存的时候,由于Redis的数据存储是基于内存的数据存储如果存储了一个大Key的话可能会导致大量的内存资源被占用,从而影响缓存的效率以及其他小Key的访问性能。

  在一些分布式系统中,大Key可能会导致数据倾斜,导致某些节点的负载过重,而其他节点的负载较少,从而影响到整体的系统负载。

数据拆分

  假设在Redis缓存系统中我们来存储用户信息,并且某些用户信息可能会涉及到较多的历史数据、日志、消息等,这就会导致Redis中的某些缓存数据会非常大从而影响到系统性能。面对这种问题,我们一般的解决方案是将数据进行拆分,将数据拆分成小块之后再进行缓存,这样就可以减小数据缓存大小。

  例如,可以将某个用户的历史消息按月份或按天拆分成多个 Key:user:{user_id}:messages:2023-01,user:{user_id}:messages:2023-02,这样每个 Key 只存储某一时间段的数据,避免了单个大Key的问题。

使用Redis Hash结构

  如果在某个缓存中存储的数据信息量较大的时候,我们可以通过Redis的Hash结构来存储这些信息,例如将用户的各项数据字段作为 Hash 的 field-value 存储。如下所示,user:{user_id}:info 这个 Key 对应的是一个Hash,其中包含 name, email, phone 等字段。这样,即使信息量大,也不会因为单一的Key存储而影响系统性能。

数据压缩

 在某些场景下可能会涉及到缓存用户的操作日志、用户图片、用户文件等,这个时候我们可以通过某些数据压缩算法将数据进行压缩之后存储到Redis中,例如比较常见的如 gziplz4,当我们在使用数据时候在进行解压缩操作,这样可以减少Redis的内存消耗以及网络传输带宽的消耗。

使用Redis分区

  在某些社交场景中可能会遇到用户数量过于庞大的情况,并且每个用户存储的信息量也比较大,这种情况下就有可能导致Redis的缓存压力过大,这种情况下,我们可以将Redis数据分成多个分片,然后通过Redis Cluster进行分布式存储。通过对于合理的分片策略的设置来避免单节点负载过大的情况。

  假设我们使用的是MySQL来存储一些用户交易记录和一些交易流水日志,这些数据是一个非常庞大的数据体量,在某些场景中可能会导致查询变得非常缓慢,尤其是当单条记录数据内容过长的时候,这种影响会非常明显。面对这种情况我们一般采取的策略有如下一些。

分表策略

  由于用户交易记录会越来越多,可能会导致某个单表的数量过于庞大,这个时候,我们可以根据用户ID或者是创建时间来进行分表存储,例如transactions_2023_01 存储 2023 年 1 月的交易记录,transactions_2023_02 存储 2023 年 2 月的交易记录。这样可以有效避免单个表数据量过大,查询时也能更快。

表字段拆分

  在存储一些交易明细的时候,可能会将为了方便讲这些明细信息存储成JSON文本,这种情况下由于数据量较大,数据内容较多就会导致查询效率较慢。为了解决这个问题,我们可以将长文本的内容存储到单独的表中,或者是某些非结构化的数据库中,例如存储用户交易记录时,只存储简要信息如交易 ID、时间、金额等到主表,而将详细的交易内容如详细商品信息、交易描述等存储到一个单独的表或外部存储中。这样可以提高列表的查询效率。在需要的时候在进行详情的查看。

数据归档

  当数据库中存在了大量的过时的数据的时候,我们可以将这些数据定期的进行历史数据归档,存储到一些冷数据存储中例如HDFS云存储中,这样可以有效的提高热数据的查询效率,在需要的时候,在提供历史数据的查询操作,例如,将超过 1 年的交易记录从活跃数据库中移除,只保留当前活跃的交易记录在数据库中,历史数据则存储到冷存储中,按需查询。

  假设,在使用一些分布式队列的时候,例如KafkaRabbitMQ,消息中可能会包含有大量的数据传输,例如用户的完整行为日志、视频文件等,这种情况下如果处理不及时就可能会导致消息队列的数据挤压,以及处理延迟等问题,而常见的解决方案有如下一些。

消息拆分

  与之前数据拆分的逻辑是一样的,如果某个消息体中包含了大量数据,导致消息数据处理较慢,我们可以将一个大消息拆分成多个小消息,例如大文件处理,可以将文件拆分成几个片段,然后传输完成之后再进行合并,这样就可以避免单个大消息造成的系统传输负担。

压缩消息内容

  如果消息内容过大,我们可以通过Snappygzip等技术来对消息进行压缩,这样可以减少在传输过程中的网络带宽消耗。

设置合理的消息过期时间

  可以为消息设置合理的过期时间,这样可以确保消息能够及时的从队列中移除,避免出现消息积压导致消息队列性能问题。

  假设我们通过 Elasticsearch 来存储和搜索大量的日志数据,比如说比较流行的ELK系统,当某些日志条目的内容非常大,可能就会导致查询和索引的性能下降。这种情况下,我们可以通过如下的一些方案来提高系统查询性能。

日志数据的字段拆分

  前面提到的,当某些日志数据内容较多的时候可能涉及到通过JSON数据记录,这个时候,我们可以将日志进行拆分,提取关键信息进行存储,避免过度的存储无用信息。例如,将日志中的元数据和具体内容分别存储在不同的字段中,而不是把所有内容存储在一个大字段里。

日志的索引优化

  对日志数据中的字段进行 映射优化,避免不必要的字段被索引。对于文本类型的字段,选择合适的分词器进行优化,减少无意义的索引。

滚动索引(Index Rollup)

  对于日志数据量巨大而导致单一索引的查询和存储成本增加的情况,我们可以使用 Elasticsearch 的 Index Rollup 技术,对历史日志进行汇总处理,这样我们就可以保存摘要信息,将详细的日志数据定期归档或删除。通过这种方式来保留重要的汇总信息,就可以有效的减少存储和查询的压力。

  根据上面的描述,不同的使用场景有着不同的大Key解决方案,具体方案涉及到数据拆分、压缩、分片、优化数据存储结构等方式。但是关键还是要了解数据特点,根据数据特点来合理设计数据模型和存储策略,避免单个大Key带来的性能瓶颈。

来源:从程序员到架构师

相关推荐