使用ZF2和Doctrine2

时间:2016-03-22 00:04:36

标签: php mysql zend-framework doctrine-orm

我为我的一个客户开发了一个应用程序。他已经有了一个。所以我需要将他的实际数据库(SQL Server)转换为新的数据库(MySQL)。

SQL Server的某些表有超过10.000.000条记录。当我开始开发这个转换器时,我已经开始使用一些带有一些记录的表,所以我找到所有记录并保存到我的新MySQL数据库中。我将向您展示一些代码以便更好地理解(这只是一个例子)

<?php

namespace Converter\Model;

class PostConverter extends AbstractConverter 
{

    public function convert() 
    {
        // this is the default connection, it is a mysql database (new application)
        $em = $this->getEntityManager();
        // this return an alternative connection to the sqlserver database (actual application)
        $emAlternative = $this->getEntityManagerAlternative();

        // instance of Converter\Repository\Post
        $repository = $emAlternative->getRepository('Converter\Entity\Post');

        $posts = $repository->findAll();

        foreach ($posts as $post)
            $post = new Post();
            $post->setTitle($object->getTitle());
            $em->persist($post);
        }  

        $em->flush();
    }
}

现在让我们假设Post表有超过10.000.000条记录。我不能只找到所有并迭代它。我会离开RAM。所以我做了类似的事。

存储库类:

<?php

namespace Converter\Repository;

class Posts extends \Doctrine\ORM\EntityRepository
{

    public function findPosts($limit, $offset)
    {
        $qb = $this->createQueryBuilder('Post');

        $qb->setMaxResults($limit);
        $qb->setFirstResult($offset);

        return $qb->getQuery->getResult();
    }
}

在这里,我在while循环中只找到了几篇帖子。但它有点慢。我找不到更好的解决方案来改善性能

<?php

namespace Converter\Model;

class PostConverter extends AbstractConverter 
{

    public function convert() 
    {
        $em = $this->getEntityManager();
        $emAlternative = $this->getEntityManagerAlternative();

        $repository = $emAlternative->getRepository('Converter\Entity\Post');

        $limit = 1000;

        while ($object = $repository->findPosts($limit, $offset) {
            $post = new Post();
            $post->setTitle($object->getTitle());
            $em->persist($post);

            $offset += $limit;
        }  

        $em->flush();
    }
}

我之前从未做过这样的事情。也许我会走错路。如果你们中的一些人能告诉我正确的话,我真的很感激,所以我可以继续这样做。

谢谢大家

修改

我不能把一个转移到另一个。我在这里发布的只是一个例子,在转换中我必须在插入新数据库之前处理几乎所有数据。他的实际应用是在2005年开发的。数据库甚至没有标准化

4 个答案:

答案 0 :(得分:5)

我目前正在构建一个类似问题的数据仓库系统。 Doctrine's own documentation正确说明:

  

ORM工具主要不适合大规模插入,更新或删除。每个RDBMS都有自己的,最有效的方式来处理此类操作,如果下面列出的选项不足以满足您的需要,我们建议您使用适用于这些批量操作的特定RDBMS工具。

我将如何处理它:

  • 使用Doctrine的工具创建空的MySQL数据库。
  • 列出MySQL数据库中的所有索引和主键并删除它们。我会编写这个脚本。这将消除常量索引更新的开销,直到数据迁移完成。
  • 编写脚本以复制数据。几千批次循环访问SQL Server数据并插入MySQL。
    • 使用PDO或本机库。没有Doctrine或查询构建器。手写编写查询。
    • 打开与SQL Server的一个连接和一个与MySQL的连接。在剧本期间保持打开状态。
    • 使用LIMIT和主键&gt;批量查询最后的身份证使用OFFSET查询通常较慢。
    • 在循环之外准备语句以优化查询处理。
    • 在一个事务中包装每批插入以减少事务开销。
    • &#34;手动&#34;必要时检查参照完整性。您的桌子还没有主键。
    • 如果您有许多表,请将代码分段为对象或函数,以便可以从内存中清除局部变量,并且它更容易调试。
    • 您可能需要定期致电gc_collect_cycles()。如果您的代码被分解为对象,这是一种控制内存的简单方法。
  • 重新创建数据库索引和主键。奖励积分如果这些是从一开始编写的。请注意因重复数据错误而无法创建的任何主键。
  • 在将新的MySQL数据库打开到生产用途之前进行测试和测试。您不想再编写另一个脚本来修复数据迁移。

答案 1 :(得分:1)

如果源数据库(MSSQL)和目标数据库(MySQL)中的模式完全相似或类似,我将从一个数据库导出记录,然后使用纯数据库工具将它们导入另一个数据库。例如:

  1. 在MSSQL中,对于每个表export the records to CSV
  2. 在MySQL中,对于每个表import the records from CSV
  3. 您可以使用shell脚本将所有这些粘合在一起并自动完成该过程。

    此导出/导入将相当快,因为​​它发生在数据库层。这也是你可能获得的最快速度。

    根据定义,从模型层移动整个数据库的速度会变慢:您将为每一行创建一个模型对象。也就是说,当源和目标模式发生分歧时,使用模型层是一种很好的方法,因为这样您就可以使用编程模型使一个模式适应另一个模式。

    在您的具体示例中,如果unset($object)循环底部while,您可能会看到性能有所改善,但我怀疑内存是瓶颈。 (I / O是。)

答案 2 :(得分:1)

我之前和之后都尝试过这种方法,使用DBMS本机数据转储和恢复工具总是更快,而不是通过这样的框架处理记录。

我建议使用bcphttps://msdn.microsoft.com/en-us/library/aa337544.aspx)等实用程序将数据转储出SQL Server,然后使用MySQL LOAD DATAhttp://dev.mysql.com/doc/refman/5.7/en/load-data.html )或mysqlimport将数据导入MySQL。

如果您需要在将数据加载到MySQL之前重新构建数据,您可以通过在MySQL中设置新的数据结构,然后使用可以搜索的实用程序来操作要导入的数据。替换为sed

答案 3 :(得分:1)

首选使用DBMS本机数据转储    并通过恢复工具而不是处理记录    像这样的框架。    以CSV格式导出数据库并导入mysql。