前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Redis使用set 大key bigkey怎么解决

Redis使用set 大key bigkey怎么解决

原创
作者头像
疯狂的KK
发布于 2024-12-13 01:55:59
发布于 2024-12-13 01:55:59
1680
举报
文章被收录于专栏:Java项目实战Java项目实战

Java的世界里,集合框架是构建高效、灵活程序的基石。然而,当集合中的元素变得异常庞大时,我们就会遭遇所谓的“BigKey”问题。这不仅会导致内存消耗激增,还可能引发性能瓶颈。今天,我们就来深入探讨一下,如何在Java中使用Set时解决BigKey问题,让你的程序不再被内存黑洞吞噬!

BigKey问题概述

在Java中,Set 是一个不允许重复元素的集合,通常用于存储唯一的元素。当Set中的元素(即key)变得非常大时,我们称之为BigKey问题。这种情况在实际开发中并不罕见,尤其是在处理大型数据集或复杂对象时。BigKey问题会导致以下几个问题:

  1. 内存消耗增加:每个大key都会占用更多的内存空间,导致整体内存消耗急剧增加。
  2. 性能下降:在Set中查找、插入和删除操作的时间复杂度为O(1),但是当key变得非常大时,这些操作的性能会受到影响。
  3. 垃圾回收压力:大量的BigKey会增加垃圾回收的压力,导致频繁的Full GC,影响程序的响应速度。

解决方案概览

解决BigKey问题,我们可以从以下几个方面入手:

  1. 优化数据结构:选择合适的数据结构来存储大key。
  2. 压缩数据:对大key进行压缩,减少内存占用。
  3. 分片处理:将大key分散到多个Set中,避免单个Set过大。
  4. 缓存优化:合理使用缓存机制,减少对内存的依赖。

优化数据结构

使用合适的哈希函数

HashSet中,元素的存储位置是通过哈希函数计算得到的。如果哈希函数不够好,可能会导致哈希冲突,进而影响性能。我们可以通过自定义哈希函数来优化这一过程。

代码语言:java
AI代码解释
复制
import java.util.HashSet;
import java.util.Objects;

public class CustomHashSet<E> extends HashSet<E> {
    @Override
    public int hashCode() {
        // 自定义哈希函数,根据实际情况进行优化
        return Objects.hash(this);
    }
}

使用TreeSet

如果元素的顺序很重要,我们可以使用TreeSetTreeSet基于红黑树实现,可以保证元素的有序性。但是,TreeSet的性能通常不如HashSet,因此需要根据实际需求选择。

代码语言:java
AI代码解释
复制
import java.util.TreeSet;

public class Main {
    public static void main(String[] args) {
        TreeSet<String> treeSet = new TreeSet<>();
        treeSet.add("largeKey1");
        treeSet.add("largeKey2");
        // 其他操作
    }
}

压缩数据

序列化

对于大key,我们可以通过序列化技术将其转换为更紧凑的形式。Java提供了多种序列化机制,如Java序列化、JSON序列化等。

代码语言:java
AI代码解释
复制
import java.io.*;

public class SerializationExample {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        LargeObject largeObject = new LargeObject();

        // 序列化
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bao);
        oos.writeObject(largeObject);
        oos.close();

        // 反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        LargeObject deserialized = (LargeObject) ois.readObject();
        ois.close();
    }
}

使用压缩算法

对于非常大的key,我们可以使用压缩算法(如GZIP、LZ4等)来进一步减少内存占用。

代码语言:java
AI代码解释
复制
import java.util.zip.GZIPOutputStream;
import java.io.*;

public class CompressionExample {
    public static void main(String[] args) throws IOException {
        String data = "largeKeyData";
        ByteArrayOutputStream bao = new ByteArrayOutputStream(data.length());
        GZIPOutputStream gzip = new GZIPOutputStream(bao);
        gzip.write(data.getBytes());
        gzip.close();

        byte[] compressed = bao.toByteArray();
        // 使用compressed数据
    }
}

分片处理

基于哈希的分片

将大key分散到多个Set中,可以减少单个Set的内存压力。我们可以根据key的哈希值来决定将其分配到哪个Set。

代码语言:java
AI代码解释
复制
import java.util.concurrent.ConcurrentHashMap;

public class ShardedSet<K> {
    private final ConcurrentHashMap<Integer, Set<K>> shardMap = new ConcurrentHashMap<>();

