将数据库内容从一个非常差的结构迁移到一个非常合理的结构的最佳实践?

Joh*_*ith 17 mysql database migration database-design relational-database

TL; DR什么是在一个结构非常差的数据库(具有多次重复列,没有相互关系和重复数据)之间迁移大量数据到另一个高度组织和关系结构的最佳方法? - 对不起,长期阅读!

我最近接受了一项非常复杂的工作.它改写了整个公司基于网络的IT平台.我担心我不能提供太多细节,因为我们不能让老开发者知道(他有一个反对公司负责人的隐喻枪,因为他是唯一一个知道如何做发票生成等关键事情的人,并要求越来越多的钱).

主要问题是整个网络平台(由所有员工和所有客户使用)由一个技能低于业余的人编码.它由大约300个单独的代码文件组成.没有模板库 - 它全部硬编码到每个文件中.没有逻辑数据库结构 - 它实际上是随着他的进展而组成的.没有安全感 - 令人震惊.无论如何,我们将在约3个月的时间内重写整个平台.

然而老板说,早上它上线,任何地方都不会丢失任何客户数据.必须直接复制整个数据库内容.数据库的结构目前很差,几乎不可能使用,但本周我们将(试图!)编写一些脚本,将其迁移到我们新的,高度关系的结构,这更符合逻辑.问题是,最好的方法是什么?

一个例子是地址.在旧数据库中,地址用于大约12个表中(总共4​​4个表...).在我们的网站中,我们有一个addresses表将被其他表(例如address_id)交叉引用以保持清洁.主要的问题是,在大约一半的表,地址存储为line1,line2,town,city,等,这是很好的,但在另一半,他只是有一个单一的address存储整件事场!

第二个例子是日期-在某些表他秒纪元以来的日期,在别人MySQL的NOW()日期,而在其他他硬是将其存储在每行6列- ,year,month,day,,hour -哎哟...minutesecond

  • 试图解决这个问题的好方法是什么?我们应该看看我们的表,在哪里工作,我们需要拉从数据到我们,或者我们应该扭转这种看看他的表和工作了,他的数据需要进入我们的?

  • 从编程的角度来看,我们应该如何解决这个问题?许多数据需要动态格式化(例如日期),因此我们考虑一次一行地采集数据,正确格式化,然后将其重新插入脚本中的正确位置.

  • 查询的速度和效率对我们来说不是问题,因为我们只需要在本地计算机上运行一次(在测试之后).当SQL转储时,他的数据库目前大约是800MB,但是这很多都是他无用的测试数据,或者说完全没必要.

关于解决这个问题的最佳方法的任何想法?作为参考,我们的系统将用PHP重写,因此任何基于PHP的建议都会很好.该数据库目前(现在仍将是)在MySQL中.

Wil*_*ung 10

这里没有解决方案.没有魔法.只是简单的努力.

你有了新的模型,完成这项工作的唯一方法就是转到每个表格,然后将它们单独地,逻辑地,纸上,白板上等转换成新模型.

您将需要处理的不仅仅是简单的格式问题.您还将处理数据重复问题.如果你有12个地址表,但只有1个客户端,哪个地址获胜?

单独该决定可以简化许多处理(例如,您可以忽略除了从主客户端记录链接的一个有福地址之外的其他地址).

这会带给你最后的问题.转换期间"不丢失任何数据".

根据"不丢失任何数据"的含义,这很可能是从第一天开始的非首发.例如,如果您丢弃地址,那么就会丢失数据.当然,每个组件"都有一个地址",但不一定是他们之前的那个.在他们可能完全相同之前,他们可能也没有.这将是非常混乱的.

完成映射和其他过程后,在大多数语言中编写它们都很简单.脚本语言适用于此.您可以"按原样"将每个表批量加载到新数据库中,并编写存储过程以进行转换.无论你熟悉什么.您的转换可能会有几个步骤,并且大部分代码可能只是为了促进转换而"一次性".

这将是乏味的.这些东西总是如此.有太多的细节.这是一个可怕的系统的所有原因是转换将是可怕的原因.如果你没有足够的时间把它拉下来,也不要感到惊讶.

最后,如果您有大量数据,如果您无法在业务停机期间(周末,过夜等)执行切换,您可能会遇到一些时间限制.如果您在运行时使用更新数据,这将是另一个鱼.如果可能的话,我不能强烈建议不要那样做.


