尽可能快地处理40M的文档(和索引)

时间:2009-10-15 22:50:06

标签: xml parsing database

祝你有个美好的一天。 所以我的问题基本上就是这个,我需要处理37.800.000文件。

每个“文件”真的不止于此,我所拥有的是:

  • 37.800.000 XML文档。
  • 超过120.000.000的Tiff图像。

每个XML文档都引用一个或多个Tiff图像,并为它所代表的图像提供一组常用关键字。

我需要构建的是一个解析每个XML文件的系统(不仅有我需要的关键字,而且还有很多垃圾)。对于每个文件,它需要将索引存储在数据库(作为列)和图像的路径(也在数据库中),路径只是因为我不认为将图像存储在里面也是个好主意

最终目的是用户可以使用索引关键字搜索数据库,系统会加载与该索引关联的图像。

我已经使用XPath构建了解析器,并且还定义了db的架构(这很简单)。但是我遇到了两件事情,导致我的系统工作非常缓慢并且偶尔会抛出SQLExceptions:

我想,为了在处理文件时没有充满pc内存,我需要一种分页代码但是反向,以便将相应的项目发送到db(例如,每1000个文件打包一次) ),那么,如何实现这是我的第一个问题。

第二个是XML文件没有连续命名,所以我需要像这样处理重复:当尝试索引和现有的图像或图像时(通过查看它的唯一键名是否也在数据库中),我需要将该图像索引日期与最新的索引图像进行比较,以查看重复项必须要去的(系统只关注最新索引,通过查看索引文件的date关键字)。

任何人都知道如何解决这个问题?我正在使用Java来处理图像搜索门户的解析器和JSP,也使用MySQL。

提前感谢。

这是索引文件之一的结构。