    public void add(K key) {
        int shardIndex = Math.abs(key.hashCode()) % shardCount;
        Set<K> shard = shardMap.computeIfAbsent(shardIndex, k -> ConcurrentHashMap.newKeySet());
        shard.add(key);
    }

    // 其他方法
}

基于范围的分片

如果key有明确的范围,我们可以根据范围来分片,这样可以更均匀地分配key。

代码语言:java
AI代码解释
复制
public class RangeBasedSharding<K extends Comparable<K>> {
    private final int shardCount;
    private final Set<K>[] shards;

    public RangeBasedSharding(int shardCount) {
        this.shardCount = shardCount;
        this.shards = new Set[shardCount];
        for (int i = 0; i < shardCount; i++) {
            shards[i] = ConcurrentHashMap.newKeySet();
        }
    }

    public void add(K key) {
        int shardIndex = key.compareTo(someRange) % shardCount;
        shards[shardIndex].add(key);
    }

    // 其他方法
}

缓存优化

使用软引用

对于大key,我们可以使用软引用(SoftReference)来减少内存占用。软引用允许垃圾回收器在内存不足时回收这些对象。

代码语言:java
AI代码解释
复制
import java.lang.ref.SoftReference;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class SoftReferenceCache<K, V> {
    private final ConcurrentHashMap<K, SoftReference<V>> cache = new ConcurrentHashMap<>();

    public V get(K key) {
        SoftReference<V> ref = cache.get(key);
        return (ref != null) ? ref.get() : null;
    }

    public void put(K key, V value) {
        cache.put(key, new SoftReference<>(value));
    }

    // 其他方法
}

缓存失效策略

合理设置缓存失效策略,可以避免缓存占用过多内存。常见的策略有LRU(最近最少使用)、LFU(最不频繁使用)等。

代码语言:java
AI代码解释
复制
import java.util.LinkedHashMap;
import java.util.Map;

public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int capacity;

    public LRUCache(int capacity) {
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > capacity;
    }

    // 其他方法
}

结语

