首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

插入键时自定义等于/散列(Guava缓存)

基础概念

Guava 是 Google 提供的一个 Java 库,包含了许多有用的工具和数据结构。Guava 缓存(Cache)是 Guava 中的一个组件,用于在内存中高效地存储和检索数据。Guava 缓存支持多种缓存策略,如基于时间的过期、基于大小的淘汰等。

相关优势

  1. 高效性:Guava 缓存使用内存存储数据,访问速度快。
  2. 灵活性:支持多种缓存策略,可以根据需求配置。
  3. 易用性:API 设计简洁,易于上手。
  4. 线程安全:内部实现保证了线程安全。

类型

Guava 缓存主要分为两种类型:

  1. LoadingCache:自动加载缓存,当访问一个不存在的键时,会自动调用指定的加载函数加载数据并存入缓存。
  2. CacheBuilder:用于构建缓存的配置器,可以配置缓存的过期时间、最大大小等。

应用场景

Guava 缓存适用于以下场景:

  1. 数据缓存:将频繁访问但不经常变化的数据缓存到内存中,减少数据库或磁盘 I/O 操作。
  2. 计算结果缓存:将复杂计算的结果缓存起来,避免重复计算。
  3. 会话缓存:在 Web 应用中缓存用户会话信息,提高响应速度。

示例代码

以下是一个使用 Guava 缓存的简单示例:

代码语言:txt
复制
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

import java.util.concurrent.TimeUnit;

public class GuavaCacheExample {
    public static void main(String[] args) {
        // 创建一个缓存,最多存储 100 个条目,每个条目在 10 分钟后过期
        Cache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(100)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .build();

        // 向缓存中插入数据
        cache.put("key1", "value1");
        cache.put("key2", "value2");

        // 从缓存中获取数据
        String value1 = cache.getIfPresent("key1");
        System.out.println("Value for key1: " + value1);

        String value3 = cache.getIfPresent("key3");
        System.out.println("Value for key3: " + value3);
    }
}

遇到的问题及解决方法

问题:缓存数据不一致

原因:缓存数据和实际数据源数据不一致,可能是由于缓存未及时更新或数据源发生变化。

解决方法

  1. 使用 CacheLoader:在访问不存在的键时,自动从数据源加载最新数据。
  2. 手动刷新缓存:定期或在数据源发生变化时,手动刷新缓存。
代码语言:txt
复制
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import java.util.concurrent.TimeUnit;

public class GuavaCacheExample {
    public static void main(String[] args) {
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(100)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .build(new CacheLoader<String, String>() {
                    @Override
                    public String load(String key) throws Exception {
                        // 从数据源加载数据
                        return loadDataFromSource(key);
                    }
                });

        // 从缓存中获取数据
        String value1 = cache.get("key1");
        System.out.println("Value for key1: " + value1);
    }

    private static String loadDataFromSource(String key) {
        // 模拟从数据源加载数据
        return "value_" + key;
    }
}

问题:缓存击穿

原因:某个热点键在缓存中过期后,大量请求同时访问该键,导致缓存击穿。

解决方法

  1. 使用互斥锁:在加载数据时加锁,保证只有一个线程加载数据。
  2. 设置热点数据永不过期:对于热点数据,可以设置永不过期。
代码语言:txt
复制
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class GuavaCacheExample {
    public static void main(String[] args) {
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(100)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .build(new CacheLoader<String, String>() {
                    private final Lock lock = new ReentrantLock();

                    @Override
                    public String load(String key) throws Exception {
                        lock.lock();
                        try {
                            // 检查缓存中是否已有数据
                            String value = cache.getIfPresent(key);
                            if (value != null) {
                                return value;
                            }

                            // 从数据源加载数据
                            String newValue = loadDataFromSource(key);
                            cache.put(key, newValue);
                            return newValue;
                        } finally {
                            lock.unlock();
                        }
                    }
                });

        // 从缓存中获取数据
        String value1 = cache.get("key1");
        System.out.println("Value for key1: " + value1);
    }

    private static String loadDataFromSource(String key) {
        // 模拟从数据源加载数据
        return "value_" + key;
    }
}

