返回顶部
分享到

Redis中对大Key进行处理方式

Redis 来源:互联网 作者:佚名 发布时间:2025-06-22 21:47:35 人浏览
摘要

什么是大key 很多铁子可能会认为大key,是这个key的值很大其实不是,而是key的value值很大一般对于下面这些我们可以称为大key. String 类型值大于10KB。 Hash、List、Set、Zset类型元素个数超过5000个

什么是大key

很多铁子可能会认为大key,是这个key的值很大其实不是,而是key的value值很大一般对于下面这些我们可以称为大key.

  • String 类型值大于10KB。
  • Hash、List、Set、Zset类型元素个数超过5000个。

大key会造成什么影响

这个大key主要会带来下面这几种影响:

  • 阻塞工作线程:如果我们使用del命令删除大key,会阻塞工作线程这样就没有办法处理其他客户端发过来的命令了。
  • 内存分布不均: 集群模型在slot分片均匀情况下会出现数据和查询倾斜的情况,部分有大key的Redis结点占用内存较多。
  • 客户端超时阻塞: Redis的工作线程只有一个,操作这个大key会比较耗时会阻塞Redis在客户端看来就说很久很久没有响应。
  • 引发网络阻塞: 每次获取大key产生的网络流量比较大,这对于网卡比较小的服务器是灾难性的。

如何找到大key

1.我们可以通过redis客户端提供的命令 redis-cli --bigkeys.来查看

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

[root@VM-4-17-centos redis]# redis-cli --bigkeys

 

# Scanning the entire keyspace to find biggest keys as well as

# average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec

# per 100 SCAN commands (not usually needed).

 

[00.00%] Biggest string found so far '"k1"' with 2 bytes

[00.00%] Biggest hash   found so far '"ksyi"' with 100 fields

 

-------- summary -------

 

Sampled 2 keys in the keyspace!

Total key length in bytes is 6 (avg len 3.00)

 

Biggest   hash found '"ksyi"' has 100 fields

Biggest string found '"k1"' has 2 bytes

 

0 lists with 0 items (00.00% of keys, avg size 0.00)

1 hashs with 100 fields (50.00% of keys, avg size 100.00)

1 strings with 2 bytes (50.00% of keys, avg size 2.00)

0 streams with 0 entries (00.00% of keys, avg size 0.00)

0 sets with 0 members (00.00% of keys, avg size 0.00)

0 zsets with 0 members (00.00% of keys, avg size 0.00)

注意:

  • 在使用这个命令来查询大key时,最好在从节点上执行。如果在主节点上执行会阻塞从节点。
  • 如果没有这个从节点,那么我们可以选择在Redis业务压力比较轻的时候执行避免影响正常的业务功能;或者我们可以使用**-i选项**来控制扫描间隔避免长时间扫描降低Redis的性能。

这个方式其实也有缺点,他之只能返回每种类型最大的那个bigkey,无法获得大小在前N位的bigkey。

2.使用memory usage命令进行查询

  • MEMORY USAGE命令给出一个key和它的值在RAM中所占用的字节数。返回的结果是key的值以及为管理该key分配的内存总字节数。
  • 对于嵌套数据类型,可以使用选项SAMPLES,其中count表示抽样的元素个数,默认值为5。当需要抽样所有元素时,使用SAMPLES 0 。

其语法如下:

1

MEMORY USAGE key [SAMPLES count]

下面我们来演示一下

1

2

127.0.0.1:6379> memory usage ksyi

(integer) 496

3.通过scan命令来查找大key

  • 我们可以先使用scan命令对数据库进行扫描,然后用type命令返回每一个key的类型。
  • 对于String类型,我们可以直接使用strlen命令获取字符串的长度,也就是占用内存的字节数。

如何删除大key

如果我们一下子释放大量的内存,空闲内存块链表操作时间会增加,相应地就会造成Redis主线程阻塞,如果redis主线程发生了阻塞其他客户端的请求可能会超时,如果超时的连接越来越多会造成各自异常问题。

因此我们删除大key这一个动作我们要特别的小心具体要怎么做这里给出两种方法:

  • 渐进式删除
  • 异步删除(unlink)

渐进式删除

1.对于string类型

对于这个String类型的大key我们可以使用del直接删除如果这个他确实很大我们可以使用unlink来进行异步删除 。这个特别简单在这里就不演示了。

2.对于Hash类型

对于删除大的Hash类型,我们可以使用hscan命令每次获取100个字段,这个个数根据业务来确定然后我们在使用hdel命令每次删除一个字段即可。

  • 对应golang代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

package main

 

import (

    "fmt"

    "github.com/go-redis/redis"

)

 

func DelBigHashKey(conn *redis.Client) {

    bigKey := "xxx"

    cursor := uint64(0)

    for {

 

        ScanRet := conn.HScan(bigKey, cursor, "", 100)

        keys, c, err := ScanRet.Result()

        if err != nil {

            panic(err)

        }

        fmt.Println(keys)

        for i := 0; i < len(keys); i++ {

            conn.HDel(bigKey, keys[i])

        }

        if c == 0 {

            break

        }

        cursor = c

    }

}

func main() {

    conn := redis.NewClient(&redis.Options{

        Addr:     "101.35.98.26:6379", // url

        Password: "",                  //redis密码

        DB:       0,                   // 0号数据库

    })

    _, err := conn.Ping().Result()

    if err != nil {

        fmt.Println("ping err :", err)

        return

    }

 

    DelBigHashKey(conn)

 

}

