使用 ZF2 和 Doctrine2 将 SQL 服务器表转换为 MySQL
Convert SQL Server tables to MySQL using ZF2 and Doctrine2
我为我的一位客户开发了一个应用程序。他已经有一个了。所以我需要将他的实际数据库(SQL 服务器)转换为新数据库(MySQL)。
一些 table 的 SQL 服务器有超过 10.000.000 条记录。当我开始开发这个转换器时,我从一些带有几条记录的 table 开始,所以我找到所有记录并保存到我的新 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 table 有超过 10.000.000 条记录。我不能只找到所有内容并对其进行迭代。我会离开RAM。所以我做了这样的事情。
存储库class:
<?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年开发的,数据库连规范化都没有
如果源数据库 (MSSQL) 和目标数据库 (MySQL) 中的模式完全相同或相似,我会从一个数据库导出记录,然后使用纯数据库将它们导入另一个数据库工具。示例:
- 在 MSSQL 中,对于每个 table、export the records to CSV
- 在 MySQL 中,对于每个 table、import the records from CSV
您可以使用 shell 脚本将所有这些粘合在一起并自动执行该过程。
这个 export/import 会相当快,因为它发生在数据库层。这也是您可能获得的最快速度。
根据定义,从模型层移动整个数据库会变慢:您将为每一行创建一个模型对象。也就是说,当源模式和目标模式不同时,使用模型层是一种很好的方法,因为这样您就可以使用编程模型使一个模式适应另一个模式。
在您的具体示例中,如果您在 while
循环的底部 unset($object)
,您可能会看到性能有所提高,但我怀疑内存是瓶颈。 (I/O 是。)
我以前尝试过这种方法,根据我的经验,使用 DBMS 本机数据转储和恢复工具总是比通过这样的框架处理记录更快。
我建议使用 bcp
(https://msdn.microsoft.com/en-us/library/aa337544.aspx) to dump the data out of SQL Server and then use MySQL's LOAD DATA
(http://dev.mysql.com/doc/refman/5.7/en/load-data.html) 或 mysqlimport
等实用程序将数据导入 MySQL。
如果您需要在将数据加载到 MySQL 之前重新构造数据,您可以通过在 MySQL 中设置新的数据结构,然后使用一个可以像 sed
.
这样搜索和替换的实用程序
我目前正在构建一个具有类似问题的数据仓库系统。 Doctrine's own documentation 正确表述:
An ORM tool is not primarily well-suited for mass inserts, updates or deletions. Every RDBMS has its own, most effective way of dealing with such operations and if the options outlined below are not sufficient for your purposes we recommend you use the tools for your particular RDBMS for these bulk operations.
我会这样处理:
- 使用 Doctrine 的工具创建空的 MySQL 数据库。
- 列出 MySQL 数据库中的所有索引和主键并删除它们。我会编写脚本。这将消除在数据迁移完成之前不断更新索引的开销。
- 写一个脚本来复制数据。以几千个为单位循环遍历 SQL 服务器数据并插入 MySQL。
- 使用 PDO 或本机库。没有 Doctrine 或查询构建器。手写查询。
- 打开一个到 SQL 服务器的连接和一个到 MySQL 的连接。在脚本运行期间保持它们打开。
- 使用
LIMIT
和主键 > 最后 id 批量查询。使用 OFFSET
查询通常较慢。
- 在循环外准备语句以优化查询处理。
- 将每批插入包装在一个事务中以减少事务开销。
- "Manually" 如有必要,请检查参照完整性。您的表还没有主键。
- 如果您有很多表,请将您的代码分割成对象或函数,这样可以从内存中清除局部变量,这样调试起来会更容易。
- 您可能需要定期致电
gc_collect_cycles()
。如果您的代码被分解成对象,这是一种控制内存的简单方法。
- 重新创建数据库索引和主键。如果这些是从一开始就编写好的,那么奖励积分。注意是否有任何由于重复数据错误而无法创建的主键。
- 在打开新的 MySQL 数据库用于生产之前进行测试和测试。您不想稍后编写另一个脚本来修复数据迁移。
更喜欢使用 DBMS 本机数据转储
和恢复工具,而不是通过
像这样的框架。
以 CSV 格式导出数据库并以 mysql.
格式导入
我为我的一位客户开发了一个应用程序。他已经有一个了。所以我需要将他的实际数据库(SQL 服务器)转换为新数据库(MySQL)。
一些 table 的 SQL 服务器有超过 10.000.000 条记录。当我开始开发这个转换器时,我从一些带有几条记录的 table 开始,所以我找到所有记录并保存到我的新 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 table 有超过 10.000.000 条记录。我不能只找到所有内容并对其进行迭代。我会离开RAM。所以我做了这样的事情。
存储库class:
<?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年开发的,数据库连规范化都没有
如果源数据库 (MSSQL) 和目标数据库 (MySQL) 中的模式完全相同或相似,我会从一个数据库导出记录,然后使用纯数据库将它们导入另一个数据库工具。示例:
- 在 MSSQL 中,对于每个 table、export the records to CSV
- 在 MySQL 中,对于每个 table、import the records from CSV
您可以使用 shell 脚本将所有这些粘合在一起并自动执行该过程。
这个 export/import 会相当快,因为它发生在数据库层。这也是您可能获得的最快速度。
根据定义,从模型层移动整个数据库会变慢:您将为每一行创建一个模型对象。也就是说,当源模式和目标模式不同时,使用模型层是一种很好的方法,因为这样您就可以使用编程模型使一个模式适应另一个模式。
在您的具体示例中,如果您在 while
循环的底部 unset($object)
,您可能会看到性能有所提高,但我怀疑内存是瓶颈。 (I/O 是。)
我以前尝试过这种方法,根据我的经验,使用 DBMS 本机数据转储和恢复工具总是比通过这样的框架处理记录更快。
我建议使用 bcp
(https://msdn.microsoft.com/en-us/library/aa337544.aspx) to dump the data out of SQL Server and then use MySQL's LOAD DATA
(http://dev.mysql.com/doc/refman/5.7/en/load-data.html) 或 mysqlimport
等实用程序将数据导入 MySQL。
如果您需要在将数据加载到 MySQL 之前重新构造数据,您可以通过在 MySQL 中设置新的数据结构,然后使用一个可以像 sed
.
我目前正在构建一个具有类似问题的数据仓库系统。 Doctrine's own documentation 正确表述:
An ORM tool is not primarily well-suited for mass inserts, updates or deletions. Every RDBMS has its own, most effective way of dealing with such operations and if the options outlined below are not sufficient for your purposes we recommend you use the tools for your particular RDBMS for these bulk operations.
我会这样处理:
- 使用 Doctrine 的工具创建空的 MySQL 数据库。
- 列出 MySQL 数据库中的所有索引和主键并删除它们。我会编写脚本。这将消除在数据迁移完成之前不断更新索引的开销。
- 写一个脚本来复制数据。以几千个为单位循环遍历 SQL 服务器数据并插入 MySQL。
- 使用 PDO 或本机库。没有 Doctrine 或查询构建器。手写查询。
- 打开一个到 SQL 服务器的连接和一个到 MySQL 的连接。在脚本运行期间保持它们打开。
- 使用
LIMIT
和主键 > 最后 id 批量查询。使用OFFSET
查询通常较慢。 - 在循环外准备语句以优化查询处理。
- 将每批插入包装在一个事务中以减少事务开销。
- "Manually" 如有必要,请检查参照完整性。您的表还没有主键。
- 如果您有很多表,请将您的代码分割成对象或函数,这样可以从内存中清除局部变量,这样调试起来会更容易。
- 您可能需要定期致电
gc_collect_cycles()
。如果您的代码被分解成对象,这是一种控制内存的简单方法。
- 重新创建数据库索引和主键。如果这些是从一开始就编写好的,那么奖励积分。注意是否有任何由于重复数据错误而无法创建的主键。
- 在打开新的 MySQL 数据库用于生产之前进行测试和测试。您不想稍后编写另一个脚本来修复数据迁移。
更喜欢使用 DBMS 本机数据转储 和恢复工具,而不是通过 像这样的框架。 以 CSV 格式导出数据库并以 mysql.
格式导入