首页 > 编程语言 >Java常见的本地存储方式

Java常见的本地存储方式

时间:2024-06-23 23:21:26浏览次数:23  
标签:map 存储 缓存 Java 本地 删除 3.3 key Guava

1、HashMap

1.1 特点

  • K-V 形式
  • 线程不安全
  • 查询效率快

通过线程不安全的特点,表现了 HashMap 的应用场景局限于单线程(没有线程并发问题的场景)

1.2 基础操作

Map<Object,Object> map=new HashMap<>();
// 添加元素
map.put("key","value");
// 获取元素
map.get("key");
// 删除元素
map.remove("key");

2、ConcurrentHashMap

为了针对无法应用 HashMap 的多线程环境,可以采用并发安全的集合类 ConcurrentHashMap。

对于 HashMap 的并发安全类有许多

  • Hashtable
  • Collections.synchronizedMap()
  • ConcurrentHashMap

concurrentHashMap 在 JDK 采用的 CAS+synchronized,性能最好

Map<Object,Object> map=new ConcurrentHashMap<>();
// 添加元素
map.put("key","value");
// 获取元素
map.get("key");
// 删除元素
map.remove("key");

3、Guava 缓存

Guava是Google提供的一套JAVA的工具包,而Guava Cache则是该工具包中提供的一套完善的JVM级别的高并发缓存框架。其实现机制类似ConcurrentHashMap,但是进行了众多的封装与能力扩展。作为JVM级别的本地缓存框架,Guava Cache具备缓存框架该有的众多基础特性。

3.1特性

  • 线程安全(和 ConcurrentHashMap 一样)
  • 支持缓存记录的过期设定( 和 redis 缓存一样)
  • 支持缓存容量限制与不同淘汰策略( FIFO 算法、LRU 算法)

3.2应用场景

初次接触到 Guava 感觉 redis 分布式缓存很像,具有同样的功能。但是区别在于,redis 分布式缓存是部署在应用之外的,存储大小受限于机器内存大小,在于应用通信存在网络消耗。Guava 是 JVM 本地缓存,与应用交互不存在网络消耗,访问效率快、但是存储大小受限于 JVM 堆内存大小。

应用场景:

  • 存储内容小
  • 命中率高
  • 访问速度快
  • 能够容忍数据不一致。(尝试使用定时任务进行同步或利用 cancel 订阅 binLog 进行数据同步)

注意:上面的场景并不是仅仅局限于 Guava 本地缓存,而是整个的本地缓存体系

3.3 整合 Guava

3.3.1 导入 guava 依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>18.0</version>
</dependency>

3.3.2 guava cache 创建

Cache<String, String> localCache = CacheBuilder.newBuilder()
               // 内部哈希表的最小容量,也就是 cache 的初始容量
                .initialCapacity(5)
               // cache 的最大缓存数
                .maximumSize(10)
               // 并发等级,也可以定义为同时操作缓存的线程数,默认 4 
                .concurrencyLevel(3)
               // 设置过期时间 
                .expireAfterWrite(10, TimeUnit.SECONDS)
                .build();

3.3.3 获取对象

从缓存中获取数据调用的方法为 get (K key, Callable<? extends V> loader) 方法,此方法的含义是根据键 key 获取数据,若 key 不存在,则通过执行指定的 Callable 方法来构造缓存,示例代码如下所示:

 localCache.get("key", new Callable<ThreadPoolConfigEntity>() {
                @Override
                public Object call() throws Exception {
                    System.out.println("未获取到 key 数据,可以进行回调");
                    // 进行数据写入,如查询 MySQL、 Redis
                    return null;
                }
            })

3.3.4 删除数据

3.3.4.1 被动删除
  • 基于数据大小删除:LRU+FIFO
  • 基于过期时间删除:在指定时间内没有被访问
  • 基于引用删除:通过 weakKeys 和 weakValues 方法指定 Cache 只保存对缓存记录 key 和 value 的弱引用。这样当没有其他强引用指向 key 和 value 时,key 和 value 对象就会被垃圾回收器回收
3.3.4.2 主动删除
//删除指定的key对应数据
cache.invalidate("s");
//将一批对应的数据删除
cache.invalidateAll(Arrays.asList("st","r","ing"));
//全部删除
cache.invalidateAll();

3.3.5 源码分析 guava

浅析本地缓存技术 - Guava Cache | 京东物流技术团队 - 知乎 (zhihu.com)

3.3.6 Guava 其他应用

3.3.6.1 布隆过滤器

布隆过滤器 ; 一段 bitMap + 多个 Hash 函数,在添加元素的时候,会对元素进行相应的 hash 运算,将相应的 hash 槽,设置为 1,访问的时候也是进行 hash 运算,计算出相应的 hash 槽,当所有的 hash 槽都为 1 时,表示该数据才存在。

特点:

  • 存在一定误差
  • 查询结果不存在的情况,一定不存在
  • 不可以进行元素的删除,只能整体删除
3.3.6.2 guava 实现布隆过滤器
public class BloomFilterTest {

    public static void main(String[] args) {
        long star = System.currentTimeMillis();
        BloomFilter<Integer> filter = BloomFilter.create(
                Funnels.integerFunnel(),
                //预计存放多少数据
                10000000,
                //可以接受的误报率
                0.01);

        for (int i = 0; i < 10000000; i++) {
            filter.put(i);
        }

        Assert.isTrue(filter.mightContain(1),"不存在");
        Assert.isTrue(filter.mightContain(2),"不存在");
        Assert.isTrue(filter.mightContain(3),"不存在");
        Assert.isTrue(filter.mightContain(10000000),"不存在");
        long end = System.currentTimeMillis();
        System.out.println("执行时间:" + (end - star));
    }
}

这里Guava引入了一个叫做Funnel的类,Funnel类定义了如何把一个具体的对象类型分解为原生字段值,从而将值分解为Byte以供后面BloomFilter进行hash运算。通过使用这个类,我们可以自己定义一个属于自己类的Funnel。如下代码

enum PersonFunnel implements Funnel<Person> {
    INSTANCE;

    @Override
    public void funnel(Object person, PrimitiveSink into) {
        into.putString(person.getFirstName(), Charset.defaultCharset())
                .putString(person.getLastName(), Charset.defaultCharset());
    }
}

此外,Guava预定义了一些原生类型的Funnel,如String、Long、Integer

3.3.6.3 Guava 时间窗口限流

阿里面试:说说自适应限流?_王磊_InfoQ写作社区

RateLimiter limiter = RateLimiter.create(5.0); // 创建一个每秒放入5个令牌的RateLimiter
for (int i = 0; i < 10; i++) {
    // 请求一个令牌
    limiter.acquire();
    System.out.println("处理请求: " + i);
}

4、Caffeine ——本地缓存之王

推荐地址: java - 性能利器Caffeine缓存全面指南 - 宋小黑 - SegmentFault 思否我的技术与生活——本地缓存之王-Caffeine | Hexo (iyaovo.github.io)

标签:map,存储,缓存,Java,本地,删除,3.3,key,Guava
From: https://www.cnblogs.com/wzl66/p/18264121

相关文章