MySQL 模式源代码控制

发布于 2024-11-11 16:22:25 字数 342 浏览 2 评论 0原文

在我的公司,我们有几位开发人员都在内部从事项目,每个人都有自己的虚拟机设置。我们使用 SVN 来处理源代码,但偶尔会遇到需要更改数据库 (MySQL) 架构的问题,并且必须将其传播给所有其他开发人员。目前,我们有一个手动编写的日志文件,其中列出了您更改的内容以及执行更改所需的 SQL。

我希望可能有一个更好的解决方案 - 理想情况下是链接到 SVN 的解决方案,例如,如果您更新到修订版 893,系统知道这需要数据库修订版 183 并自动更新您的本地架构。我们不关心同步的数据,只关心模式。

当然,一种解决方案是让所有开发人员都运行一个单一的中央数据库;然而,这有一个缺点,即模式更改可能会破坏其他人的构建,直到他们执行 svn up。

At my company we have several developers all working on projects internally, each with their own virtualbox setup. We use SVN to handle the source, but occasionally run into issues where a database (MySQL) schema change is necessary, and this has to be propagated to all of the other developers. At the moment we have a manually-written log file which lists what you changed, and the SQL needed to perform the change.

I'm hoping there might be a better solution -- ideally one linked to SVN, e.g. if you update to revision 893 the system knows this requires database revision 183 and updates your local schema automagically. We're not concerned with the data being synched, just the schema.

Of course one solution would be to have all developers running off a single, central database; this however has the disadvantage that a schema change could break everyone else's build until they do an svn up.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

落在眉间の轻吻 2024-11-18 16:22:25

一种选择是 YAML/JSON 格式的数据字典。 这里有一篇不错的文章

One option is a data dictionary in YAML/JSON. There is a nice article here

请帮我爱他 2024-11-18 16:22:25

我会考虑看看MyBatis 架构迁移工具之类的东西。它并不完全是你所描述的,但我认为它以一种优雅的方式解决了你的问题,并且可以在不引入核心 MyBatis 的情况下使用。

在滚动您自己的方面,我一直做的是拥有一个基本架构文件,该文件将从头开始创建架构,以及一个增量文件,该增量文件将所有架构更改附加为增量,并以版本号分隔(您可以尝试并使用 SVN 编号,但我总是发现手动递增更容易)。然后有一个 schema_version 表,其中包含实时数据库的信息,规范模式文件将在其中包含该信息,并具有一个脚本,该脚本将运行增量脚本中现有数据库版本之后的所有更改。

所以你会有一个像这样的模式:

-- Version: 1
CREATE TABLE user (
id bigint,
name varchar(20))

你有一个工具管理模式版本表并看到类似的内容:

> SELECT * FROM schema_version;
1,2011-05-05

然后你有几个人添加到模式中并有一个如下所示的增量文件:

-- Version: 2
ALTER TABLE user ADD email varchar(20);
-- Version: 3
ALTER TABLE user ADD phone varchar(20);

并且签入了相应的新模式with:

-- Version: 3
CREATE TABLE user (
id bigint,
name varchar(20),
email charchar(20),
phone varchar(20))

当您针对具有初始架构(版本 1)的数据库运行增量脚本时,它将从 schema_version 表中读取值,并将所有大于该值的增量应用到您的架构。当您开始处理分支时,这会变得更加棘手,但可以作为一个简单的起点。

I'd consider looking at something like MyBatis Schema Migration tools. It isn't exactly what you describe, but I think it solves your problem in an elegant way and can be used without pulling in core MyBatis.

In terms of rolling your own, what I've always done is to have a base schema file that will create the schema from scratch as well as a delta file that appends all schema changes as deltas, separated by version numbers (you can try and use SVN numbers, but I always find it easier just to manually increment). Then have a schema_version table, which contains that information in it for the live database, the canonical schema file will have that information in it and have a script that will run all changes subsequent to the existing DB version from the delta script.

So you'd have a schema like:

-- Version: 1
CREATE TABLE user (
id bigint,
name varchar(20))

You have the tool manage the schema version table and see something like:

> SELECT * FROM schema_version;
1,2011-05-05

Then you have a few people add to the schema and have a delta file that would look like:

-- Version: 2
ALTER TABLE user ADD email varchar(20);
-- Version: 3
ALTER TABLE user ADD phone varchar(20);

And a corresponding new schema checked in with:

-- Version: 3
CREATE TABLE user (
id bigint,
name varchar(20),
email charchar(20),
phone varchar(20))

When you run the delta script against a database with the initial schema (Version 1), it will read the value from the schema_version table and apply all deltas greater than that to your schema. This gets trickier when you start dealing with branches, but serves as a simple starting point.

木落 2024-11-18 16:22:25

我以前或现在使用过以下几种方法:

顺序版本号

大多数使用此方法的人都有一个单独的程序,该程序从数据库中获取版本号,然后执行与高于该版本号的数据库版本关联的任何语句,最后更新数据库中的版本号。

因此,如果版本是 37 并且升级应用程序中存在与版本 1 到 38 相关的语句,它将跳过 1 到 37 并执行语句以将数据库带到版本 38。

我见过还允许降级语句的实现每个版本都可以撤消升级所做的操作,这允许将数据库从版本 38 降级回版本 37。

在我的情况下,我们在应用程序本身中升级了此数据库,并且没有降级。因此,更改是受源代码控制的,因为它们是应用程序的一部分。

有向无环图

在最近的一个项目中,我想出了一种不同的方法。我使用作为有向无环图节点的类来封装语句,以便针对每个特定功能/错误修复/等对数据库进行特定升级。每个节点都有一个属性来声明其唯一名称以及它所依赖的任何节点的名称。这些属性还用于在程序集中搜索所有升级节点。

