`

极端事务处理模式:Write-behind缓存(转)

阅读更多

原文:http://www.infoq.com/cn/articles/write-behind-caching

介绍

应用程序通常使用数据缓存来提高性能,尤其针对那些大量应用只读事务的应用程序更是如此。当数据发生变化时,这些应用程序会直接更新数据库。问题在于随着负载的增加,响应时间将随着更新的增长而延长。数据库并不擅于执行大量处理少量记录的并发事务。相对而言,处理批量事务才是数据库的强项。

敏捷中国大会2010四大主题:业务敏捷、管理实践、技术实践、组织变革,为技术团队搭建开放的交流平台。现在报名,九折优惠!Powered by ThoughtWorks

实际上,随着额外负载的增加,响应时间急剧攀升,数据库就会耗用CPU或者磁盘。传统方式下,内存中缓存(in-memory caches)的存储会被限制,以满足JVM的空余内存空间。一旦我们需要缓存更多数量的数据,缓存就会不停地释放数据,从而为其他数据留出存储空间。而且,所需的记录必须被不停地读取,从而使得缓存无效,并将数据库直接暴露给完整的读操作负载。

目前已有多个可用的产品,包括IBM? WebSphere? eXtreme Scale,Oracle Coherence和Gigaspaces,它们均支持将一簇JVM的空闲内存作为缓存,而不是单个的JVM。这就使得合并JVM越多,缓存支持的规模就越大。如果这些JVM都是额外的具有CPU、存储器和网络的服务器,那么读请求的服务就是可伸缩的。通过使用write-behind技术,这些产品还提供更新请求的可伸缩服务。write-behind缓存的可伸缩性使得它可以解决极端事务处理(extreme transaction processing, XTP)场景。Gartner将XTP定义为:“一种应用风格,目的在于支持对分布式事务处理应用程序的设计、开发、部署、管理和维护,其特点在于对性能、可伸缩性、可用性、安全、可管理与可靠性的特别要求。”

本文将使用IBM WebSphere eXtreme Scale演示如何利用write-behind模式优化应用程序的性能。write-behind功能能够根据用户配置的间隔时间,异步地对后端数据库进行批量更新。这一方式具备的明显优势就是能够减少数据库调用,从而减少事务负载,更快地访问网格(grid)中的对象。它比write-through 缓存具有更快的响应时间,后者对缓存的更新会导致对数据库的及时更新。使用write-behind,不再要求事务等待数据库的写操作完成。此外,它能够避免应用程序出现数据库错误,因为write-behind缓冲通过内存复制单元来保持变化,直到它将其传输给数据库。

定义和配置关键概念

什么是“write-behind”缓存

在write-behind缓存中,数据的读取和更新通过缓存进行,与write-through缓存不同,更新的数据并不会立即传到数据库。相反,在缓存中一旦进行更新操作,缓存就会跟踪脏记录列表,并定期将当前的脏记录集刷新到数据库中。作为额外的性能改善,缓存会合并这些脏记录。合并意味着如果相同的记录被更新,或者在缓冲区内被多次标记为脏数据,则只保证最后一次更新。对于那些值更新非常频繁,例如金融市场中的股票价格等场景,这种方式能够很大程度上改善性能。如果股票价格每秒钟变化100次,则意味着在30秒内会发生30 x 100次更新。合并将其减少至只有一次。

在WebSphere eXtreme Scale中,脏记录列表会被复制,以保证JVM退出时仍然存在,用户可以通过设置同步和异步复制的数字,指定复制级别。同步复制意味着JVM退出时,没有数据丢失,但由于主进程必须等待获得变更信息的复制品,因此速度会慢一些。异步复制更快,但如果JVM在复制之前就退出,就会导致最近事务发生的变化可能会丢失。脏记录列表将使用大型的批事务写入到数据源中。如果数据源不可用,则网格会继续处理请求,之后再重新尝试。网格能够随着规模的变化提供更短的响应时间,因为变化会单独地提交到网格,即使数据库被停止,事务仍然能够提交。

Write-behind缓存并不能放之四海而皆准。write-behind的本质注定用户看到的变化,即使被提交也不会立即反映到数据库中。这种时间延迟被称为“缓存写延迟(cache write latency)”或“数据库fubai(database staleness)”;发生在数据库变更与更新数据(或者使得数据无效)以反映其变更的缓存间的延迟则被称为“缓存读延迟(cache read latency)”或“缓存fubai(cache staleness)”。如果系统的每部分在访问数据时都通过缓存(例如,通过公共接口),那么,由于缓存总是保持最新的正确记录,采用write- behind技术就是值得的。可以预见,采用write-behind的系统,作出变更的唯一路径就只能是缓存。

不管是稀疏的缓存还是完整的缓存,都可以用于write-behind特性。稀疏缓存仅存储数据的子集,可以被延迟填充。它通常使用关键字进行访问,因为缓存中并非所有的数据都能够使用,使用缓存进行查询可能无法执行。完整缓存包含了所有的数据,但在首次加载的时候耗时更长。第三种可行之策则是兼取二者所长。首先在短时间内预装载数据的子集,然后延迟加载其余数据。预装载的子集数据大约在总数的20%,但它能够满足80%的请求。

采用这一方式使用缓存的应用程序适用的场景为:适用简单CRUD(创建,读取,更新和删除)模式的分区数据模型。

配置write-behind功能

对于WebSphere eXtrem Scale而言,在objectgrid.xml配置中,通过将writeBehind属性添加到backingMap元素,就启用了write- behind功能,如下所示。参数的值使用语法“"[T(time)][;][C(count)]",用以指定数据库更新发生的时间。当到达设置的时间,或者队列集中的变化次数达到设定的count值,更新就会被写入到持久存储中。

列表1:write-behind配置的示例

<objectGrid name="UserGrid">

<backingMap name="Map" pluginCollectionRef="User" lockStrategy="PESSIMISTIC" writeBehind="T180;C1000"    /> 

JPA加载器

WebSphere eXtreme Scale使用了加载器读取内存中缓存的数据,以及将数据写入到数据库中。从WebSphere eXtreme Scale的6.1.0.3版本开始,包含了两个内建的加载器,它们与JPA提供器相交互,负责将关系数据映射到ObjectGrid集中,这两个内建的加载器分别为JPALoader和JPAEntityLoader。JPALoader用于缓存中存储POJO,JPAEntityLoader则用于缓存中存储ObjectGrid实体。

若要配置JPA加载器,就必须修改添加到META-INF目录下的objectgrid.xml和persistence.xml文件。加载器 bean可以与必要的实体类名一起被加入到objectgrid.xml中。在objectgrid.xml中必须定义事务的回调函数,这样就可以接收事务提交或回滚的事件,并将其发送到JPA层。persistence.xml文件则表示一个特殊的JPA提供器,它针对具有提供器的特定属性的持久化单元。

考察一个商业案例

考察一个在线的银行网站,当用户数增长时,响应时间变慢,且存在可伸缩性问题。他们需要在现有硬件环境下支持客户需求。接下来,我们就以这一案例来展示write-behind特性是如何帮助他们解决这一问题的。

用例:门户个性化

与直接从数据库中拉取用户个人信息不同,我们首先会将数据库中取出的个人信息,预先加载到缓存中。这意味着缓存是为读请求服务,而不是数据库。在旧系统中,个人信息的更新同样是直接写入到数据库。在可接受的响应时间内,随着数据库服务器的超负荷,每秒钟发生的并发更新数将受到限制。新的系统则是将个人信息的变更写入到网格中,然后使用write-behind技术将这些变更推到数据库中。这就保证了服务的质量和性能,并完全解除了单实例数据库与读写个人信息操作之间的耦合。现在,客户只需要将更多的JVMs/server添加到网格中,就可以扩大个人信息服务的规模。数据库不再成为瓶颈,因为发送到后端的事务数得到了锐减。更快的响应时间带来更快的页面加载,大大改善了用户体验,有效地利用了个人信息服务器的规模,提高了可用性,因为数据库不再成为单一失败点。

对于本用例,我们对DB2O数据库采用了WebSphere eXtreme Scale以及OpenJPA提供器。这一场景的数据模型为User与UserAccount以及UserTransaction之间的一对多关系。下面为User类的代码片段,展现了这样一种关系:

列表2:用例的实体关系

@OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = { Cas-cadeType.ALL })

private Set<UserAccount> accounts = new HashSet<UserAccount>();


    
@OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = { Cas-cadeType.ALL })

private Set<UserTransaction> transactions = new HashSet<UserTransaction>();

1.生成数据库

示例代码包含了PopulateDB类,它能够加载某些用户数据到数据库中。DB2数据库的连接信息被定义在之前展示的 persistance.xml文件中。在persistence.xml中的持久单元名(persistence unit name)将用于创建JPA EntityManagerFactory。用户对象会被创建,然后被批量地持久化到数据库中。

2. 准备缓存

一旦数据库被加载,数据网格代理(data grid agents)就会预先加载缓存。记录被批量写入到缓存中,因此在客户端与服务器之间几乎没有产生通信。还可以使用多客户端(multiple clients)来加快准备时间。在准备缓存时,一些“热门”数据可以作为所有记录的子集,其余数据则可以采用延迟加载。预先加载缓存提高了缓存命中的几率,减少了从后端层中获取数据的需要。在这个例子中,匹配数据库记录的数据会被插入到缓存中,而不是从数据库中加载,从而加快了执行时间。

3.在网格上生成加载

示例代码包含一个客户端驱动器,模仿在网格上的操作,用以演示write-behind缓存功能如何提高性能。客户端包含多个选项调整加载行为。下面的命令使用了10个线程,以每个线程200个请求将500k的记录加载到“UseGrid”网格中。

$JAVA_HOME/bin/java -Xms1024M -Xmx1024M -verbose:gc -classpath
$TEST_CLASSPATH ogdriver.ClientDriver -m Map -n 500000 -g UserGrid -nt 10 -r
200 -c $CATALOG_1 

4.结果

使用write-behind特性能够显著提高性能。我们分别使用了write-through和write-behind技术运行示例代码,比较了各自的响应时间以及数据库的CPU占用率。插入到缓存中的数据与数据库中的记录相匹配,这就避免了缓存的准备时间,保证读取的响应时间一致,这样才能比较写的响应时间。列表3展示了write-through场景下的响应时间,读取数据花费了不到10ms,而写花费的时间达到了450——550ms。在执行过程中,网络跳转与磁盘I/O操作耗费了大多数时间。列表4展示了write-behind场景下的响应时间,在这种情况下所有的事务都被提交到网格中。从图表中可以看出,它所消耗的读和写的响应时间几乎相同,都消耗了2.5-5ms。这两个图表极为明显地说明,对于更新而言,write- through响应时间更长,而write-behind耗费的更新时间几乎与读取操作相同。如果添加更多的JVM,则能够增加缓存的容量,无需改变响应时间,数据库也不再成为瓶颈。

列表3:采用write-through缓存的响应时间图表

列表4:采用write-behind缓存的响应时间图表

数据库的CPU占用率图表展现了使用write-behind对后端加载的改善。列表5是一幅采用write-through产生的数据库CPU占用图。可以看见,在运行及时写入数据库的所有更新时,CPU的使用是连续的。使用时,CPU空闲状态的波动在4%到10-12%之间。采用write- through技术,后端加载保持了恒定的值。如果采用write-behind,在满足缓存的间隔时间内,后端的加载耗费了更低的CPU占用率,如列表 6所示。该图显示,当数据库较为空闲时,占用率为4%,在三分钟间隔时间结束,或者更新记录数达到峰值时,会批量地将更新写入到磁盘中,此时CPU占用率会在短时间内达到12-16%。write-behind的配置可以调整,以满足自己的运行环境,这其中包括写事务的比例,相同记录更新的频率,以及数据库的更新延迟。 

列表5:采用write-through缓存的数据库CPU占用率图表 

列表6:采用write-behind缓存的数据库CPU占用率图表

结论

本文重新审视了write-behind缓存技术,并展示了如何在WebSphere eXtreme Scale下实现这一特性。write-behind缓存功能减少了后端加载,降低了事务的响应时间,并将应用程序与后端失败隔离开。这些优势以及它配置的简单性,都使得write-behind缓存异常的强大。

参考

  1. Gartner RAS Core Research Note G00131036, Gartner Group - http://www.gartner.com/
  2. Gartner RAS核心研究备忘录G00131036,Gartner组织- http://www.gartner.com/
  3. User’s Guide to WebSphere eXtreme Scale
  4. WebSphere eXtreme Scale用户指南
  5. WebSphere eXtreme Scale Wiki

资源

  1. WebSphere eXtreme Scale free trial download
  2. WebSphere eXtreme Scale V7.0 on Amazon Elastic Compute Cloud (EC2)

致谢

感谢Billy Newport,Jian Tang,Thuc Nguyen,Tom Alcott和Art Jolin对撰写本文给予的帮助。

查看英文原文:Extreme Transaction Processing Patterns: Write-behind Caching

分享到:
评论

相关推荐

    SegmentBased Recovery: Writeahead logging revisited

    标题《Segment-Based Recovery: Write-ahead logging revisited》表明本文是对传统写前日志(Write-Ahead Logging,WAL)技术的重新审视,尤其是针对分段恢复(Segment-Based Recovery)的应用。WAL是一种确保数据库...

    阵列Cache写机制:Write-through与Write-back区别.docx

    Starting and stopping cache flushing levels是Write-back方式下生效的两个设置,它们影响控制器如何处理未写入磁盘的缓存内数据。缓存内数据写入磁盘称为flushing。可以配置Starting and stopping cache flushing ...

    MDK5使用JLink调试时出现内存对齐方面的警告JLink Warning

    我的keil5是MDK524版本,在使用JLink调试时出现JLink Warning:mis-aligned memory write:address:0x20000000,numbytes:2,alignment:2(halfword-aligned)的警告。找到c:\Keil_v5\ARM\segger(Keil安装目录)下的JL2...

    硬盘Cache写机制 Write-through与Write-back的区别.docx

    Write-through 模式处理流程: 1. 数据更新 2. 同时写入缓存 Cache 和后端存储 3. 数据写入完毕 Write-back 模式处理流程: 1. 数据更新 2. 只写入缓存 Cache 3. 缓存中的数据被替换出缓存时,被修改的缓存数据才...

    fs-write-stream-atomic-类似`fs.createWriteStream(...)`但是是原子的

    3. **错误处理**:如果在写入或重命名过程中发生错误,`fs-write-stream-atomic`会清理临时文件,避免留下无用的数据。 4. **兼容性**:这个库旨在与Node.js的`fs.createWriteStream`接口保持一致,因此开发者可以...

    PSWriteColor:Write-Color是Write-Host的包装,可让您使用彩色输出创建美观的脚本

    Write-Color是Write-Host的包装,使您可以创建带有彩色输出的漂亮脚本。 变更日志 0.87.3-2021.04.12 :light_bulb: 禁用文件外出功能的WhatIf功能,这将阻止记录到文件 :light_bulb: 添加了LogRetry (2)以提供...

    MDK525安装JLink驱动后,下载出现JLink读取内存错误解决

    新版MDK-RAM_v5.25.2使用JLink下载调试““**JLink Warning: Mis-aligned memory read: Address: ......”错误解决

    Caused by: android.system.ErrnoException: write failed: ENOSPC (

    ### 错误解析:Caused by: android.system.ErrnoException: write failed: ENOSPC (No space left 在Android开发过程中,可能会遇到“Caused by: android.system.ErrnoException: write failed: ENOSPC (No space ...

    css3选择器-read.docx

    CSS3中的`:read-write`和`:read-only`选择器是两个非常重要的伪类选择器,它们主要用于处理用户可编辑和不可编辑元素的样式。这两个选择器对于创建动态和交互式的网页设计至关重要,尤其是在处理表单元素如`input`和...

    ehcache-spring-write-behind:实现 Ehcache + Spring 和 write behind 模式

    ####EhCache Spring 如何##Recipe 15(Spring 缓存抽象) 在这个秘籍中,您将看到如何在 Spring 3.1 及更高版本的最新缓存抽象 API 中使用内置的 EhCache 支持。 我们将使用基于注释的方法,但将遵循传统的 EhCache...

    Coherence企业级缓存(四) 数据管理模式.pdf

    本文将深入探讨Coherence提供的四种数据管理模式:Read-Through、Write-Through、Refresh-Ahead以及Write-Behind,并分析它们的特点及适用场景。 #### 二、Coherence简介 Coherence是Oracle公司推出的一款面向企业...

    前端开源库-csv-write-stream

    4. **错误处理**:`csv-write-stream` 提供了错误处理机制,当数据处理过程中出现错误时,可以捕获并处理这些异常。 5. **集成其他库**:这个库可以很好地与其他Node.js库结合使用,例如与`readable-stream` 或 `...

    Redis扩展:缓存雪崩和缓存穿透问题解决方案.docx

    - **Write Behind Caching**:更新数据时,仅更新缓存,定时异步地将缓存中的修改同步到数据库。 4. **版本号机制**: - 在缓存中为每个数据项增加一个版本号字段,每次更新数据时都更新版本号。读取数据时,先...

    实验6:Two-way Associated Cache1

    2. **写分配(write allocate)**:写操作会分配新的缓存块,与write back搭配,更新缓存并设置脏位。 **实验中的实现**: 本实验实现的是一级Cache,采用Two-way Associated结构,使用LRU替换算法,并结合write back...

    PyPI 官网下载 | django-write-around-cache-0.2.0.tar.gz

    "Write-around"缓存策略是一种设计模式,它在主存储器无法满足高速访问需求时,允许数据绕过主存储器直接写入更快的存储层,从而提高数据存取速度。 **缓存系统在Web开发中的作用** 在Web开发中,缓存是提升应用...

    CSerialPort串口类更新版2014-01-10

    ** PURPOSE This class can read, write and watch one serial port. ** It sends messages to its owner when something happends on the port ** The class creates a thread for reading and ...

    计算机组成原理英文版课件:10-Memory.pdf

    1980年代,微处理器还没有内置缓存,但到1995年,二级缓存已成为微处理器的标准配置。随着处理器性能每年以55%的速度增长,而DRAM(动态随机存取内存)的提升速度仅为7%,两者之间的性能差距以50%的年增长率扩大,这...

    PHP Master Write Cutting-edge Code.pdf

    - **SQL查询**:学习如何编写基本的SQL查询语句以及更复杂的事务处理。 - **安全性**:讨论如何避免SQL注入攻击和其他常见的安全问题。 ##### 3. API (Application Programming Interfaces) 随着互联网的发展,API...

    Write-ahead Logging

    ### 写前日志(Write-ahead Logging)技术详解 #### 一、引言与目标 **写前日志**(Write-ahead Logging, WAL)是一种广泛应用于数据库系统中的技术,旨在提高系统的高可用性、数据一致性和故障恢复能力。通过在修改...

    计算机组成原理英文版课件:11-Memory.pdf

    \n\n缓存的工作机制包括写策略(Write Policy)和替换策略(Replacement Policy)。写策略决定如何处理缓存与主存的数据一致性,如写直达、写回、写一次等。替换策略则决定了当缓存满时,如何选择要替换的块,如LRU...

Global site tag (gtag.js) - Google Analytics