永远不要编写缓存解决方案
另一种加快数据库工作的方法是缓存我们之前已经请求过的对象。
重要的!永远不要编写自己的缓存解决方案。这项任务有很多你做梦也想不到的陷阱。
问题 1 -缓存刷新。有时,当需要从缓存中删除对象或在缓存中更新对象时,会发生事件。胜任地做到这一点的唯一方法是通过缓存引擎将所有请求传递给数据库。否则,每次你都必须明确地告诉缓存应该删除或更新其中的哪些对象。
问题 2 -内存不足。在您发现内存中的对象占用大量空间之前,缓存似乎是个好主意。您需要额外的数十 GB 内存才能使服务器应用程序缓存有效工作。
由于内存总是不足,因此需要一种从缓存中删除对象的有效策略。这有点类似于Java中的垃圾收集器。你还记得,几十年来,最聪明的人一直在发明各种按代标记对象的方法,等等。
问题 3 -不同的策略。正如实践所示,在缓存中存储和更新的不同策略对不同的对象有效。高效的缓存系统不能对所有对象只执行一种策略。
问题 4 -的高效存储。您不能只将对象存储在缓存中。对象经常包含对其他对象的引用,等等。以这种速度,您将不需要垃圾收集器:它只是没有任何东西可以删除。
因此,与其存储对象本身,不如存储它们的原始字段的值有时更有效。以及基于它们快速构建对象的系统。
结果,您将在内存中获得一个完整的虚拟 DBMS,它应该可以快速运行并且消耗很少的内存。
数据库缓存
除了直接在 Java 程序中缓存外,缓存通常直接在数据库中组织。
有四大方法:
第一种方法是对数据库进行非规范化。SQL Server 在内存中存储数据的方式与在表中的存储方式不同。
当数据以表的形式存储在磁盘上时,开发人员通常会尽量避免数据重复——这个过程称为数据库规范化。因此,为了加快内存中数据的处理速度,执行了相反的过程——数据库反规范化。一堆相关的表已经可以以组合的形式存储——以大表的形式等。
第二种方法是查询缓存。并查询结果。
DBMS 发现相同或相似的请求经常出现。然后它只是开始缓存这些请求及其响应。但与此同时,您需要确保及时从缓存中删除数据库中已更改的行。
对于可以分析查询并帮助 DBMS 确定如何最好地缓存它们的人来说,这种方法可能非常有效。
第三种方法是内存数据库。
另一种常用的方法。另一个数据库位于服务器和 DBMS 之间,它将所有数据仅存储在内存中。它也称为 In-Memory-DB。如果您有许多不同的服务器访问同一个数据库,那么使用 In-Memory-DB 您可以根据特定服务器的类型组织缓存。
例子:
![](https://cdn.codegym.cc/images/article/a2a92dd6-07b2-46e4-880c-7f2673af154b/1024.jpeg)
方法4——数据库集群。几个只读基地。
另一种解决方案是使用集群:多个相同类型的 DBMS 包含相同的数据。同时,您可以从所有数据库读取数据,只写入一个。然后与其余数据库同步。
这是一个非常好的解决方案,因为它易于配置并且在实践中有效。通常,对数据库的一个更改数据请求,会收到 10-100 个读取数据的请求。
Hibernate 中的缓存类型
Hibernate 支持三种级别的缓存:
- 会话级缓存(Session)
- 在 SessionFactory 级别缓存
- 缓存请求(及其结果)
你可以尝试用这样一个图的形式来表示这个系统:
![](https://cdn.codegym.cc/images/article/938d3cbc-b006-4c2d-9bdc-b2311e5de4a5/1024.jpeg)
最简单的缓存类型(也称为一级缓存)是在 Hibernate 会话级别实现的。默认情况下,Hibernate 总是使用这个缓存并且不能被禁用。
让我们立即考虑以下示例:
Employee director1 = session.get(Employee.class, 4);
Employee director2 = session.get(Employee.class, 4);
assertTrue(director1 == director2);
看起来这里会执行对数据库的两个查询,但事实并非如此。在第一次请求数据库后,Employee 对象将被缓存。而如果在同一个会话中再次查询该对象,Hibernate 将返回同一个 Java 对象。
相同的对象意味着即使是对象引用也是相同的。这真的是同一个对象。
save()、update()、saveOrUpdate()、load()、get()、list()、iterate()和scroll()方法将始终使用一级缓存。其实,没有什么可以补充的了。
二级缓存
如果一级缓存绑定了session对象,那么二级缓存就绑定了session对象。会话工厂. 这意味着对象在这个缓存中的可见性比在一级缓存中要宽得多。
例子:
Session session = factory.openSession();
Employee director1 = session.get(Employee.class, 4);
session.close();
Session session = factory.openSession();
Employee director2 = session.get(Employee.class, 4);
session.close();
assertTrue(director1 != director2);
assertTrue(director1.equals(director2));
在此示例中,将对数据库进行两次查询。Hibernate 将返回相同的对象,但它不会是同一个对象——它们将有不同的引用。
二级缓存默认是禁用的。因此,我们对数据库有两个查询而不是一个。
要启用它,您需要在 hibernate.cfg.xml 文件中写入以下行:
<property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletEhCacheProvider"/>
<property name="hibernate.cache.use_second_level_cache" value="true"/>
启用二级缓存后,Hibernate 的行为会发生一些变化:
Session session = factory.openSession();
Employee director1 = session.get(Employee.class, 4);
session.close();
Session session = factory.openSession();
Employee director2 = session.get(Employee.class, 4);
session.close();
assertTrue(director1 == director2);
只有在所有这些操作之后才会启用二级缓存,并且在上面的示例中,只会执行一次对数据库的查询。