`
kang275284
  • 浏览: 165346 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

数据库对象的缓存策略

阅读更多
前言
本文探讨Jive(曾经开源的Java论坛)和Hibernate(Java开源持久层)的数据库对象的缓存策略,并阐述作者本人的Lightor(Java开源持久层)采用的数据库对象缓存策略。
本文的探讨基于以前开源的Jive代码,Hibernate2.1.7源码,和作者本人的Lightor代码。
本文用ID (Identifier的缩写)来代表数据记录的关键字。
数据对象查询一般分为两种:条件查询,返回一个满足条件的数据对象列表; ID查询,返回ID对应的数据对象。
本文主要探讨“条件查询”和“ID查询”这两种情况的缓存策略。
本文只探讨一个JVM内的数据缓存策略,不涉及分布式缓存;本文只探讨对应单表的数据对象的缓存,不涉及关联表对象的情况。
一、Jive的缓存策略
1.Jive的缓存策略的过程描述:
(1)条件查询的时候,Jive用 select id from table_name where …. (只选择ID字段)这样的SQL语句查询数据库,来获得一个ID列表。
(2) Jive根据ID列表中的每个ID,首先查看缓存中是否存在对应ID的数据对象:如果存在,那么直接取出,加入到 结果列表中;如果不存在,那么通过一条select * from table_name where id = {ID value} 这样的SQL查询数据库,取出对应的数据对象,放入到结果列表,并把这个数据对象按照ID放入到缓存中。
(3) ID查询的时候,Jive执行类似第(2)步的过程,先从缓存中查找该ID,查不到,再查询数据库,然后把结果放入到缓存。
(4) 删除、更新、增加数据的时候,同时更新缓存。
2.Jive缓存策略的优点:
(1) ID查询的时候,如果该ID已经存在于缓存中,那么可以直接取出。节省了一条数据库查询。
(2) 当多次条件查询的结果集相交的情况下,交集里面的数据对象不用重复从数据库整个获取,直接从缓存中获取即可。
比如,第一次查询的ID列表为{1, 2},然后根据ID列表的ID从数据库中一个一个取出数据对象,结果集为{a(id = 1), b(id = 2)}。
下一次查询的ID列表为{2, 3},由于ID = 2的数据对象已经存在于缓存中,那么只要从数据库中取出ID = 3的数据对象即可。
3.Jive缓存策略的缺点:
(1) 在根据条件查找数据对象列表的过程中,DAO的第(1)步用来获得ID列表的那一次数据库查询,是必不可少的。
(2) 如果第(1)步返回的ID列表中有n个ID,在最坏的命中率(缓存中一个对应ID都没有)情况下,Jive还要再查询n次数据库。最坏情况下,共需要n + 1数据库查询。
二、Hibernate的二级缓存策略
Hibernate用Session类包装了数据库连接从打开到关闭的过程。
Session内部维护一个数据对象集合,包括了本Session内选取的、操作的数据对象。这称为Session内部缓存,是Hibernate的第一级最快缓存,属于Hibernate的既定行为,不需要进行配置(也没有办法配置 :-)。
Session的生命期很短,存在于Session内部的第一级最快缓存的生命期当然也很短,命中率自然也很低。当然,这个Session内部缓存的主要作用是保持Session内部数据状态同步。
如果需要跨Session的命中率较高的全局缓存,那么必须对Hibernate进行二级缓存配置。一般来说,同样数据类型(Class)的数据对象,共用一个二级缓存(或其中的同一块)。
1.Hibernate二级缓存策略的过程描述:
(1)条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。
(2) 把获得的所有数据对象根据ID放入到第二级缓存中。
(3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。
(4) 删除、更新、增加数据的时候,同时更新缓存。
 
2.Hibernate二级缓存策略的优点:
(1) 具有Jive缓存策略同样的第(1)条优点:ID查询的时候,如果该ID已经存在于缓存中,那么可以直接取出。节省了一条数据库查询。
(2) 不具有Jive缓存策略的第(2)条缺点,即hibernate不会有最坏情况下的 n + 1次数据库查询。
3.Hibernate二级缓存策略的缺点:
(1) 同Jive缓存策略的第(1)条缺点一样,条件查询的时候,第(1)步的数据库查询语句是不可少的。而且Hibernate选择所有的字段,比只选择ID字段花费的时间和空间都多。
(2) 不具备Jive缓存策略的第(2)条优点。条件查询的时候,必须把数据库对象从数据库中整个取出,即使该数据库的ID已经存在于缓存中。
三、Hibernate的Query缓存策略
可以看到,Jive缓存和Hibernate的二级缓存策略,都只是针对于ID查询的缓存策略,对于条件查询则毫无作用。(尽管Jive缓存的第(2)个优点,能够避免重复从数据库获取同一个ID对应的数据对象,但select id from …这条数据库查询是每次条件查询都必不可少的)。
为此,Hibernate提供了针对条件查询的Query缓存。
1.Hibernate的Query缓存策略的过程描述:
(1) 条件查询的请求一般都包括如下信息:SQL, SQL需要的参数,记录范围(起始位置rowStart,最大记录个数maxRows),等。
(2) Hibernate首先根据这些信息组成一个Query Key,根据这个Query Key到Query缓存中查找对应的结果列表。如果存在,那么返回这个结果列表;如果不存在,查询数据库,获取结果列表,把整个结果列表根据Query Key放入到Query缓存中。
(3) Query Key中的SQL涉及到一些表名,如果这些表的任何数据发生修改、删除、增加等操作,这些相关的Query Key都要从缓存中清空。
2.Hibernate的Query缓存策略的优点
(1) 条件查询的时候,如果Query Key已经存在于缓存,那么不需要再查询数据库。命中的情况下,一次数据库查询也不需要。
3.Hibernate的Query缓存策略的缺点
(1) 条件查询涉及到的表中,如果有任何一条记录增加、删除、或改变,那么缓存中所有和该表相关的Query Key都会失效。
比如,有这样几组Query Key,它们的SQL里面都包括table1。
SQL = select * from table1 where c1 = ? …., parameter = 1, rowStart = 11, maxRows = 20.
SQL = select * from table1 where c1 = ? …., parameter = 1, rowStart = 21, maxRows = 20.
SQL = select * from table1 where c1 = ? ….., parameter = 2, rowStart = 11, maxRows = 20.
SQL = select * from table1 where c1 = ? ….., parameter = 2, rowStart = 11, maxRows = 20.
SQL = select * from table1 where c2 = ? …., parameter = ‘abc’, rowStart = 11, maxRows = 20.
 
当table1的任何数据对象(任何字段)改变、增加、删除的时候,这些Query Key对应的结果集都不能保证没有发生变化。
很难做到根据数据对象的改动精确判断哪些Query Key对应的结果集受到影响。最简单的实现方法,就是清空所有SQL包含table1的Query Key。
 
(2) Query缓存中,Query Key对应的是数据对象列表,假如不同的Query Key对应的数据对象列表有交集,那么,交集部分的数据对象就是重复存储的。
比如,Query Key 1对应的数据对象列表为{a(id = 1), b(id = 2)},Query Key 2对应的数据对象列表为{a(id = 1), c(id = 3)},这个a就在两个List同时存在了两份。
4.二级缓存和Query缓存同步的困惑
假如,Query缓存中,一个Query Key对应的结果列表为{a (id = 1) , b (id = 2), c (id = 3)}; 二级缓存里面有也id = 1对应的数据对象a。
这两个数据对象a之间是什么关系?能够保持状态同步吗?
我阅读Hibernate的相关源码,没有发现两个缓存之间的这种同步关系。
或者两者之间毫无关系。就像我上面所说的,只要表数据发生变化,相关的Query Key都要被清空。所以不用考虑同步问题?
四、Lightor的缓存策略
Lightor是我做的Java开源持久层框架。Lightor的意思是,Lightweight O/R。Hibernate,JDO,EJB CMP这些持久层框架,都是Layer。Lightor算不上Layer,而只是一个Helper。这里的O/R意思不是Object/Relational,而是Object/ResultSet的意思。:-)
Lightor的缓存策略,主要参照Hibernate的缓存思路,Lightor的缓存也分为 Query缓存和ID缓存。但其中有一点不同,两者之间并不是毫无联系的,而是相互关联的。
1.Lightor的缓存策略的过程描述:
(1) 条件查询的请求一般都包括如下信息:SQL, 对应SQL的参数,起始记录位置(rowStart),最大记录个数(maxRows),等。
(2) Lightor首先根据这些信息组成一个Query Key,根据这个Query Key到Query缓存中查找对应的结果ID列表。注意,这里获取的是ID列表。
如果结果ID列表存在于Query缓存,那么根据这个ID列表的每个ID,到ID缓存中取对应的数据对象。如果所有ID对应的数据对象都找到,那个返回这个数据对象结果列表。注意,这里获取的是整个数据对象(所有字段)的列表。
如果结果ID列表不存在于Query缓存,或者结果ID列表中的某一个ID不存在于ID缓存,那么,就查询数据库,获取结果列表。然后,把获取的每个数据对象按照ID放入到ID缓存;并组装成一个ID列表,按照Query Key存放到Query缓存中。注意,这里是把ID列表,而不是整个对象列表,放入到Query缓存中。
(3) ID查询的时候,Lightor先从ID缓存中查找该ID,如果不存在,那么查询数据库,把结果放入ID缓存。
(4) Query Key中的SQL涉及到一些表名,如果这些表的任何数据发生修改、删除、增加等操作,这些相关的Query Key都要从缓存中清空。
2.Lightor的缓存策略的优点
(1) Lightor的ID缓存具有Jive缓存,和Hibernate二级ID缓存的优点。ID查询的时候,如果该ID已经存在于缓存中,那么可以直接取出。节省了一条数据库查询。
(2) Lightor的Query缓存具有Hibernate的Query缓存的优点。条件查询的时候,如果Query Key已经存在于缓存,那么不需要再查询数据库。命中的情况下,一次数据库查询也不需要。
(3) Lightor的Query缓存中,Query Key对应的是ID列表,而不是数据对象列表,真正的数据对象只存在于ID缓存中。所以,不同的Query Key对应的ID列表如果有交集,ID对应的数据对象也不会在ID缓存中重复存储。
(4) Lightor的缓存也没有Jive缓存的最坏情况n + 1次数据库查询缺点。
3.Lightor的缓存策略的缺点
(1) Lightor的Query缓存具有Hibernate的Query缓存的缺点。条件查询涉及到的表中,如果有任何一条记录增加、删除、或改变,那么缓存中所有和该表相关的Query Key都会失效。
(2) Lightor的ID缓存也具有hibernate的二级ID缓存具有的缺点。条件查询的时候,即使ID已经存在于缓存中,也需要重新把数据对象整个从数据库取出,放入到缓存中。
五、Query Key的效率
Query缓存的Query Key的空间和时间开销比较大。
Query Key里面存放的东西不少,SQL, 参数,范围(起始,个数)。
这里面最大的东西就是SQL。又占地方,又花时间(hashCode, equals)。
Query Key最关键的两个方法是hashCode和equals,重点是SQL的hashCode和equals。
 
Lightor的做法是,由于Lightor直接使用SQL,不用HQL、OQL之类,所以推荐尽量使用static final String的SQL,能够节省空间和时间,以至于Query Key的效率能够相当于ID Key的效率。
至于Hibernate的QueryKey,有兴趣的读者可以去下载阅读Hibernate的各个版本的源代码,跟踪一下QueryKey的实现优化过程。
六、总结
这里列一个表,综合表示Jive, Hibernate, Lightor的缓存策略的特征。
 
N + 1问题
重复ID缓存问题
Query缓存支持
Jive缓存
不支持
Hibernate缓存
支持
Lightor缓存
支持
 
注:
“重复ID缓存问题”的含义是,每次条件查询,不是只取ID列表,而是取出完整对象(所有字段)的列表。这样,同一个ID对应的数据对象,即使在缓存中已经存在,也可能被重新放入缓存。参见相关缓存的缺点描述。
“重复ID缓存问题”的负面效应到底有多大,就看你的select id from …(只选择ID)比你的 select * from … (选择所有字段)快多少。主要影响因素是,字段的个数,字段值的长度,与数据库服务器之间网络传输速度。
不管怎么说,即使选择所有字段,也只是一次数据库查询。而N + 1问题带来的可能最坏的负面效应(N + 1次数据查询)却是非常大的。
选择缓存策略的时候,应根据这些情况发生的概率和正负面效应进行取舍。
 
分享到:
评论

相关推荐

    数据库缓存技术文档 缓存 数据库

    数据库缓存技术是一种提高系统性能的关键策略,尤其在高并发访问和大数据量的场景下,其作用尤为显著。本文档将深入探讨数据库缓存的基本原理、适用条件以及...理解并正确应用缓存策略,对于优化数据库应用至关重要。

    ASP.NET 数据库缓存

    总的来说,ASP.NET 2010的数据库缓存策略能够显著优化Web应用的性能,通过合理地使用缓存,开发者可以创建更加高效、响应快速的网站。在实际开发中,应根据具体需求和资源情况,选择合适的缓存策略和配置,以达到...

    linq 的全数据库缓存

    2. **数据缓存策略**:对于每一次查询,系统首先尝试从缓存中获取数据。如果缓存中不存在相应的数据,则从数据库中获取并将数据存入缓存。 3. **缓存键的设计**:从示例代码中可以看出,缓存键采用固定的前缀加上...

    Memchached数据库缓存缓存思路

    ### Memcached数据库缓存策略详解 #### 一、引言 随着互联网技术的快速发展与用户需求的日益增长,高并发场景下的系统设计成为了许多大型网站和应用必须面对的问题之一。数据库作为信息系统的核心组件,其性能直接...

    hibernate 缓存策略

    总的来说,理解并合理运用Hibernate的缓存策略,能够显著提升应用性能,减少数据库的压力,但同时也需要考虑数据一致性及并发控制等问题。通过阅读源码,可以更深入地了解其内部机制,以便更好地进行性能调优。

    通过缓存数据库结果提高PHP性能

    然而,如何选择合适的缓存策略是个挑战。对于那些需要实时数据库数据的应用,基于时间的缓存更新可能不理想,因为这样可能会错过某些即时变化。在这种情况下,利用“数据库更改通知”可以确保应用程序在数据库数据...

    Hibernate的缓存策略

    ### Hibernate的缓存策略 #### 一、缓存的基本概念 缓存技术是现代软件架构设计中的重要组成部分,尤其在数据库交互频繁的应用场景下尤为重要。简单来说,缓存是一种存储技术,位于应用程序与物理数据之间,目的是...

    深入挖掘ASP.NET 2.0系列课程(4):数据库的缓存管理与网站页面的缓存管理

    对象缓存则是将数据库对象(如实体)存储在缓存中,以便快速检索。这两种方法都可以通过`System.Web.Caching.Cache`类来实现,该类提供了丰富的API用于添加、删除和管理缓存项。 网站页面的缓存管理是另一种提升...

    hibernate缓存策略

    ### Hibernate缓存策略详解 #### 一、理解Hibernate缓存 ##### 1.1 缓存概念 在软件开发领域,缓存技术是一项重要的优化手段,它可以显著提高应用程序的性能和响应速度。Hibernate作为一种持久层框架,其核心功能...

    高性能Java数据库缓存_缓存思路

    1. 单个对象缓存:存储单条数据库记录,常用的数据结构如HashMap,配合LRU算法优化。 2. 列表缓存:例如论坛帖子列表,需要支持分页功能。 3. 长度缓存:记录集合的总数,用于实现分页显示。 4. 复杂查询缓存:如按...

    java之数据库缓存

    例如,了解Ehcache的缓存策略(LRU、LFU、TTI/TTL)以及它们如何决定何时移除缓存项,或者Redis的数据结构(哈希、列表、集合、有序集合)及其如何影响性能,都是开发者需要掌握的高级技能。 同时,“工具”在这里...

    数据库缓存扩展

    Memcache是一款广泛使用的分布式内存对象缓存系统,它能够存储数据在内存中,减少对数据库的直接访问,从而提升了整体系统的响应速度。 Memcache.dll是Windows环境下PHP与Memcache服务交互的动态链接库文件,版本为...

    高并发高负载数据库架构策略!

    在优化Web应用性能并采用数据库对象缓存策略后,可以处理较大的访问量。 进一步优化,可以采用数据库集群部署,例如Oracle的多实例配置。这种方式通过共享同一物理存储,提供多个数据库实例,通过虚拟IP对外提供...

    高效方便实用的远程数据库对象.v1.8.rar

    7. **缓存机制**:可能包含本地或分布式缓存策略,减少对数据库的频繁访问,提高系统响应速度。 8. **安全防护**:可能内置防止SQL注入的措施,保障系统的安全性。 9. **性能优化**:通过批量操作、延迟加载等手段...

    支撑1000万pv的数据库缓存解决方案.docx

    标题中的“支撑1000万pv的数据库缓存解决方案”指的是设计和实施一个能够处理每日1000万页面浏览量(Page View,简称PV)的数据库缓存策略,以提高系统的性能和效率。描述中提到的系统是基于Hibernate构建的,包括...

    数据库缓存:释放性能瓶颈的金钥匙

    通过这种方式,可以针对不同的数据对象设定不同的缓存策略,实现更加精细化的管理。 ##### 4. 缓存预热 在系统启动初期,预先加载关键数据到缓存中,可以有效地避免缓存穿透问题: ```php # 缓存预热示例 $users ...

    ASP.NET 页面/数据库缓存实例

    通过在页面的后台代码(如 `Cache_Page.aspx.cs`)设置缓存策略,我们可以指定缓存的时间、依赖项等属性。 - 使用 `OutputCache` 指令可以控制缓存行为,例如在 `@Page` 指令中添加 `OutputCache` 参数,设置缓存...

    phpFastCache是一种高性能分布式的对象缓存系统

    phpFastCache是一款针对PHP开发的高效分布式对象缓存系统,它的主要目的是提高动态Web应用的性能,通过将频繁访问的数据暂存到缓存中,减少对数据库的直接操作,从而降低数据库负载。这款开源库在Web开发领域广泛...

Global site tag (gtag.js) - Google Analytics