给定一个默认的根节点作为任何没有依赖关系的节点的依赖节点,并且该节点包含创建 migrationregister 表的语句,该表列出了已应用的节点的名称。将所有节点排序到顺序列表后,依次执行它们,跳过已经应用的节点。

这全部包含在与主应用程序不同的应用程序中,并且它们在同一存储库中进行源代码控制,以便当开发人员完成某个功能和与其关联的数据库更改时,它们会在同一变更集中一起提交。如果您拉取功能的更改,您也会拉取数据库更改。此外,主应用程序只需要一个预期节点名称的列表。任何多余或缺失,它都知道与数据库不匹配。

我选择这种方法是因为该项目通常由多个开发人员并行开发,每个开发人员有时有多个开发项目(分支开发,有时非常分支)。处理数据库版本号非常痛苦。如果每个人都从版本 37 开始,“Alice”开始使用版本 38,因此它将更改她的数据库,而“Bob”也开始必须更改数据库的工作并且也使用版本 38,那么最终需要有人进行更改。假设 Bob 完成并推送到服务器。现在,当 Alice 提取 Bob 的变更集时,她必须将语句的版本更改为 39,并将数据库版本设置回 37,以便 Bob 的更改能够得到执行,但随后她的更改会再次执行。

但是,当 Alice 拉取 Bob 的变更集时发生的只是一个新的迁移节点和节点名称列表中的另一行需要检查时,一切就正常了。

我们使用 Mercurial(分布式)而不是 SVN(客户端-服务器),因此这就是这种方法对我们如此有效的部分原因。

There are a couple approaches I've used before or currently use:

Sequential Version Number

Most that use this approach have a separate program that grabs a version number from the database, and then executes any statements associated with database versions higher than that number, finally updating the version number in the database.

So if the version is 37 and there are statements associated with version 1 through 38 in the upgrading application, it will skip 1 through 37 and execute statements to bring the database to version 38.

I've seen implementations that also allow for downgrade statements for each version to undo what the upgrade did, and this allows for taking a database from version 38 back down to version 37.

In my situation we had this database upgrading in the application itself and did not have downgrades. Therefore, changes were source-controlled because they were part of the application.

Directed Acyclic Graph

In a more recent project I came up with a different approach. I use classes that are nodes of a directed acyclic graph to encapsulate the statements to do specific upgrades to the database for each specific feature/bugfix/etc. Each node has an attribute to declare its unique name and the names of any nodes on which it was dependent. These attributes are also used to search the assembly for all upgrade nodes.

A default root node is given as the dependency node for any nodes without dependencies, and this node contains the statements to create the migrationregister table that lists the names of nodes that have already been applied. After sorting all the nodes into a sequential list, they are executed in turn, skipping the ones that are already applied.

This is all contained in a separate application from the main application, and they are source-controlled in the same repository so that when a developer finishes work on a feature and the database changes associated with it, they are committed together in the same changeset. If you pull the changes for the feature, you also pull the database changes. Also, the main application simply needs a list of the expected node names. Any extra or missing, and it knows the database does not match.

I chose this approach because the project often has parallel development by multiple developers, with each developer sometimes having more than 1 thing in development (branchy development, sometimes very branch). Juggling database version numbers was quite the pain. If everybody started with version 37 and "Alice" starts on something and uses version 38 so it will change her database, and "Bob" also starts on work that has to change the database and also uses version 38, someone will need to change eventually. So let's say Bob finishes and pushes to the server. Now Alice, when she pulls Bob's changeset, has to change the version for statements to 39 and set her database version back to 37 so that Bob's changes will get executed, but then hers execute again.

But when all that happens when Alice pulls Bob's changeset is that there's simply a new migration node and another line in the list of node names to check against, things just work.

We use Mercurial (distributed) rather than SVN (client-server), so that's part of why this approach works so well for us.

瞄了个咪的 2024-11-18 16:22:25

一个简单的解决方案是在 SVN(或任何库)中保留完整的模式。也就是说,每次更改模式时,运行MySQL“desc”以转储所有表的描述,用此覆盖最后一个此类模式转储,然后提交。然后,如果您运行版本差异,它应该告诉您发生了什么变化。当然,您需要按字母顺序(或某种可预测的顺序)保留所有表。

对于不同的方法:几年前,我从事一个桌面应用程序项目,我们定期发送可能有架构更改的新版本,并且我们希望在没有用户干预的情况下处理这些版本。因此,该程序描述了它所期望的模式。在启动时,它执行了一些元数据调用来检查其实际拥有的数据库架构,并将其与预期进行比较。然后自动更新架构以匹配其预期。通常,当我们添加新列时,我们可以简单地让它以空或空白开始,因此一旦我们让第一个版本工作,这几乎需要零编码工作。当需要进行一些实际操作来填充新字段时,我们必须编写自定义代码,但这种情况相对较少。

An easy solution would be to keep a complete schema in SVN (or whatever library). That is, every time you change the schema, run MySQL "desc" to dump out descriptions of all the tables, overwrite the last such schema dump with this, and then commit. Then if you run a version diff, it should tell you what changed. You would, of course, need to keep all the tables in alphabetical order (or some predictable order).

For a different approach: Years ago I worked on a project for a desktop application where we were periodically sending out new versions that might have schema changes, and we wanted to handle these with no user intervention. So the program had a description of what schema it expected. At start up it did some metadata calls to check the schema of the database that it actually had and compared these to what it expected. If then automatically updated the schema to match what it expected. Usually when we added a new column we could simply let it start out null or blank, so this required pretty much zero coding effort once we got the first version to work. When there was some actual manipulation required to populate new fields, we'd have to write custom code, but that was relatively rare.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文