使用 Git 跟踪 mysql 架构 - 一些问题
Posted
技术标签:
【中文标题】使用 Git 跟踪 mysql 架构 - 一些问题【英文标题】:Using Git to track mysql schema - some questions 【发布时间】:2011-07-27 23:06:28 【问题描述】:如果这是推荐的?
能否请教一些 git 命令示例,了解如何跟踪 mysql 架构的版本?
除了我们通常在应用程序根目录上使用的存储库之外,我们还应该使用另一个存储库吗?
我应该使用一种叫做 hook 的东西吗?
更新:
1) 我们导航到 .git 数据库所在的项目根目录。
2)我们创建一个名为 hooks 的子文件夹。
3) 我们将这样的内容放入一个名为 db-commit 的文件中:
#!/bin/sh
mysqldump -u DBUSER -pDBPASSWORD DATABASE --no-data=true> SQLVersionControl/vc.sql
git add SQLVersionControl/vc.sql
exit 0
现在我们可以:
4) git commit -m
此提交将包括在提交之前运行的 mysql 模式转储。
以上内容的来源在这里: http://edmondscommerce.github.io/git/using-git-to-track-db-schema-changes-with-git-hook.html
如果这是一种可以接受的方式,我能否请有耐心的人逐行并尽可能详细地发表评论,这里发生了什么:
#!/bin/sh
mysqldump -u DBUSER -pDBPASSWORD DATABASE --no-data=true> SQLVersionControl/vc.sql
git add SQLVersionControl/vc.sql
exit 0
非常感谢。
【问题讨论】:
【参考方案1】:我发现以下选项对于版本控制/兼容 git 的 mysqldump 是强制性的。
mysqldump --skip-opt --skip-comments |sed -e 's/DEFINER[ ]*=[ ]*[^*]*\*/\*/'
(也许--no-data
)
--skip-opt
非常有用,它带走了所有的--add-drop-table --add-locks --create-options --disable-keys --extended-insert --lock-tables --quick --set-charset
。当数据库包含触发器时,DEFINER
sed 是必需的。
【讨论】:
【参考方案2】:(无耻塞)
dbvc commandline tool 允许您管理存储库中的数据库架构更新。
它在数据库中创建并使用一个表_dbvc
,其中包含一个正在运行的更新列表。您可以轻松运行尚未应用于数据库架构的更新。
该工具使用 git 来确定执行更新的正确顺序。
DBVC 使用情况
显示命令列表
dbvc help
显示特定命令的帮助
dbvc help init
为现有数据库初始化 DBVC。
dbvc init
创建数据库转储。这用于在新环境中创建数据库。
mysqldump foobar > dev/schema.php
使用架构创建数据库。
dbvc create
添加更新文件。这些用于更新其他环境中的数据库。
echo 'ALTER TABLE `foo` ADD COLUMN `status` BOOL DEFAULT 1;' > dev/updates/add-status-to-foo.sql
将更新标记为已运行。
dbvc mark add-status-to-foo
显示需要运行的更新列表。
dbvc status
显示所有更新及其状态。
dbvc status --all
更新数据库。
dbvc update
【讨论】:
【参考方案3】:尽管听起来很棒(我也确实想到了这个想法),但当我尝试实现它时,却碰壁了。理论上,通过使用 --skip-extended-insert 标志,尽管初始转储会很大,但每日转储之间的差异应该是最小的,因此可以假设存储库随时间的大小增加也是最小的,对吧?错了!
Git 存储的是 shapshots,而不是 diff,这意味着在每次提交时,它将获取整个转储文件,而不仅仅是 diff。此外,由于带有--skip-extended-instert 的转储将使用每个插入行上的所有字段名称,因此与没有--skip-extended-instert 的转储相比,它将是巨大的。这会导致规模爆炸式增长,这与人们所期望的完全相反。
在我的例子中,大约 300MB 的 sql 转储,存储库在几天内就达到了千兆字节。那么,我做了什么?我首先尝试了同样的事情,只删除了--skip-extended-instert,这样转储会更小,快照也会相应地更小。这种方法维持了一段时间,但随着时间的推移,它也变得无法使用。
不过,使用 --skip-extended-insert 的差异用法实际上仍然是一个好主意,只是,现在我尝试使用 subversion 而不是 git。我知道,与 git 相比,svn 是古老的历史,但它似乎工作得更好,因为它确实使用差异而不是快照。
简而言之,我认为最好的解决方案是执行上述操作,但使用颠覆而不是 git。
【讨论】:
已经实施和使用这种方法一段时间了,我必须说它肯定要好得多。与 git 存储库每天增加 300MB 不同,我现在增加了 7MB,因此“svn update”的工作速度要快得多。我想“手动差异,并且只在 git 存储库中保留差异”方法可以这样工作,但它围绕 scm 的 diff 功能工作,因此,将大部分 scm 带出循环,从而违背了使用的目的首先是一个 scm。 大多数时候您只想转储结构而不是实际数据。 您认为 git 存储 blob 而不是差异的假设在某种程度上是正确的。它的模型基于此,但实际上它也存储差异,只是默认情况下和从一开始就不是。更多阅读在这里git-scm.com/book/en/v2/Git-Internals-Packfiles【参考方案4】:这里描述了 IMO 的最佳方法:http://viget.com/extend/backup-your-database-in-git。为了您的方便,我在这里重复最重要的部分。
诀窍是使用mysqldump --skip-extended-insert
,它创建的转储可以更好地被 git 跟踪/区分。
还有一些关于最佳存储库配置的提示,以减少磁盘大小。复制自here:
core.compression = 9 :gzip 的标志,用于指定 blob 和包的压缩级别。级别 1 速度快,文件较大,级别 9 需要更多时间,但压缩效果更好。 repack.usedeltabaseoffset = true :出于兼容性原因,默认为 false,但 Git >=1.4.4 支持。 pack.windowMemory = 100m :(重新)打包对象可能会消耗大量内存。为了防止您的所有资源耗尽,对其进行一些限制是有用的。还有 pack.deltaCacheSize。 pack.window = 15 :默认为 10。值越大,Git 越努力寻找相似的 blob。 gc.auto = 1000 :默认为 6700。如文章中所述,建议每隔一段时间运行一次 git gc。我个人每天运行 git gc --auto ,所以只有在有足够垃圾时才打包东西。 git gc --auto 通常只在周围有 6700 个松散物体时才会触发打包机制。此标志会降低此金额。 gc.autopacklimit = 10:默认为 50。每次运行 git gc 时,都会生成一个松散对象的新包。随着时间的推移,你会得到太多浪费空间的包。偶尔将所有包组合成一个包是一个好主意,这样所有对象都可以组合和分解。默认情况下,当有 50 个包时, git gc 会执行此操作。但对于这种情况,较低的数字可能会更好。可以通过以下方式修剪旧版本:
git rebase --onto master~8 master~7
(复制自here)
【讨论】:
【参考方案5】:以下包括一个 git pre-commit 钩子来捕获 mysql 数据库/模式,给定 user='myuser'、password='mypassword'、database_name='dbase1'。正确地将错误冒泡到 git 系统(其他答案中的exit 0
可能很危险,并且可能无法正确处理错误场景)。 (可选)可以将数据库导入添加到结帐后挂钩(在捕获所有数据时,而不仅仅是模式),但请注意您的数据库大小。详细信息在下面的 bash-script cmets 中。
预提交钩子:
#!/bin/bash
# exit upon error
set -e
# another way to set "exit upon error", for readability
set -o errexit
mysqldump -umyuser -pmypassword dbase1 --no-data=true > dbase1.sql
# Uncomment following line to dump all data with schema,
# useful when used in tandem for the post-checkout hook below.
# WARNING: can greatly expand your git repo when employing for
# large databases, so carefully evaluate before employing this method.
# mysqldump -umyuser -pmypassword dbase1 > dbase1.sql
git add dbase1.sql
(可选)结帐后挂钩:
#!/bin/bash
# mysqldump (above) is presumably run without '--no-data=true' parameter.
set -e
mysql -umyuser -pmypassword dbase1 < dbase1.sql
应用程序的版本,我正在运行的操作系统:
root@node1 Dec 12 22:35:14 /var/www# mysql --version
mysql Ver 14.14 Distrib 5.1.54, for debian-linux-gnu (x86_64) using readline 6.2
root@node1 Dec 12 22:35:19 /var/www# git --version
git version 1.7.4.1
root@node1 Dec 12 22:35:22 /var/www# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 11.04
Release: 11.04
Codename: natty
root@node1 Dec 12 22:35:28 /var/www#
【讨论】:
【参考方案6】:假设您已经有一个 git repo,请在 shell 脚本或其他任何内容中执行以下操作:
#!/bin/bash -e
# -e means exit if any command fails
DBHOST=dbhost.yourdomain.com
DBUSER=dbuser
DBPASS=dbpass # do this in a more secure fashion
DBNAME=dbname
GITREPO=/path/to/git/repo
cd $GITREPO
mysqldump -h $DBHOST -u $DBUSER -p$DBPASS -d $DBNAME > $GITREPO/schema.sql # the -d flag means "no data"
git add schema.sql
git commit -m "$DBNAME schema version $(`date`)"
git push # assuming you have a remote to push to
然后每天从 cron 作业或你有什么启动这个脚本。
编辑:通过在 $gitdir/hooks/pre-commit 中放置一个脚本(名称很重要),该脚本将在每次提交之前执行。这样,每次提交都会捕获数据库模式的状态,这是有道理的。如果你每次提交都自动运行这个sql脚本,你会炸掉你的数据库,这是没有意义的。
#!/bin/sh
这一行指定它是一个 shell 脚本。
mysqldump -u DBUSER -pDBPASSWORD DATABASE --no-data=true> SQLVersionControl/vc.sql
这与我上面的回答相同;仅从数据库中获取 DDL 并将其存储在文件中。
git add SQLVersionControl/vc.sql
这会将 SQL 文件添加到对您的存储库所做的每个提交中。
exit 0
这将成功退出脚本。这可能很危险。如果mysqldump
或git add
失败,你可能会吹走你想保留的东西。
【讨论】:
这基本上是 Chris 对实际 mysqldump 命令的回答,所以如果你接受这个,也给他投上一票。 @mkb - 我正在使用 Capistrano 进行部署。它有什么作用?它部署了我上次提交的更改。所以,我只需要将模式添加到存储库。根据这样做,并查看我的更新问题,也许您的答案可能会有所改变。钩子是shell脚本吗?非常感谢,我真的很抱歉所有这些问题。 :s @mkb:非常感谢。最后,你告诉我,我要感谢你,我打算使用的脚本可能很危险。有没有办法像在示例中那样附加 -e 标志,也许这会有所帮助?我发现使用钩子是一种很好的方法,因为这一切都在我提交之前自动完成(再次感谢),所以它非常适合工作流程。或者,我应该采用您的第一个代码,并以某种方式使其成为 Capistrano 任务吗? (无论如何,我真的不知道最后的假设,但至少我会知道继续寻找。非常感谢你的耐心。 如果您将第一行更改为#/bin/bash -e
可能。我也从未使用过 Capistrano。
@qsoft ***.com/questions/15656463/…【参考方案7】:
虽然我没有使用 Git,但我已经使用源代码控制超过 15 年了。在决定将源代码控制中的 src 和随附资源存储在何处以及如何存储时要遵循的最佳实践:如果在项目中使用数据库模式,那么您应该对“那个”项目中的模式和所有其他项目资源进行版本控制。如果您开发了一组在其他项目中重用的模式或编程资源,那么您应该为这些可重用资源建立一个单独的存储库。该单独的可重用资源项目将自行进行版本控制,并将跟踪该存储库中实际可重用资源的版本。
如果您在不同的项目中使用可重用存储库之外的版本化资源,那么您会遇到以下情况(只是一个示例)。项目 XYZ 版本 1.0 现在使用 DB Schema_ABC 版本 4.0 在这种情况下,您将了解您使用了可重用资源的特定版本,并且由于它是版本化的,您将能够在整个项目中跟踪它的使用。如果您收到有关 DBSchema_ABC 的错误报告,您将能够修复架构和重新版本,并了解 DBSchem_ABC 的其他用途以及您可能需要在哪里进行一些更改。从那里您还将了解哪些项目包含哪些版本的可重用资源......您只需要了解如何跟踪您的资源。
采用这种类型的开发环境和资源管理策略是发布可用软件和管理中断/修复增强环境的关键。即使您在自己的时间为自己的教育进行开发,您也应该使用源代码控制......就像你一样......
至于 Git,如果可以的话,我会找到一个 gui 前端或一个开发环境集成。 Git 相当大,所以我相信它有很多前端支持,也许吧?
【讨论】:
当我对版本控制系统更加熟悉时,我必须这样做。我使用它的时间不超过 1 天(尽管已经阅读了将近一个月)。 :) 我已经完成了 git commit, git add 。和 git 推送。再没有这个了。 :) 我需要时间。但是感谢您的回复,它肯定会帮助我一路走来。然而,现在,我只需要在命令行上放置一些命令,并让一些体面的事情发生,比使用 phpmyadmin 并将版本下载到桌面更好。 :) 我了解,但不完全了解您发出的命令很可能会导致存储库交叉。搞砸到不归路是相当容易的。【参考方案8】:如果您只是跟踪架构,请将所有 CREATE 语句放入一个 .sql 文件中,然后将该文件添加到 git。
$> mkdir myschema && cd myschema
$> git init
$> echo "CREATE TABLE ..." > schema.sql
$> git add schema.sql
$> git commit -m "Initial import"
【讨论】:
我明白了……诀窍是将所有内容放在添加到存储库中的文件上,每次更改时,git 都会知道。但我不明白“将创建语句放入一个 .sql 文件”是什么意思。小心解释一下这是什么意思。 - 假设我在本地更改架构,如何反映我的数据库更改? - 是的,我有点迷路了……我也喜欢这首歌的数据,但从我读到的内容来看,它有一些空间问题。 git 不保存增量。它存储每个修订的完整副本,因此在 git 中存储数据库的数据将会变得非常快速。至于存储模式,您可以使用带有参数的 mysqldump 仅转储模式,并将结果放入 git 存储库,然后自动提交。 @mkb:您能否用一些命令行示例描述(在答案中)该工作流程?我是新手……以上是关于使用 Git 跟踪 mysql 架构 - 一些问题的主要内容,如果未能解决你的问题,请参考以下文章