【问题标题】:Accessing HashMap being used as a Cache访问用作缓存的 HashMap
【发布时间】:2016-07-07 12:16:16
【问题描述】:

我有一个休息 api,它将记录插入到数据库中,id 为 entityId。我有一个保存 entityId 和实体对象的 HashMap,它在这里充当缓存。

在插入实体并更新缓存(HashMap)后,当我进行查找时,有时无法从缓存中找到 id。

注意:- 我正在循环运行其余 api 并进行负载测试以验证对象始终存在于缓存中

我们非常感谢这方面的任何帮助。无法从缓存中找到 id 可能是什么原因。

【问题讨论】:

  • 是 ConcurrentHashMap 吗?上课范围是什么?您是否在更新缓存后进行日志记录?如果是,你能看到 id 吗?
  • 是的,当我进行日志记录时,我看到地图中存在最新值。但是我尝试使用 ma.getValues() 方法获取新添加的值没有反映的值。
  • 当我从同一张地图上进行第二次查找时,我可以看到该值大部分时间都存在。
  • 我尝试过同时使用 HashMap 和 ConcurrentHashMap

标签: java multithreading concurrency hashmap thread-safety


【解决方案1】:

这取决于您的负载测试的运行方式以及后续写入和读取请求之间的时间间隔。

虽然更重要的是你的 Cache/Hashmap 的更新方式。

Are you updating the HashMap BEFORE inserting into the Database


Are you updating the HashMap AFTER inserting into the Database.

如果您在写入数据库后进行插入,那么在 HashMap 更新之前可能会有延迟,这意味着当后续的读取请求进入时,HashMap 尚未更新。

可能值得在将请求添加到 HM 时添加日志,并在读取请求进入时添加日志。这可能会让您了解 HashMap 是否已实际更新与否。

【讨论】:

  • 为什么HashMap在插入数据库后更新会有延迟?
  • 这取决于将记录插入数据库需要多长时间,以及如何设置与数据库的连接。但是,如果您在数据库插入之后对 HashMap 进行更新,则可能意味着在 HashMap 更新之前的几百毫秒。并且由于您提到您在循环中执行此操作,这可能意味着您的读取 REST API 甚至在此更新之前就已触发。同样,我并不是说这可能是问题,我只是建议它可能是它的潜在原因。
【解决方案2】:

HashMap 不是线程安全的,见the API documentation:

请注意,此实现不同步。如果多个线程同时访问一个哈希映射,并且至少有一个线程在结构上修改了映射,则它必须在外部同步。 (结构修改是添加或删除一个或多个映射的任何操作;仅更改与实例已包含的键关联的值不是结构修改。)这通常通过在自然封装映射的某个对象上同步来完成.如果不存在这样的对象,则应使用 Collections.synchronizedMap 方法“包装”地图。这最好在创建时完成,以防止意外不同步地访问地图:

Map m = Collections.synchronizedMap(new HashMap(...));

您所描述的地图中的插入将被视为结构修改。

改用ConcurrentHashMap,更新不会像上面带有synchronizedMap 的代码那样锁定整个数据结构。

如果问题是新值没有在其他线程需要使用它之前进入缓存,只是因为线程正在快速连续访问相同的代码,那么最好忍受缓存未命中而不是引入瓶颈。最好不要在插入时缓存结果,而是等待第一个选择查询(在提交插入后的新事务中),这样可以避免将条目添加到缓存然后事务回滚的情况,在缓存中留下无效数据。

还有使用大量内存存储未使用的条目的危险。您可以让工作线程定期检查过期项目。

缓存是比看起来更难的事情之一。考虑使用ehcache 作为在此处推出您自己的解决方案的替代方案。

【讨论】:

    猜你喜欢
    • 2016-04-12
    • 1970-01-01
    • 2018-01-02
    • 1970-01-01
    • 2017-07-01
    • 1970-01-01
    • 2019-04-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多