参考链接

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

什么是布隆过滤器?如何使用?

布隆过滤器的原理是,当一个元素被加入集合时,通过K个函数将这个元素映射成一个位数组中的K个点,把它们置为1。...当你往简单数组或列表中插入新数据,将不会根据插入项的值来确定该插入项的索引值。这意味着新插入项的索引值与数据值之间没有直接关系。...布隆过滤器存储空间和插入/查询时间都是常数(O(k))。另外,函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。...布隆过滤器可以表示全集,其它任何数据结构都不能; k和m相同,使用同一组函数的两个布隆过滤器的交并运算可以使用位操作进行。 缺点 但是布隆过滤器的缺点和优点一样明显。误算率是其中之一。...但是如果元素数量太少,则使用列表足矣。 另外,一般情况下不能从布隆过滤器中删除元素。我们很容易想到把位数组变成整数数组,每插入一个元素相应的计数器加1, 这样删除元素将计数器减掉就可以了。

3.1K52

flink维表关联系列之Hbase维表关联:LRU策略

维表关联系列目录: 一、维表服务与Flink异步IO 二、Mysql维表关联:全量加载 三、Hbase维表关联:LRU策略 四、Redis维表关联:实时查询 五、kafka维表关联:广播方式 六、自定义异步查询...在Flink中做维表关联,如果维表的数据比较大,无法一次性全部加载到内存中,而在业务上也允许一定数据的延时,那么就可以使用LRU策略加载维表数据。...,插入是链表尾部,取数据是链表头部,也就是访问的顺序与插入的顺序是一致的。...guava Cache google guava下面提供了Cache缓存模块,轻量级,适合做本地缓存,能够做到以下几点: a. 可配置本地缓存大小 b. 可配置缓存过期时间 c....util.ArrayList[KeyValue]): String = { "" } } 对于查询hbase, 需要合理设计rowKey,为了避免查询热点,例如rowKey通过md5方式