Image文件位于“FileInfo”元素的“dwFileName”属性中。当前索引文档的文件名是“DW5BasketFileName”。如果有多个图像具有相同的索引,则除了扩展名之外,还有更多的索引文件等于(它以001开头并继续计数。

每份文件的平均大小为4KB。

<DWDocument DW5BasketFileName="DOCU0001.001">
  <FileInfos>
    <ImageInfos>
      <ImageInfo id="0,0,0" nPages="0">
        <FileInfo fileName="c:\bandejas\otra5\D0372001.DWTiff" dwFileName="D0001001.DWTiff" signedFileName="D0372001.DWTiff" type="normal" length="66732" />
      </ImageInfo>
    </ImageInfos>
  </FileInfos>
  <FileDatas />
  <Section number="0" startPage="0" dwguid="d3f269ed-e57b-4131-863f-51d147ae51a3">
    <Metadata version="0">
      <SystemProperties>
        <DocID>36919</DocID>
        <DiskNo>1</DiskNo>
        <PageCount>1</PageCount>
        <Flags>2</Flags>
        <StoreUser>DIGITAD1</StoreUser>
        <Offset>0</Offset>
        <ModificationUser>ESCANER1</ModificationUser>
        <StoreDateTime>2009-07-23T21:41:18</StoreDateTime>
        <ModificationDateTime>2009-07-24T14:36:03</ModificationDateTime>
      </SystemProperties>
      <FieldProperties>
        <TextVar length="20" field="NO__REGISTRO" id="0">10186028</TextVar>
        <TextVar length="20" field="IDENTIFICACION" id="1">85091039325</TextVar>
        <TextVar length="40" field="APELLIDOS" id="32">DYMINSKI MORALES</TextVar>
        <TextVar length="40" field="NOMBRES" id="33">JHONATAN OSCAR</TextVar>
        <Date field="FECHA_DEL_REGISTRO" id="64">1985-10-10T00:00:00</Date>
      </FieldProperties>
      <DatabaseProperties />
      <StoreProperties DocumentName="10/10/1985 12:00:00 a.m." />
    </Metadata>
    <Page number="0">
      <Rendition type="original">
        <Content id="0,0,0" pageNumberInFile="0" />
        <Annotation>
          <Layer id="1" z_order="0" dwguid="5c52b1f0-c520-4535-9957-b64aa7834264">
            <LayerLocation x="0" y="0" />
            <CreateUser>ESCANER1</CreateUser>
            <CreateTime>2009-07-24T14:37:28</CreateTime>
            <Entry dwguid="d36f8516-94ce-4454-b835-55c072b8b0c4">
              <DisplayFlags>16</DisplayFlags>
              <CreateUser>ESCANER1</CreateUser>
              <CreateTime>2009-07-24T14:37:29</CreateTime>
              <Rectangle x="6" y="0" width="1602" height="20" flags="0" size="10" color="#ffffff" bkgcolor="#000000" />
            </Entry>
            <Entry dwguid="b2381b9f-fae2-49e7-9bef-4d9cf4f15a3f">
              <DisplayFlags>16</DisplayFlags>
              <CreateUser>ESCANER1</CreateUser>
              <CreateTime>2009-07-24T14:37:31</CreateTime>
              <Rectangle x="1587" y="23" width="21" height="1823" flags="0" size="10" color="#ffffff" bkgcolor="#000000" />
            </Entry>
            <Entry dwguid="9917196d-4384-4052-8193-8379a61be387">
              <DisplayFlags>16</DisplayFlags>
              <CreateUser>ESCANER1</CreateUser>
              <CreateTime>2009-07-24T14:37:33</CreateTime>
              <Rectangle x="0" y="1836" width="1594" height="10" flags="0" size="10" color="#ffffff" bkgcolor="#000000" />
            </Entry>
            <Entry dwguid="3513e0c8-a6c9-42ec-ae9c-dc084376fcdb">
              <DisplayFlags>16</DisplayFlags>
              <CreateUser>ESCANER1</CreateUser>
              <CreateTime>2009-07-24T14:37:35</CreateTime>
              <Rectangle x="0" y="0" width="23" height="1839" flags="0" size="10" color="#ffffff" bkgcolor="#000000" />
            </Entry>
          </Layer>
          <DW4CheckSum dwCheckSum="1479972439" dwDate="131663617" dwTime="319564778" dwImageSize="66732" dwSource="0" source="" />
        </Annotation>
      </Rendition>
    </Page>
  </Section>
</DWDocument>

5 个答案:

答案 0 :(得分:5)

我想说,第一个问题来自磁盘访问时间。即使你的xml文件只有1k,那么它们也相当于37GB的数据并且需要时间来阅读。没有什么可以改善这一点。

但是,您可以确保不会浪费额外的时间进行其他不必要的阻塞计算。

  1. 如果数据库也在同一个磁盘中,批次应远远大于1000,您希望在内存允许的情况下访问数据库的次数很少(如果xml文件连续存储在磁盘中)
  2. 确保尽快释放变量,以便垃圾收集器可以释放内存。
  3. 您希望在计算机等待读取文件的同时执行xml解析,因此您应该设置另一个线程来并行执行任务。
  4. 至于你的第二个问题,对于每个图像,你可以对具有相同索引的图像进行更新sql语句,如果没有更新行,则将此图像插入新行。这是否比使用select后跟插入/更新更好,取决于你有重复的百分比。

    我正在进行假设并假设xml文件的创建速度不比处理它们的速度快,如果是这种情况,您需要做的就是将已经处理过的文件名保存到数据库或到平面文件并在下次启动时将其读回,确保您不必重新开始。

答案 1 :(得分:3)

对于索引我建议您使用Solr,它可以非常快速地索引大量文档。 Solr还有一个专用的类StreamingUpdateSolrServer,用于使用多个线程和批量提交来更新索引。

Solr是用Java编写的,基于快速全文搜索引擎库Lucene。它有一个简单的Java api,您可以通过它将文档提交到服务器进行索引。

这是关于新StreamingUpdateSolrServer类的discussion,用于批量索引文档。

新的Solr 1.4版本中提供了StreamingUpdateSolrServer,这个版本应该会在几天内发布(我从六月开始使用的是一个非常稳定的1.4 Solr版本,我发现它已经非常稳定了。)

答案 2 :(得分:2)

我怀疑你是在一个巨大的数据库事务中加载这些数据。您的个人数据文件并不具有挑战性。但是您在此数据库中输入的条目数是120M x avg#of keywords / image。天真的加载程序可以做到这一点:

start transaction
for each index file
    parse file
    for each keyword
        insert (keyword,imagename) into db
commit transaction

这会强制您的数据库缓冲日志文件中的整个数据加载,以防需要回滚。相反,如果您已启用自动提交,那么您正在为每个关键字执行单独的事务,这也可能是次优的,因为它正在执行并发锁定/解锁数亿次。

中间地点是提交每个'n'个插入,比如10,000:

inserts = 0
reset = 10000
start transaction
for each index file
    parse file
    for each keyword
        insert (keyword,imagename) into db
        inserts += 1
        if inserts % reset == 0
            commit transaction
            start transaction
commit transaction

理想情况下,加载程序也可以重新启动 - 这需要对每个索引文件进行SELECT查询,以便在插入关键字之前查看其图像是否存在于数据库中。

inserts = 0
reset = 10000
start transaction
for each index file
    parse file
    if "SELECT count(*) from IMAGES where name='<insert imagename>'" == 0
        for each keyword
            insert (keyword,imagename) into db
            inserts += 1
            if inserts % reset == 0
                commit transaction
                start transaction
commit transaction

但在你的情况下,这可能是非常昂贵的。作为替代方案,您可以查看数据库支持的导入工具类型,并使用您的程序创建一个或多个文本文件,可能不会比关键字,imagename逗号分隔对列表更复杂。然后,加载器应该具有提交间隔或独占锁定等选项,以最小化锁定和日志开销。

最后,在加载数据之前,先关闭KEYWORDS表上的索引。在加载过程中,此索引没有任何帮助,但您使用每个关键字imagename insert更新它。在表加载后添加此索引将需要一些时间,但您只需在结束时支付一次这种惩罚,而不是在加载期间多次支付。 (如果在可重新启动选项中执行SELECT代码,则执行加载期间具有IMAGES表索引,否则每个SELECT都必须执行全表扫描。)

答案 3 :(得分:0)

好吧,我会读取并解析多线程文件(将它们映射到对象),将这些对象放入线程安全队列,检查重复项,首先在内存中检查,然后在数据库本身,或者如果你有空间,完全在记忆中(不知道你的xmls有多大)。在执行此操作之后或之后,将其一个接一个地推入数据库,如果需要检查数据库中的重复项,请将其停止。它既不漂亮也不快,但考虑到你有这些文件无序......也许是最好的解决方案。

答案 4 :(得分:0)

只是补充:我强烈建议将图像存储在数据库中。如果您已经开始使用120 Mio图像文件,那么即使是现代最先进的文件系统,您也将很快就能拥有它们的边界。

你可能不得不摆脱MySQL并获得一个真正的数据库(DB2,Oracle或SQL Server)。