在Java中处理BigKey问题,需要我们从多个角度出发,综合考虑数据结构、数据压缩、分片处理和缓存优化等多个方面。通过这些方法,我们可以有效地减少内存消耗,提高程序性能,避免被内存黑洞吞噬。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java安全-反序列化-4-CC6
上一篇文章中通过AnnotationInvocationHandler#invoke方法来触发LazyMap#get方法,而AnnotationInvocationHandler这个类在高版本Java(8u71以后)进行了修改,导致该利用链无法利用。 这里讲另一个相对比较通用的Gadget:CommonCollections6,这个Gadget使用了另一个类org.apache.commons.collections.keyvalue.TiedMapEntry,该类在其hashCode方法中调用了getValue,而在getValue中调用了this.map.get,即可以调用LazyMap#get方法。
Naraku
2022/04/26
5180
Java安全-反序列化-4-CC6
Java集合的总结
添加元素时使用 ensureCapacityInternal() 方法来保证容量足够,如果不够时,需要使用 grow() 方法进行扩容,新容量的大小为 oldCapacity + (oldCapacity >> 1),也就是旧容量的 1.5 倍。
大学里的混子
2019/03/13
4210
Java容器(List、Set、Map)知识点快速复习手册
容器主要包括 Collection 和 Map 两种,Collection 又包含了 List、Set 以及 Queue。
蛮三刀酱
2019/03/26
6800
Java容器(List、Set、Map)知识点快速复习手册
java创建本地缓存模拟redis缓存操作
在一般的小项目中,数据量不大.但是有的时候需要使用缓存记录一些标识或者票据之类的,比如我这边想实现,可以记录系统同时在线的用户数据,或者对其他数据的缓存记录,减少DBA请求
java攻城狮
2020/10/10
2.3K0
java创建本地缓存模拟redis缓存操作
Set集合
Set集合完整定义 public interface Set extends Collection
兰舟千帆
2022/07/16
5990
Set集合
shiro权限控制(二):分布式架构中shiro的实现
前言: 前段时间在搭建公司游戏框架安全验证的时候,就想到之前web最火的shiro框架,虽然后面实践发现在netty中不太适用,最后自己模仿shiro写了一个缩减版的,但是中间花费两天时间弄出来的shiro可不能白费,这里给大家出个简单的教程说明吧。 shiro的基本介绍这里就不再说了,可以自行翻阅博主之前写的shiro教程,这篇文章主要说明分布式架构下shiro的session共享问题。 一、原理描述 无论分布式、还是集群下,项目都需要获取登录用户的信息,而不可能做的就是让客户在每个系统或者每个模块中反复
生活创客
2018/03/29
1.9K0
Java容器(List、Set、Map)知识点快速复习手册(上)
容器主要包括 Collection 和 Map 两种,Collection 又包含了 List、Set 以及 Queue。
Rude3Knife的公众号
2019/08/07
4560
Java容器(List、Set、Map)知识点快速复习手册(上)
BATJ面试必会之 Java 容器篇
容器主要包括 Collection 和 Map 两种,Collection 存储着对象的集合,而 Map 存储着键值对(两个对象)的映射表。
乔戈里
2019/03/19
7040
BATJ面试必会之 Java 容器篇
Redis 很屌,不懂使用规范就糟蹋了
谁曾想,凌晨 12 点之后,用户量暴增,出现了一个技术故障,用户无法下单,当时老大火冒三丈!
码哥字节
2021/10/11
4980
ssm整合Redis
这次谈谈Redis,关于Redis应该很多朋友就算没有用过也听过,算是这几年最流行的NoSql之一了。 
似水的流年
2018/01/14
2.7K0
Redis BigKey介绍
在Redis中,一个字符串最大512MB,一个二级数据结构(例如hash、list、set、zset)可以存储大约40亿个(2^32-1)个元素,但实际上中如果下面两种情况,我就会认为它是bigkey。
OwenZhang
2021/12/07
1.2K0
set\list\map部分源码解析
List、Set实现Collection接口。Map并没有实现任何接口,但内部聚合了一个Collection对象
洋仔聊编程
2019/01/15
8100
Java集合面试题(2021最新版)
马上到今年的金三银四了,又是跳槽的好季节,准备跳槽的同学都摩拳擦掌准备大面好几场,本次小编为大家准备了精选的 Java 集合面试题,快来查漏补缺吧。
Java程序猿
2021/04/30
11.6K0
Java安全之C3P0反序列化
C3P0是一个开源的JDBC连接池,它实现了数据源和 JNDI 绑定,具有连接数控制、连接可靠性测试、连接泄露控制、缓存语句等功能,支持 JDBC3 规范和 JDBC2 的标准扩展。 使用它的开源项目有Hibernate、Spring等。例如在执行JDBC的增删改查的操作时,如果每一次操作都来一次打开连接,操作,关闭连接,那么创建和销毁JDBC连接的开销就太大了。为了避免频繁地创建和销毁JDBC连接,我们可以通过连接池(Connection Pool)复用已经创建好的连接。
ph0ebus
2023/08/26
4350
JAVA 对象序列化(一)——Serializable
http://www.cnblogs.com/chenfei0801/archive/2013/04/05/3001149.html
bear_fish
2018/09/19
5520
内含扩容源码的面试题,目标是手写HashMap!
    推荐在单线程环境下使用HashMap替代,如果需要多线程使用则用ConcurrentHashMap。
上分如喝水
2021/08/16
4060
内含扩容源码的面试题,目标是手写HashMap!
Java Spring mvc 操作 Redis 及 Redis 集群
古时的风筝
2018/01/08
2K0
Map复制给新Map时,用 “=、clone、还是putAll”?论Map的深复制和浅复制
在我们最初使用map复制开发业务代码时,通常会踩到深浅复制(拷贝)这个坑里,比如我,在Map复制时 (如:Map<String, String> new_Map = old_Map) 出现过以下两类问题:
陈哈哈
2020/07/03
4.7K0
Java集合容器面试题(2020最新版)
Java面试总结汇总,整理了包括Java基础知识,集合容器,并发编程,JVM,常用开源框架Spring,MyBatis,数据库,中间件等,包含了作为一个Java工程师在面试中需要用到或者可能用到的绝大部分知识。欢迎大家阅读,本人见识有限,写的博客难免有错误或者疏忽的地方,还望各位大佬指点,在此表示感激不尽。文章持续更新中…
Java架构师必看
2020/04/10
1.3K0
设计模式之原型模式(创建型)
原型模式(Prototype Pattern):原型模式是提供一个原型接口,提供原型的克隆,创建新的对象,是一种对象创建型模式。
SmileNicky
2019/03/06
3580
推荐阅读
相关推荐
Java安全-反序列化-4-CC6
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档