3.对应删除大的List,通过ltrim命令每次迭代删除少量元素

1

2

3

4

5

6

7

// DelBigListKey 删除大的Listkey

func DelBigListKey(conn *redis.Client) {

    bigKey := "xxx" //要删除的大key

    for conn.LLen(bigKey).Val() > 0 {

        conn.LTrim(bigKey, 0, -101) //每次删除最右边100个元素

    }

}

4.删除大的Set 可以先使用sscan获取部分元素,比如每次扫描集合当中100个元素在用srem命令每次删除一个键

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

func DelBigSetKey(conn *redis.Client) {

    bigKey := "xxx"     //要删除的大key

    cursor := uint64(0) //游标

    for {

        keys, c, err := conn.SScan(bigKey, cursor, "", 100).Result()

        if err != nil {

            panic(err)

        }

        cursor = c

        for i := 0; i < len(keys); i++ {

            fmt.Println(conn.SRem(bigKey, keys[i]).Val())

        }

        if c == 0 {

            //删除完毕

            break

        }

    }

}

对于删除Zset,使用zremrangebyrank命令每次删除top 100元素

1

2

3

4

5

6

func DelBigZsetKey(conn *redis.Client) {

    bigKey := "xxx"

    for conn.ZCard(bigKey).Val() > 0 {

        conn.ZRemRangeByRank(bigKey, 0, 99)

    }

}

以上就是针对大key删除的方案。

异步删除

除了上面的删除方案我们可以采用异步删除的方式可以使用unlink命令代替del来删除这样就不会阻塞主线程。

如何禁用keys*、flushdb 、flushall等命令

如果我们想要禁用这些命令我们可以在redis.conf 默认配置文件,找到 SECURITY 区域,如以下所示:

这样这些命令就被禁用了。

Redis有两个原语来删除键。一种称为DEL,是对象的阻塞删除。这意味着服务器停止处理新命令,以便以同步方式回收与对象关联的所有内存。如果删除的键与一个小对象相关联,则执行DEL命令所需的时间非常短,可与大多数其他命令相媲美

Redis 中的O(1)或O(log_N)命令。但是,如果键与包含数百万个元素的聚合值相关联,则服务器可能会阻塞很长时间(甚至几秒钟)才能完成操作。

基于上述原因,Redis还提供了非阻塞删除原语,例如UNLINK(非阻塞DEL)以及FLUSHALL和FLUSHDB命令的ASYNC选项,以便在后台回收内存。这些命令在恒定时间内执行。另一个线程将尽可能快地逐步释放后台中的对象。

FLUSHALL和FLUSHDB的DEL、UNLINK和ASYNC选项是用户控制的。这取决于应用程序的设计,以了解何时使用其中一个是个好主意。然而,作为其他操作的副作用,Redis服务器有时不得不删除键或刷新整个数据库。具体而言,Redis在以下场景中独立于用户调用删除对象:

我们可以将配置文件当中的这些参数设置为yes,也就是懒释放


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。

您可能感兴趣的文章 :

原文链接 :
相关文章
  • Redis中对大Key进行处理方式

    Redis中对大Key进行处理方式
    什么是大key 很多铁子可能会认为大key,是这个key的值很大其实不是,而是key的value值很大一般对于下面这些我们可以称为大key. String 类型值
  • 一文浅析如何在Redis中实现缓存功能
    Redis 是一种高性能的键值存储系统,广泛用于实现缓存功能。它通过将数据存储在内存中,能够快速读写数据,从而显著提高应用程序的性
  • Redis Cluster模式配置
    分片 一、分片的本质与核心价值 问题根源 单机 Redis 存在内存容量和吞吐量瓶颈,分片通过将数据分散到多个节点解决此问题。 核心价值
  • Redis中的Lettuce使用介绍
    Lettuce 是一个高级的、线程安全的 Redis 客户端,用于与 Redis 数据库交互。它提供了许多方法来配置连接池的参数,例如最大连接数、最小空
  • redis过期key的删除策略
    在使用redis的过程中,不免会产生过期的key,而这些key过期后并不会实时地马上被删除,当这些key数量累积越来越多,就会占用很多内存,因
  • React实现组件之间通信的几种常用方法
    React 中如何实现组件之间的通信? 1. Props 传递 最直接的通信方式是通过 props 将数据从父组件传递给子组件。父组件通过属性将数据传递给
  • redis-cli常用命令使用介绍
    1 redis-cli连接redis服务 1.1 无密码本地登录 1 2 3 4 5 redis-cli redis 127.0.0.1:6379 redis 127.0.0.1:6379 PING PONG 1.2 指定ip、端口、密码 1 redis-cli -h [ip] -p
  • K8s部署Redis主从集群的教程

    K8s部署Redis主从集群的教程
    一、环境准备 1.1 环境说明 本文搭建MongoDB,基于WMware虚拟机,操作系统CentOS 8,且已经基于Kubeadm搭好了k8s集群,k8s节点信息如下: 服务器
  • Redis连接池配置方式

    Redis连接池配置方式
    连接池配置 一、Redis连接池 与JDBC中在与数据库进行连接时耗时,从而需要引入连接池一样。 Java在与Redis进行连接也是需要时间的。所以在
  • 通过docker和docker-compose安装redis两种方式

    通过docker和docker-compose安装redis两种方式
    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 的 NoSQL 数据库,并提供多种语言的 API。这里介
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计