1.2K21
  • 品味布隆过滤器 Bloom filter的设计之美

    布隆过滤器的原理:当一个元素被加入集合时,通过 K 个函数将这个元素映射成一个位数组中的 K 个点,把它们置为 1。...简单来说就是准备一个长度为 m 的位数组并初始化所有元素为 0,用 k 个函数对元素进行 k 次运算跟 len (m) 取余得到 k 个位置并将 m 中对应位置设置为 1。...图片 如上图,位数组的长度是8,函数个数是 3,先后保持两个元素x,y。这两个元素都经过三次哈希函数生成三个哈希值,并映射到位数组的不同的位置,并置为1。...整体误判率为 图片,当 m 足够大,误判率会越小,该公式约等于图片 我们会预估布隆过滤器的误判率 p 以及待插入的元素个数 n 分别推导出最合适的位数组长度 m 和 哈希函数个数 k。...1、缓存穿透场景 首先我们需要初始化布隆过滤器,然后当用户请求,判断过滤器中是否包含该元素,若不包含该元素,则直接返回不存在。

    2.2K41

    Redis 字典

    当我们往列表中插入数据,如果某个数据经过函数之后,存储位置已经被占用了,我们就从当前位置开始,依次往后查找,看是否有空闲位置,如果遍历到尾部都没有找到空闲的位置,那么我们就再从表头开始找,直到找到为止...2.2 Redis如何解决冲突 2.2.1 链表法 当有两个或以上的被分配到列表数组同一个索引上,就发生了冲突。Redis使用链表法解决冲突。...如图所示,当k0和k1的经过函数得到索引值都为1,就会使用next指针将两个节点连接起来。而由于节点没有指向链尾的指针,因此新的节点总是插入到链表的头部,排在已有节点的前面。...收缩操作:ht1的大小为 第一个大于等于ht0.used的2的n次方幂。 2、将保存在ht0中的键值对重新计算值和索引值,然后放到ht1指定的位置上。...当redis计算哈希,采用的是MurmurHash2哈希算法。 哈希表采用链表法解决冲突,被分配到同一个地址的会构成一个单向链表。

    1.7K84

    布隆过滤器 | 亿级数据处理原理与实战

    下面是一幅示意图: 所有函数都有如下基本特性: 如果两个值是不相同的(根据同一函数),那么这两个值的原始输入也是不相同的。...这个特性是函数具有确定性的结果,具有这种性质的函数称为单向函数。...那是因为映射函数本身就是函数,函数是会有碰撞的。...布隆过滤器存储空间和插入/查询时间都是常数 ,另外,函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。...但是如果元素数量太少,则使用列表足矣。 另外,一般情况下不能从布隆过滤器中删除元素。我们很容易想到把位数组变成整数数组,每插入一个元素相应的计数器加 1, 这样删除元素将计数器减掉就可以了。

    1.9K31

    浅谈布隆过滤器

    这个特性是函数具有确定性的结果,具有这种性质的函数称为单向函数。...函数的输入和输出不是唯一对应关系的,如果两个值相同,两个输入值很可能是相同的,但也可能不同,这种情况称为“碰撞(collision)”。...那是因为映射函数本身就是函数,函数是会有碰撞的。...布隆过滤器存储空间和插入/查询时间都是常数 $O(K)$,另外,函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。...但是如果元素数量太少,则使用列表足矣。 另外,一般情况下不能从布隆过滤器中删除元素。我们很容易想到把位数组变成整数数组,每插入一个元素相应的计数器加 1, 这样删除元素将计数器减掉就可以了。

    58142

    你还应该知道的哈希冲突解决策略

    1、线性探测(Linear probing) 插入一个值 使用函数H(K)在大小为M的表中插入密钥K: 设置 indx = H(K) 如果表位置indx已经包含密钥,则无需插入它。...检索一个值 如果使用线性探测将插入表中,则线性探测将找到它们! 当使用函数 H(K)在大小为N的表中搜索K: 设置 indx = H(K) 如果表位置indx包含,则返回FOUND。...使用随机,探测序列是由密钥播种的伪随机数生成器的输出生成的(可能与另一个种子组件一起使用,该组件对于每个都是相同的,但是对于不同的表是不同的)。...可以证明,用于线性探测的插入或未成功发现的探针的平均数量约为 当 α 接近1,这些平均案例时间成本很差,仅受M限制;但当 α 等于或小于7.75(与M无关),效果还不错(分别为4和8.5) 平均成功查找成本...考虑随机,因此聚类不是问题。每个探针位置是随机且独立生成的。 对于表中的,成功找到它所需的探针数等于将其插入表中所采用的探针数。每个新密钥的插入都会增加负载系数,从0开始到α。

    1.5K31

    列表到BitMap的概念与应用(二)

    当一个元素被加入集合中,通过k各函数将这个元素映射成一个位数组中的k个点,并将这k个点全部置为1。下图是k=3的布隆过滤器。 ?...我们假设所有哈希函数足够均匀,后落到Bitmap每个位置的概率均等。...Bitmap的大小为m、原始数集大小为n、哈希函数个数为k: k个相互独立的函数,接收一个元素Bitmap中某一位置为0的概率为: ?...这里只要增加一个bloom算法的服务,服务端插入一个key,在这个服务中设置一次。需要查询服务端,先判断key在后端是否存在,这样就能避免服务端的压力。...但是如果元素数量太少,则使用列表足矣),不能从布隆过滤器中删除元素。我们很容易想到把位数组变成整数数组,每插入一个元素相应的计数器加1, 这样删除元素将计数器减掉就可以了。

    60730

    秒杀系统“天花板”,不服不行!

    2 优化方案 ①双缓存区定时更新 通过上面的分析可以发现,为了防止堆内存增长过快,需要控制商品数据更新的粒度和频次。...我们希望数据更新能控制在更小的范围,同时能够控制数据更新的频率,最终设计出双缓存区定时更新方案,如图 8 所示。...图 8:双缓存区定时更新示意图 该方案的实现是将活动下的商品以 SKU 维度列到不同的桶中,更新的操作以桶的粒度进行。...该方案份数和定时时间可以根据具体业务情况进行调整,在性能和实时性上取得平衡,在上线后取得了较好的优化效果。...②引入本地 LRU 缓存缓存区定时更新的方案虽然在系统性能上得到了提升,但依然无法支持千万级商品的扩容。

    70730

    布隆过滤器,一文总结快速掌握,你能够get多少?

    基本思想 当一个元素被加入集合时,通过K个函数将这个元素映射到一个位数组中的K个点,把它们置为1。...检索某个元素,再通过这K个函数将这个元素映射,看看这些位置是不是都是1就能知道集合中这个元素存不存在。如果这些位置有任何一个0,则该元素一定不存在;如果都是1,则被检元素很可能存在。...优点 二进制组成的数组,内存占用空间少,并且插入和查询速度很快,常数级别。 Hash函数相互之间没有必然联系,方便由硬件并行实现。...k1对应的值为keke,对应ASCII码为107 101 107 101,对应的二进制为 0110 1011,0110 0101,0110 1011,0110 0101。...Google Guava提供的布隆过滤器的位数组是存储在JVM内存中,故是单机版的,并且最大位长为int类型的最大值。 使用布隆过滤器,重要关注点是预估数据量n以及期望的误判率fpp。

    1.3K10

    Java高效开发12个精品库

    SLF4J SLF4J或Simple Logging Facade for Java,它为不同的框架提供了一个抽象概念,允许开发人员在部署插入任何框架。...Google Guava Google Guava是Java编程的另一个受欢迎的Java核心库 ? Google Guava软件包中的库或多或少是对核心库的对应部分有增强功能,并使编程更加高效和有效。...Guava 包括内存缓存、不可变集合、函数类型、图形库和可用于 I/O、、并发、原语、字符串处理、反射等等的API实用程序。 05....它为Java泛型提供了极大的支持,并允许对象的自定义表示。 10. Joda Time 这就是我一直强调的简单但功能强大的库,它节省了大量的开发时间。...Okhttp在断网恢复连接,在多个基于IP的服务中切换IP地址。okhttp的一个有用的功能是与现代TLS(SNI,ALPN)的自动连接,并且在发生故障回到TLS 1.0。 12.

    1.3K40

    详解布隆过滤器的原理和实现

    插入与查询时间复杂度均为 O(k),常数级别,k 表示函数执行次数。 函数之间可以相互独立,可以在硬件指令层加速计算。 缺点: 误差(假阳性率)。 无法删除。...误差(假阳性率) 布隆过滤器可以 100% 判断元素不在集合中,但是当元素在集合中可能存在误判,因为当元素非常多时函数产生的 k 位点可能会重复。...,假设: 位数组长度 m 函数个数 k 预期元素数量 n 期望误差_ε_ 在创建布隆过滤器我们为了找到合适的 m 和 k ,可以根据预期元素数量 n 与 ε 来推导出最合适的 m 与 k 。...根据上面的算法原理可以知道实现布隆过滤器主要做三件事情: k 次函数计算出 k 个位点。 插入时将位数组中 k 个位点的值设置为 1。...rename key 的方式更新 bloom 缓存与数据库同时无法命中缓存写入一个过期时间较短的空值。

    87020

    HashMap、LRU、列表

    为了提高性能,该容器提供了一个优化:当删除key,不是立马删除这一项,而是留下需要删除的选项给一个删除的标记。该条目可以被重新用于相同的key,或者被单个垃圾收集器逐步删除完全部的条目后压缩。...为了节省内存有更加保守的内存扩张(扩容的少)以及内存收缩策略(gc的频繁(删除不用的空间,新的数组)) 当mSize大于或等于mHashes数组长度则扩容,完成扩容后需要将老的数组拷贝到新分配的数组,...调用put插入新的对象也是存储在链表尾端,这样当内存缓存达到设定的最大值,将链表头部的对象(近期最少用到的)移除。 内存中使用LRUCache是最合适的。...当我们按照键值查询元素,我们用同样的函数,将键值转化数组下标,从对应的数组下标的位置取数据。 时间复杂度 插入一个数据,最好情况下,不需要扩容,最好时间复杂度是 O(1)。...冲突 1.开放寻址法 线性探测 我们往列表中插入数据,如果某个数据经过函数之后,存储位置已经被占用了,我们就从当前位置开始,依次往后查找,看是否有空闲位置,直到找到为止。

    1.1K51

    提升编程效率的利器: 解析Google Guava库之集合篇Table二维映射(四)

    Guava的Table是一种特殊的数据结构,它允许你使用两个(通常被称为行)来映射一个值。你可以将Table视为一个二维的Map,其中每个单元格都由行唯一确定,并存储一个值。...HashBasedTable提供了快速的插入、查找和删除操作,并且不保证任何特定的顺序。 TreeBasedTable:这个实现基于红黑树,它根据的自然顺序或者提供的比较器对行进行排序。...TreeBasedTable在按键顺序遍历数据非常高效,但插入和查找操作可能比哈希表慢。 ImmutableTable:这是一个不可变的Table实现,它在创建接收所有数据,并且之后不允许修改。...如果你不使用table,那就需要用嵌套Map实现,代码可能就是下面这样 需要注意的是,与Guava Table相比,嵌套的Map在处理某些操作可能会更加繁琐,例如检查是否存在,因为你需要遍历所有的内部...Table的优势 使用Guava的Table而不是嵌套的Map有几个优势: 类型安全:Table明确指定了行和值的类型,减少了类型转换的错误。

    82610

    这次妥妥地拿下列表---基础、如何设计以及扩展使用(LRU)

    这个先经过函数的计算得到值(数组下标),然后根据值在数组相应的位置存储(商品名,商品价格)这一对内容。...当我们按照查询这一对内容,只要使用同样的函数,将转换为下标,从数组下标的位置取这一对内容就完成了查找。因此,列表用于查找,时间复杂度是 O(1)。...因此,一个函数 hash() 设计的基本要求是: 函数计算得到的值是一个非负整数。因为我们得到的值是用来作为数组下标的,因为值需要是一个大于等于 0 的值,即非负整数。...当往列表中插入数据的时候,如果这个数据的经过函数之后得到的数组位置已被占用了,那么就从得到的数组位置开始,依次往后查找(到达数组尾之后再从头开始),看是否有空闲位置,直到找到为止。...当有新数据插入的时候,我们将新数据插入到新的列表中,然后从老的列表中取出一个数据插入到新的列表中。之后,每次插入一个数据,都重复上述的过程。

    75220

    java开发常用的工具类库google guava

    mapTable 接口可以看作是一个类似于矩阵的数据结构,它包含两个:行。...取出对象省去了强制类型转换,避免手动类型转换失误。..." + 100);}cacheBuilderCache是Guava提供的一个缓存工具类,它可以帮助我们在内存中缓存数据。...缓存大小限制:设置缓存的最大容量,当缓存超过设定的容量,可以通过一些策略(比如使用 LRU 或 FIFO)来自动淘汰一些不常用的缓存项。可以使用 maximumSize 方法设置缓存的最大容量。...弱引用或值:CacheBuilder 提供了一些选项,可以使用弱引用持有缓存或值。当没有其他地方引用某个或值缓存会自动将其从内存中移除,以避免内存泄漏。

    52710

    大白话布隆过滤器

    比特数组均初始化为0,所有哈希函数都可以分别把输入数据尽量均匀地。 当插入一个元素,将其数据通过k个哈希函数转换成k个哈希值,这k个哈希值将作为比特数组的下标,并将数组中的对应下标的值置为1。...集合中的 x、y、z 三个元素通过 3 个不同的哈希函数列到位数组中。当查询元素 w ,因为有一个比特为 0,因此 w 不在该集合中。 ?...假设我们的哈希函数选择比特数组中的比特,都是等概率的。当然在设计哈希函数,也应该尽量满足均匀分布。 在比特数组长度m的布隆过滤器中插入一个元素,它的其中一个哈希函数会将某个特定的比特置为1。...防止缓存穿透 缓存穿透是指查询一条数据库和缓存都没有的一条数据,就会一直查询数据库,对数据库的访问压力会一直增大。 布隆过滤器在解决缓存穿透的问题效果也是很好,这里不再细说,后续文章会写。...Guava 实现 guava 对应布隆过滤器的实现做出了支持,使用 guava 可以很轻松的实现一个布隆过滤器。 1.

    1.6K20

    详解布隆过滤器的原理和实现「建议收藏」

    插入与查询时间复杂度均为 O(k),常数级别,k 表示函数执行次数。 函数之间可以相互独立,可以在硬件指令层加速计算。 缺点: 误差(假阳性率)。 无法删除。...误差(假阳性率) 布隆过滤器可以 100% 判断元素不在集合中,但是当元素在集合中可能存在误判,因为当元素非常多时函数产生的 k 位点可能会重复。...维基百科有关于假阳性率的数学推导(见文末链接)这里我们直接给结论(实际上是我没看懂…),假设: 位数组长度 m 函数个数 k 预期元素数量 n 期望误差_ε_ 在创建布隆过滤器我们为了找到合适的...根据上面的算法原理可以知道实现布隆过滤器主要做三件事情: k 次函数计算出 k 个位点。 插入时将位数组中 k 个位点的值设置为 1。...,可以采用 rename key 的方式更新 bloom 缓存与数据库同时无法命中缓存写入一个过期时间较短的空值。

    95420

    Redis-布隆过滤器

    Bloom Filter的原理在元素加入集合时,通过多个函数将元素映射到位数组中的多个点,并将它们置为1。...Bloom Filter 实现在Guava中提供了一种Bloom Filter的实现。...Guava的Bloom Filter实现还提供了其他一些方法和功能,例如批量插入元素、序列化和反序列化等。可以根据具体需求使用相应的方法。...例如,在网页缓存中,当一个用户请求一个网页,可以首先使用布隆过滤器判断该网页是否已经被缓存,如果不存在则从后端获取并缓存,避免了不必要的数据库查询或网络请求。...防止缓存穿透:布隆过滤器可以用于防止缓存穿透,即当一个查询请求的结果不在缓存,为了避免频繁查询数据库,可以首先通过布隆过滤器判断该请求是否为无效请求,如果是无效请求,则可以直接返回空结果,从而减轻对数据库的压力

    44530

    系统设计:URL短链设计

    此URL的最后六个字符是我们要生成的短。我们将在这里探讨两种解决方案: A.编码实际URL 我们可以计算给定URL的唯一(例如MD5或SHA256等)。然后可以对进行编码以显示。...B基于的分区:在这个方案中,我们对存储的对象进行。然后根据列计算要使用的分区。在我们的例子中,我们可以使用“key”或实际URL的来确定存储数据对象的分区。...我们的函数将把URL随机分配到不同的分区(例如,我们的函数总是可以将任何映射到[1…256]之间的数字),这个数字将代表我们存储对象的分区。...8.缓存 我们可以缓存经常访问的URL。我们可以使用一些现成的解决方案,比如Memcache,它可以用各自的存储完整的url。应用服务器在访问后端存储之前,可以快速检查缓存是否具有所需的URL。...我们可以使用链接的图或类似的数据结构来存储URL和,这也将跟踪最近访问的URL。 为了进一步提高效率,我们可以复制缓存服务器以在它们之间分配负载。 如何更新每个缓存副本?

    6.1K165
    领券