Redis 分批删除字符串键操作

这篇具有很好参考价值的文章主要介绍了Redis 分批删除字符串键操作。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Redis 分批删除字符串

  • 有时候我们需要清理一些非常大的key(如hash键),或者通配非常多的(如string类型), 如果直接使用keys、del操作会对线上的redis有性能影响,一般建议使用unlink 异步删除操作,释放交给redis自身去处理,但也有一些场景,可能需要快速释放内存,或者通配去删除 ,或者针对哈希表的某些item进行删除,这时候可以通过 SCAN 操作,再匹配后删除。
  • 删除字符串时,同时要注意字符串ket的内存使用是否过大,删除过大也会影响到redis性能,下面提供按批量的删除方法,在删除时观察监控指标, CPU利用率是否在合理范围

Python

使用python 的 redis 模块,可以对sentinel 、 cluster 模式创建客户端

from redis.sentinel import Sentinel
from redis.cluster import RedisCluster, ClusterNode

通过SCAN的方式来删除, 设置 内存大小 10KB (按实际调整大小), match 为匹配的键,比如: user* , redis_server为创建的客户端,可以通过 Sentinel类、 RedisCluster 类 和实例化对象。 time.sleep控制每次操作间隔,建议可以先每500ms清理1000个,观察性能再做适当调整。代码参考如下:

def del_by_scan(redis_server, match, count, memory_usage=10240):
    cursor = '0'
    success, skip = 0, 0
    print(f"begin to del_by_scan={match}")
    while cursor != 0:
        cursor, data = redis_server.scan(cursor=cursor, match=match, count=count)
        for key in data:
            memory = redis_server.memory_usage(key)
            if memory > memory_usage:
                print(f"scan key: {key}, type({key}): {redis_server.type(key)}, memory usage: {memory}B > 1.2MBB, skip.")
                skip += 1
            else:
                print(f"scan key: {key}, type({key}): {redis_server.type(key)}, memory usage: {memory}B will be delete.")
                redis_server.delete(key)
                success += 1
        time.sleep(1)
    print(f"delete scan key: {match}, Done ! success: {success}, skip: {skip} ")

插入1000条测试数据,然后执行批量删除 user* 开头的 string 类型,每1秒删除100个(仅测试),实际可以调大些(在保证性能前提下) , 测试效果如下:memory_usage=49用于测试调很小。实际建议10KB左右。

del_by_scan(redis_server, "user*", 100memory_usage=49)

删除后执行日志如下:成功删除 999条。 跳过1条(超过设置的大小)。

2023-07-07 13:27:13:begin to del_by_scan=user*
2023-07-07 13:27:13:scan key: b'user991', type(b'user991'): b'string', memory usage: 49B will be delete.
2023-07-07 13:27:13:scan key: b'user589', type(b'user589'): b'string', memory usage: 49B will be delete.
2023-07-07 13:27:13:scan key: b'user685', type(b'user685'): b'string', memory usage: 49B will be delete.
......
2023-07-07 13:27:19:scan key: b'user1000', type(b'user1000'): b'string', memory usage: 50B > 49B, skip.
......
2023-07-07 13:27:25:delete scan key: user*, Done ! success: 999, skip: 1 

后端调用是Redis SCAN 命令,参考如下:

1688706474.976630 [0 192.168.88.1:65060] "SCAN" "0" "MATCH" "user*" "COUNT" "100"
1688706475.020499 [0 192.168.88.1:65060] "SCAN" "152" "MATCH" "user*" "COUNT" "100"
1688706475.077715 [0 192.168.88.1:65060] "SCAN" "972" "MATCH" "user*" "COUNT" "100"
1688706475.124888 [0 192.168.88.1:65060] "SCAN" "850" "MATCH" "user*" "COUNT" "100"
1688706475.178739 [0 192.168.88.1:65060] "SCAN" "230" "MATCH" "user*" "COUNT" "100"
1688706475.229983 [0 192.168.88.1:65060] "SCAN" "702" "MATCH" "user*" "COUNT" "100"
1688706475.281272 [0 192.168.88.1:65060] "SCAN" "873" "MATCH" "user*" "COUNT" "100"
1688706475.338270 [0 192.168.88.1:65060] "SCAN" "653" "MATCH" "user*" "COUNT" "100"
1688706475.390810 [0 192.168.88.1:65060] "SCAN" "83" "MATCH" "user*" "COUNT" "100"
1688706475.443487 [0 192.168.88.1:65060] "SCAN" "71" "MATCH" "user*" "COUNT" "100"
1688706475.501230 [0 192.168.88.1:65060] "SCAN" "191" "MATCH" "user*" "COUNT" "100"