Jak*_*cil 6

我最近做了几次大规模的迁移,并在此期间逐渐为自己开发了一些实用的最佳实践.它没有什么真正开创性的,但你可能会发现其中一些有用:

一般提示

  • 在开始之前,请确保您了解现有数据模型以及新版本系统的要求.
  • 尽可能地设计新的数据库模式,并尝试不要因为您需要迁移旧内容而给自己带来压力.
  • 使用具有实体ORM的框架.开发新版本不仅容易,而且迁移也会更容易.

移民

处理数据迁移的代码将在一段时间内成为项目的一部分,因此将它专用于包/文件夹(即legacy)是一个好主意.在此程序包中,保留转换脚本和与旧系统相关的其他文件.经过一段时间,你将能够通过简单的方法摆脱它rm -rf legacy.

脚本应该以小步骤进行转换.最好是多次遍历一个表并保持步骤小,简单和可调试,而不是让一个大脚本尽可能快地执行所有操作.

在自己的事务中运行每个步骤并在成功完成后提交它也是一个好主意,这样当一个步骤失败时,您不需要再次重新运行整个迁移.

整个迁移过程以及特定步骤或步骤组应该可以使用命令行中的一个命令运行,因为您将多次运行它直到达到最终版本,因此您的自动化程度越高越好.

主脚本(即legacy/bin/full-migration)应该执行整个过程(即获取遗留生产数据库的新副本,(重新)在其中创建新数据库和表,运行整个迁移)并且它应该与完全相同的过程在生产服务器中部署新版本后,最终会运行(仅使用不同的配置).它允许您在开发环境中彻底测试它.

因为转换可能需要很长时间,所以记录每个操作都是有益的(普通print action + object_id应该这样做).通常会有一些行有一些意外的差异,这些行会导致脚本崩溃或导致引用完整性错误.在这样的情况下,很好地查看它是哪个对象,以便您可以立即转到数据库,检查数据,相应地更新脚本并再次运行失败的步骤.

事实证明,对我来说非常有用的一件事是使用ORM为遗留数据库表定义模型类.我已经在Django中做了几次这样的事情,它支持多个数据库连接和每个模型的路由,所以我能够编写看起来大致相似的脚本(Python):

from legacy import models as old
from catalog import models as new

# Loop through all products from the legacy DB
for old_product in old.Product.objects.all():  
    # Create an instance of the new product model class
    new_product = new.Product() 
    # Copy and modify attributes as needed
    new_product.name = old_product.product_name.strip()
    # ...
    # Save it to the new database
    new_product.save()
Run Code Online (Sandbox Code Playgroud)

此外,新模式的限制越多越好(即可能的NOT NULL,外键检查等),因为它可以帮助您查看关于旧模式的假设在哪里是错误的,并且还可以防止不正确的数据进入你的新系统(InnoDB作为MySQL的后端是一个好主意).

其他好的做法是尽可能保留新数据库中的旧主键.如果您在迁移后在新数据中看到一些奇怪的内容,则可以返回并按旧系统中的ID查找该项目.

  • +1用于保留主键.这是一个好主意,因为对于这样的项目,您将不得不错过在官方迁移完成后会出现问题的内容,此时您将乞求数据迁移的完整审计跟踪. (2认同)

Bor*_*att 4

重写的第一步是充分理解当前的数据结构和在其上运行的代码。可能有一些数据看起来是多余的,但代码出于某种奇怪的原因要求它如此。是设计不好吗?可能 - 但请确保您完全理解写入或访问数据的每一位代码,以便您可以确定哪些内容可以删除,哪些内容必须重构,以及哪些内容必须保持原样。

工具可以帮助自动化流程 - 但如果没有深入掌握当前系统,它们可能会让您陷入自动化困境。

我将设计新的数据结构,编写脚本将旧结构转移到新结构,然后测试功能。如果出现问题,请更改新结构和/或导入脚本,然后再次运行数据传输例程并重复整个过程,直到确保没有数据或功能丢失。此时,安排一个日期关闭旧系统,进行数据迁移,然后启动新系统。

当然,这一切都缺少对用户进行新/改进系统的培训。这一点至关重要!不要将其排除在您的计划之外,否则最好的新的、闪亮的改进系统将因用户的不满而沉没。