Go

使用Go Redis支持Redis Server和Redis Cluster的Golang客户端的: “github.com/redis/go-redis/v9” 处理方式和Python相似,都是通过SCAN扫描后执行分批删除。 代码参考如下:


func scanKey(ctx context.Context, rdb *redis.Client, pattern string, batchSize int64, memoryUsage int64) error {
	cursor, total, success, skip := uint64(0), uint(0), uint(0), uint(0)
	var err error
	addr := rdb.Options().Addr
	for {
		var result []string
		result, cursor, err = rdb.Scan(ctx, cursor, pattern, batchSize).Result()
		if err != nil {
			log.Printf("could not scan: %q\n", err)
			return err
		}
		total += uint(len(result))
		for i := 0; i < len(result); i++ {
			key := result[i]
			keyType := rdb.Type(ctx, key).Val()
			memory := rdb.MemoryUsage(ctx, key).Val()
			if memory > memoryUsage {
				log.Printf("redis: %s, scan key: %s, type: %s, memory usage: %dB > %dB, skip.", addr, key, keyType, memory, memoryUsage)
				skip++
			} else {
				if _, err = rdb.Del(ctx, key).Result(); err != nil {
					log.Printf("redis: %s, delete string key: %s failed: %q", addr, key, err)
					return err
				}
				log.Printf("redis: %s, key: %s, type: %s, memory usage: %dB, deleted.", addr, key, keyType, memory)
				success++
			}
		}
		if cursor == 0 {
			break
		}
		time.Sleep(1000 * time.Millisecond)
	}
	log.Printf("redis: %s, All scan key=%s Done ! total: {%d}, success: {%d}, skip: {%d}", addr, pattern, total, success, skip)
	return err
}

调用删除函数,传入相关参数

client := redis.NewClient(&redis.Options{
		Addr:        address,
		Password:    password,
		DB:          db,
		ReadTimeout: 1 * time.Minute,
	})
	
scanKey(ctx, client, "user*", 300, 49)

执行删除,相关日志参考如下:

2023/07/07 13:07:56 redis server connect success: "PONG", address: "192.168.88.11:6379"
2023/07/07 13:07:56 redis: 192.168.88.11:6379, key: user569, type: string, memory usage: 49B, deleted.
2023/07/07 13:07:56 redis: 192.168.88.11:6379, key: user627, type: string, memory usage: 49B, deleted.
2023/07/07 13:07:56 redis: 192.168.88.11:6379, key: user436, type: string, memory usage: 49B, deleted.
......
2023/07/07 14:29:51 redis: 192.168.88.11:6379, scan key: user1000, type: string, memory usage: 50B > 49B, skip.
......
2023/07/07 14:29:54 redis: 192.168.88.11:6379, All scan key=user* Done ! total: {1000}, success: {999}, skip: {1}

后端同样调用SCAN命令,参考如下文章来源地址https://www.toymoban.com/news/detail-556981.html

1688712114.495473 [0 192.168.88.11:46774] "scan" "0" "match" "user*" "count" "300"
1688712115.620560 [0 192.168.88.11:46774] "scan" "594" "match" "user*" "count" "300"
1688712116.758503 [0 192.168.88.11:46774] "scan" "617" "match" "user*" "count" "300"
1688712117.888501 [0 192.168.88.11:46774] "scan" "391" "match" "user*" "count" "300"

总结

  • 对于大批量的key做删除,或者针对某几个大key删除(如HASH, SET )类型等的操作,建议使用分批删除
  • 删除时控制每批的数量、控制间隔时间,字符串同时考虑其内存大小,删除过大时会影响到redis性能
  • 对于非字符串,其它类型如HASH也提供了HSCAN方式扫描元素,操作方式同上

到了这里,关于Redis 分批删除字符串键操作的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】string字符串查找替换、比较、提取、插入和删除

    Link 加油! 感谢! 努力!

    2024年02月12日
    浏览(45)
  • Java 字符串中删除子字符串的9种方法详细内容(remove substring from String)

    Java 中的字符串中删除子字符串有以下方法: 1.Using replace method使用替换方法 2.Using Charsequence使用字符序列 3.Replace the Substring with an empty string用空字符串替换子字符串 4.Using String’s replaceFirst method使用 String 的 replaceFirst 方法 5.replaceFirst() method 6.Using replaceAll method 使用 replaceAll 方

    2024年02月16日
    浏览(40)
  • String字符串,FastJson常用操作方法

    1、创建配置环境 注意 测试的时候需要更改一下idea的设置 2、FastJson简介 3、 序列化 测试 JSON.toJSONString(序列化java对象) 4、 反序列化 5、枚举介绍 6、JSONField注解的使用 1、注解 2、作用于字段上面 3、格式化日期时间 4、指定字段不序列化 5、指定字段顺序 6、自定义序列化内容

    2024年02月21日
    浏览(55)
  • Redis原理:动态字符串SDS

    (课程总结自b站黑马程序员课程) Redis中保存的Key是字符串,value往往是字符串或者字符串的集合。可见字符串是Redis中最常用的一种数据结构。 不过Redis没有直接使用C语言中的字符串,因为C语言字符串存在很多问题: ①获取字符串长度的需要通过运算 ②非二进制安全:指

    2024年02月09日
    浏览(36)
  • 【Redis】关于Redis数据结构简单动态字符串(SDS)的一些杂记

    推荐几篇关于SDS数据结构讲解较为详细的文章: 一、简单动态字符串 — Redis 设计与实现 (redisbook.readthedocs.io) 二、深入理解Redis之简单动态字符串 - itbsl - 博客园 (cnblogs.com) 三、Redis内部数据结构详解(2)——sds - 铁蕾的个人博客 (zhangtielei.com) 四、简单动态字符串 — Redis 设计与

    2023年04月14日
    浏览(44)
  • Redis源码之SDS简单动态字符串

    Redis 是内存数据库,高效使用内存对 Redis 的实现来说非常重要。 看一下,Redis 中针对字符串结构针对内存使用效率做的设计优化,版本为Redis3.2。       c语言没有string类型,本质是char[]数组;而且c语言数组创建时必须初始化大小,指定类型后就不能改变,并且字符数组的最

    2023年04月11日
    浏览(37)
  • redis 字符串类型常用场景以及相关示例

    可以先看一下系列第一篇文章 字符串可以用作缓存键值对的存储方式。通过将经常使用的数据存储为字符串,可以提高读取速度,并减少数据库等后端系统的负载 数据库查询结果的缓存 场景 频繁读取相同数据 当某个数据被频繁地读取,而且这些读取操作对应的数据库查询

    2024年02月16日
    浏览(33)
  • Java中的String类的常用方法(对于字符串的常用操作)

    目录 一、获取指定索引的字符 二、 获取指定字符或者字符串的索引位置 三、判断字符串是否以指定内容开头或结尾 四、替换指定的字符或者是字符串 五、获取字符串的子串 六、将字符串转换为字符数组  七、比较字符串的内容是否相等  八、连接字符串 九、比较两个字

    2024年02月20日
    浏览(73)
  • CMake String函数:如何巧妙地在cmake中操作字符串

    在CMake中,我们可以通过多种方式创建和赋值字符串。下面是两种常见的方法: 使用 set 命令:这是创建和赋值字符串的最直接方式。例如,我们可以创建一个名为 VAR 的变量,并赋值为 Hello, CMake! 。 使用 string 命令:除了 set 命令,我们还可以使用 string 命令的 APPEND 子命令来

    2024年02月08日
    浏览(50)
  • 【LeetCode】583. 两个字符串的删除操作

    这道题的状态定义和 1143. 最长公共子序列 相同,「定义一个 dp 数组,其中 dp[i]表示 到位置 i 为止的子序列性质 ,并不是必须以 i 结尾」,此时 dp 数组的最后一位即为题目所求,不需要对每个位置进行统计。 状态定义 dp[i][j] 表示到 字符串word1 的第 i 个字符为止、word2 的第

    2024年02月06日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包