从 MySQL 迁移到 PostgreSQL

从 mysql 迁移到 postgresql

将数据库从 mysql 迁移到 postgres 是一个具有挑战性的过程。

虽然 mysql 和 postgres 执行类似的工作,但它们之间存在一些根本差异,这些差异可能会产生需要解决才能成功迁移的问题。

从哪儿开始?

pg loader 是一个可以用来将数据移动到 postgresql 的工具,但是,它并不完美,但在某些情况下可以很好地工作。值得一看,看看这是否是你想要走的方向。

另一种方法是创建自定义脚本。

自定义脚本提供了更大的灵活性和范围来解决特定于您的数据集的问题。

在本文中,构建了自定义脚本来处理迁移过程。

导出数据

数据如何导出对于迁移的顺利进行至关重要。在默认设置中使用 mysqldump 将导致更困难的过程。

使用 –兼容=ansi 选项以 postgresql 需要的格式导出数据。

为了使迁移更容易处理,请将架构和数据转储分开,以便可以单独处理它们。每个文件的处理要求都非常不同,为每个文件创建一个脚本将使其更易于管理。

架构差异

数据类型

mysql 和 postgresql 中可用的数据类型存在差异,这意味着在处理架构时,您需要确定哪些字段数据类型最适合您的数据。

类别 mysql postgresql

数字int、tinyint、smallint、mediumint、bigint、float、double、decimal整数、smallint、bigint、数字、实数、双精度、串行、小串行、大串行字符串char、varchar、tinytext、text、mediumtext、longtextchar、varchar、文本日期和时间日期、时间、日期时间、时间戳、年份日期、时间、时间戳、间隔、时间戳二进制二进制、varbinary、tinyblob、blob、mediumblob、longblob字节茶布尔值布尔值(tinyint(1))布尔值枚举和集合枚举,设置enum(没有等效的 set)jsonjsonjson、jsonb几何几何、点、线、多边形点、线、lseg、框、路径、多边形、圆网络地址没有内置类型cidr、inet、macaddruuid没有内置类型(可以使用char(36))uuid数组没有内置支持支持任何数据类型的数组xml没有内置类型xml范围类型没有内置支持int4range、int8range、numrange、tsrange、tstzrange、daterange复合类型没有内置支持用户定义的复合类型

tinyint 字段类型

tinyint 在 postgresql 中不存在。您可以选择使用smallint 或boolean 来替换它。选择与当前数据集最相似的数据类型。

 $line =~ s/tinyint(?:(d+))?/smallint/gi;

枚举字段类型

枚举字段稍微复杂一些,虽然 postgresql 中存在枚举,但它们需要创建自定义类型。

为了避免重复自定义类型,最好规划出需要哪些枚举类型,并创建架构所需的最少数量的自定义类型。自定义类型不是特定于表的,一种自定义类型可以在多个表上使用。

create type color_enum as enum ('blue', 'green');..."shirt_color" color_enum not null default 'blue',"pant_color" color_enum not null default 'green',...

类型的创建需要在导入 sql 之前完成。然后可以调整脚本以使用已创建的自定义类型。

如果有多个字段使用 enum(‘blue’,’green’),这些字段都应该使用相同的 enum 自定义类型。为每个单独的字段创建自定义类型并不是好的数据库设计。

if ( $line =~ /"([^"]+)"s+enum(([^)]+))/ ) {    my $column_name = $1;    my $enum_values = $2;    if ( $enum_values !~ /''/ ) {        $enum_values .= ",''";    }    my @items = $enum_values =~ /'([^']*)'/g;    my $sorted_enum_values = join( ',', sort @items );    my $enum_type_name;    if ( exists $enum_types{$sorted_enum_values} ) {        $enum_type_name = $enum_types{$sorted_enum_values};    }    else {        $enum_type_name = create_enum_type_name($sorted_enum_values);        $enum_types{$sorted_enum_values} = $enum_type_name;        # add create type statement to post-processing        push @enum_lines,        "create type $enum_type_name as enum ($enum_values);";    }    # replace the line with the new enum type    $line =~ s/enum([^)]+)/$enum_type_name/;}

索引

索引的创建方式存在差异。索引有两种变体:有字符限制的索引和无字符限制的索引。这两个都需要处理并从 sql 中删除,并放入一个单独的 sql 文件中,以便在导入完成后运行 (run_after.sql)。

if ($line =~ /^s*keys+/i) {    if ($line =~ /keys+"([^"]+)"s+("([^"]+)")/) {        my $index_name = $1;        my $column_name = $2;        push @post_process_lines, "create index idx_${current_table}_$index_name on "$current_table" ("$column_name");";    } elsif ($line =~ /keys+"([^"]+)"s+("([^"]+)"((d+)))/i) {        my $index_name = $1;        my $column_name = $2;        my $prefix_length = $3;        push @post_process_lines, "create index idx_${current_table}_$index_name on "$current_table" (left("$column_name", $prefix_length));";    }    next;}

全文索引在 postgresql 中的工作方式完全不同。要创建全文索引,索引必须将数据转换为向量。

然后可以对向量进行索引。索引向量时有两种索引类型可供选择。 gin 和 gist。两者都有优点和缺点。一般来说,gin 优于 gist。虽然 gin 构建索引的速度较慢,但​​查找速度更快。

if ( $line =~ /^s*fulltexts+keys+"([^"]+)"s+("([^"]+)")/i ) {    my $index_name  = $1;    my $column_name = $2;    push @post_process_lines,    "create index idx_fts_${current_table}_$index_name on "$current_table" using gin (to_tsvector('english', "$column_name"));";    next;}

自动递增

postgresql 不使用 autoincrment 关键字,而是使用 generated always as identity。

导入数据时使用 generated always as identity 有一个问题。 generated always as identity不是为导入id而设计的,当向表中插入行时,不能指定id字段。 id 值将自动生成。尝试将您自己的 id 插入该行将会产生错误。

要解决此问题,可以将 id 字段设置为 serial 类型,而不是 int generated always as identity。 serial 对于导入来说更加灵活,但不建议将该字段保留为 serial。

使用此方法的另一种方法是将 overriding system value 添加到插入查询中。

insert into table (id, name)overriding system valuevalues (100, 'a name');

如果您使用 serial,则需要将一些查询写入 run_after.sql,以将 serial 更改为 generated always as identity,并在创建 schema 并插入数据后重置内部计数器。

if ( $line =~ /^s*"(w+)"s+(int|bigint)s+nots+nulls+auto_increments*,/i ) {    my $column_name = $1;    $line =~ s/^s*"$column_name"s+(int|bigint)s+nots+nulls+auto_increments*,/"$column_name" serial,/;    push @post_process_lines, "alter table "$current_table" alter column "$column_name" drop default;";    push @post_process_lines, "drop sequence ${current_table}_${column_name}_seq;";    push @post_process_lines, "alter table "$current_table" alter column "$column_name" add generated always as identity;";    push @post_process_lines, "select setval('${current_table}_${column_name}_seq', (select coalesce(max("$column_name"), 1) from "$current_table"));";}

架构结果

从mysql导出后的原始模式

drop table if exists "address_book";/*!40101 set @saved_cs_client     = @@character_set_client */;/*!40101 set character_set_client = utf8 */;create table "address_book" (  "id" int not null auto_increment,  "user_id" varchar(50) not null,  "common_name" varchar(50) not null,  "display_name" varchar(50) not null,  primary key ("id"),  key "user_id" ("user_id"));

处理的主要 sql 文件

drop table if exists "address_book";create table "address_book" (  "id" serial,  "user_id" varchar(85) not null,  "common_name" varchar(85) not null,  "display_name" varchar(85) not null,  primary key ("id"));

运行后.sql

alter table "address_book" alter column "id" drop default;drop sequence address_book_id_seq;alter table "address_book" alter column "id" add generated always as identity;select setval('address_book_id_seq', (select coalesce(max("id"), 1) from "address_book"));create index idx_address_book_user_id on "address_book" ("user_id");

值得注意的是迁移中使用的索引命名约定。索引名称包括表名和字段名。 索引名称必须是唯一的,不仅在添加索引的表中,而且在整个数据库中,添加表名称和列名称可以减少脚本中出现重复的机会。

数据处理

迁移数据库的最大障碍是将数据转换为 postgresql 接受的格式。 postgresql 存储数据的方式存在一些差异,需要额外注意。

字符集

本文使用的数据集早于utf8mb4,并使用旧的默认latin1,该字符集与postgresql默认字符集utf8不兼容,需要注意的是,postgresql utf8也与mysql的utf8mb4不同。

从 latin1 迁移到 utf8 的问题是数据的存储方式。在 latin1 中每个字符都是一个字节,而在 utf8 中字符可以是多字节,最多 4 个字节。

咖啡馆这个词就是一个例子

在 latin1 中数据存储为 4 个字节,在 utf8 中存储为 5 个字节。在字符集迁移期间,会考虑字节值,并且可能会导致 utf8 中的数据被截断。 postgresql 将在此截断时出错。

为避免截断,请向受影响的 varchar 字段添加填充。

值得注意的是,如果您更改 mysql 中的字符集,也可能会发生同样的截断问题。

字符转义

在数据库中看到反斜杠转义单引号的情况并不少见。

但是,postgresql 默认不支持这一点。相反,使用使用双单引号的 ansi sql 标准方法。

如果 varchar 字段包含 it’s 则需要更改为 it’s

 $line =~ s/'/''/g;

表锁定

在 sql 转储中,每次插入之前都会有表锁定调用。

lock tables "address_book" write;

postgresql 中一般不需要手动锁定表。

postgresql 使用多版本并发控制(mvcc)来处理事务。当更新一行时,它会创建一个新版本。一旦旧版本不再使用,它​​将被删除。这意味着通常不需要表锁定。 postgresql 将与 mvcc 一起使用锁来提高并发性。手动设置锁会对并发性产生负面影响。

因此,从 sql 转储中删除手动锁并让 postgresql 根据需要处理锁是更好的选择。

导入数据

迁移过程的下一步是运行脚本生成的 sql 文件。如果前面的步骤正确完成,这部分应该是一个顺利的动作。实际发生的情况是,导入发现了前面步骤中未发现的问题,需要返回并调整脚本并重试。

要运行 sql 文件,请使用 psql 登录 postgres 数据库并运行导入功能

i /path/to/converted_schema.sql

需要注意的两个主要错误:

错误:对于类型字符变化来说值太长(50)

这可以通过增加前面提到的 varchar 字段字符长度来解决。

错误:无效命令 n

此错误可能是由杂散转义单引号或其他不兼容的数据值引起的。要修复这些问题,可能需要将正则表达式添加到数据处理脚本中以针对特定问题区域。

其中一些错误需要更仔细地查看插入语句以找到问题所在。这在大型 sql 文件中可能具有挑战性。为了解决这个问题,请将出错的 insert 语句写到一个单独的、更小的 sql 文件中,这样可以更轻松地研究该文件以找到问题。

my %lines_to_debug = map { $_ => 1 } (1148, 1195);  ...if (exists $lines_to_debug{$current_line_number}) {    print $debug_data "$line";  }

数据分块

无论您选择使用哪种脚本语言进行迁移,分块数据对于大型 sql 文件都非常重要。

对于此脚本,数据被分成 1mb 的块,这有助于保持脚本的效率。您应该选择对您的数据集有意义的块大小。

my $bytes_read = read( $original_data, $chunk, $chunk_size );

验证数据

有几种验证数据的方法

行数

进行行计数是确保至少插入所有行的简单方法。计算旧数据库中的行数并将其与新数据库中的行进行比较。

select count(*) from address_book

校验和

跨列运行校验和可能会有所帮助,但请记住,某些字段,尤其是 varchar 字段,可能已更改为 ansi 标准格式。因此,虽然这适用于某些领域,但它不会在所有领域都准确。

对于mysql

select md5(group_concat(coalesce(user_id, '') order by id)) from address_book

对于 postgresql

SELECT MD5(STRING_AGG(COALESCE(user_id, ''), '' ORDER BY id)) FROM address_book

手动数据检查

您还需要通过手动过程验证数据。运行一些有意义的查询,这些查询可能会发现导入问题。

最后的想法

迁移数据库是一项艰巨的任务,但只要仔细规划并充分了解您的数据集以及两个数据库系统之间的差异,就可以成功完成。

迁移到新数据库不仅仅是导入,但是可靠的数据集迁移将使您在其余的过渡过程中处于有利位置。

为此迁移创建的脚本可以在 git hub 上找到。

以上就是从 MySQL 迁移到 PostgreSQL的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/62570.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月11日 07:29:54
下一篇 2025年11月11日 07:33:54

相关推荐

  • HTML、CSS 和 JavaScript 中的简单侧边栏菜单

    构建一个简单的侧边栏菜单是一个很好的主意,它可以为您的网站添加有价值的功能和令人惊叹的外观。 侧边栏菜单对于客户找到不同项目的方式很有用,而不会让他们觉得自己有太多选择,从而创造了简单性和秩序。 今天,我将分享一个简单的 HTML、CSS 和 JavaScript 源代码来创建一个简单的侧边栏菜单。…

    2025年12月24日
    200
  • 前端代码辅助工具:如何选择最可靠的AI工具?

    前端代码辅助工具:可靠性探讨 对于前端工程师来说,在HTML、CSS和JavaScript开发中借助AI工具是司空见惯的事情。然而,并非所有工具都能提供同等的可靠性。 个性化需求 关于哪个AI工具最可靠,这个问题没有一刀切的答案。每个人的使用习惯和项目需求各不相同。以下是一些影响选择的重要因素: 立…

    2025年12月24日
    300
  • 带有 HTML、CSS 和 JavaScript 工具提示的响应式侧边导航栏

    响应式侧边导航栏不仅有助于改善网站的导航,还可以解决整齐放置链接的问题,从而增强用户体验。通过使用工具提示,可以让用户了解每个链接的功能,包括设计紧凑的情况。 在本教程中,我将解释使用 html、css、javascript 创建带有工具提示的响应式侧栏导航的完整代码。 对于那些一直想要一个干净、简…

    2025年12月24日
    000
  • 布局 – CSS 挑战

    您可以在 github 仓库中找到这篇文章中的所有代码。 您可以在这里查看视觉效果: 固定导航 – 布局 – codesandbox两列 – 布局 – codesandbox三列 – 布局 – codesandbox圣杯 &#8…

    2025年12月24日
    000
  • 隐藏元素 – CSS 挑战

    您可以在 github 仓库中找到这篇文章中的所有代码。 您可以在此处查看隐藏元素的视觉效果 – codesandbox 隐藏元素 hiding elements hiding elements hiding elements hiding elements hiding element…

    2025年12月24日
    400
  • 居中 – CSS 挑战

    您可以在 github 仓库中找到这篇文章中的所有代码。 您可以在此处查看垂直中心 – codesandbox 和水平中心的视觉效果。 通过 css 居中 垂直居中 centering centering centering centering centering centering立即…

    2025年12月24日 好文分享
    300
  • 如何在 Laravel 框架中轻松集成微信支付和支付宝支付?

    如何用 laravel 框架集成微信支付和支付宝支付 问题:如何在 laravel 框架中集成微信支付和支付宝支付? 回答: 建议使用 easywechat 的 laravel 版,easywechat 是一个由腾讯工程师开发的高质量微信开放平台 sdk,已被广泛地应用于许多 laravel 项目中…

    2025年12月24日
    000
  • 如何在移动端实现子 div 在父 div 内任意滑动查看?

    如何在移动端中实现让子 div 在父 div 内任意滑动查看 在移动端开发中,有时我们需要让子 div 在父 div 内任意滑动查看。然而,使用滚动条无法实现负值移动,因此需要采用其他方法。 解决方案: 使用绝对布局(absolute)或相对布局(relative):将子 div 设置为绝对或相对定…

    2025年12月24日
    000
  • 移动端嵌套 DIV 中子 DIV 如何水平滑动?

    移动端嵌套 DIV 中子 DIV 滑动 在移动端开发中,遇到这样的问题:当子 DIV 的高度小于父 DIV 时,无法在父 DIV 中水平滚动子 DIV。 无限画布 要实现子 DIV 在父 DIV 中任意滑动,需要创建一个无限画布。使用滚动无法达到负值,因此需要使用其他方法。 相对定位 一种方法是将子…

    2025年12月24日
    000
  • 移动端项目中,如何消除rem字体大小计算带来的CSS扭曲?

    移动端项目中消除rem字体大小计算带来的css扭曲 在移动端项目中,使用rem计算根节点字体大小可以实现自适应布局。但是,此方法可能会导致页面打开时出现css扭曲,这是因为页面内容在根节点字体大小赋值后重新渲染造成的。 解决方案: 要避免这种情况,将计算根节点字体大小的js脚本移动到页面的最前面,即…

    2025年12月24日
    000
  • Nuxt 移动端项目中 rem 计算导致 CSS 变形,如何解决?

    Nuxt 移动端项目中解决 rem 计算导致 CSS 变形 在 Nuxt 移动端项目中使用 rem 计算根节点字体大小时,可能会遇到一个问题:页面内容在字体大小发生变化时会重绘,导致 CSS 变形。 解决方案: 可将计算根节点字体大小的 JS 代码块置于页面最前端的 标签内,确保在其他资源加载之前执…

    2025年12月24日
    200
  • Nuxt 移动端项目使用 rem 计算字体大小导致页面变形,如何解决?

    rem 计算导致移动端页面变形的解决方法 在 nuxt 移动端项目中使用 rem 计算根节点字体大小时,页面会发生内容重绘,导致页面打开时出现样式变形。如何避免这种现象? 解决方案: 移动根节点字体大小计算代码到页面顶部,即 head 中。 原理: flexível.js 也遇到了类似问题,它的解决…

    2025年12月24日
    000
  • 形状 – CSS 挑战

    您可以在 github 仓库中找到这篇文章中的所有代码。 您可以在此处查看 codesandbox 的视觉效果。 通过css绘制各种形状 如何在 css 中绘制正方形、梯形、三角形、异形三角形、扇形、圆形、半圆、固定宽高比、0.5px 线? shapes 0.5px line .square { w…

    2025年12月24日
    000
  • 有哪些美观的开源数字大屏驾驶舱框架?

    开源数字大屏驾驶舱框架推荐 问题:有哪些美观的开源数字大屏驾驶舱框架? 答案: 资源包 [弗若恩智能大屏驾驶舱开发资源包](https://www.fanruan.com/resource/152) 软件 [弗若恩报表 – 数字大屏可视化组件](https://www.fanruan.c…

    2025年12月24日
    000
  • 网站底部如何实现飘彩带效果?

    网站底部飘彩带效果的 js 库实现 许多网站都会在特殊节日或活动中添加一些趣味性的视觉效果,例如点击按钮后散发的五彩缤纷的彩带。对于一个特定的网站来说,其飘彩带效果的实现方式可能有以下几个方面: 以 https://dub.sh/ 网站为例,它底部按钮点击后的彩带效果是由 javascript 库实…

    2025年12月24日
    000
  • 网站彩带效果背后是哪个JS库?

    网站彩带效果背后是哪个js库? 当你访问某些网站时,点击按钮后,屏幕上会飘出五颜六色的彩带,营造出庆祝的氛围。这些效果是通过使用javascript库实现的。 问题: 哪个javascript库能够实现网站上点击按钮散发彩带的效果? 答案: 根据给定网站的源代码分析: 可以发现,该网站使用了以下js…

    好文分享 2025年12月24日
    100
  • 产品预览卡项目

    这个项目最初是来自 Frontend Mentor 的挑战,旨在使用 HTML 和 CSS 创建响应式产品预览卡。最初的任务是设计一张具有视觉吸引力和功能性的产品卡,能够无缝适应各种屏幕尺寸。这涉及使用 CSS 媒体查询来确保布局在不同设备上保持一致且用户友好。产品卡包含产品图像、标签、标题、描述和…

    2025年12月24日
    100
  • 如何利用 echarts-gl 绘制带发光的 3D 图表?

    如何绘制带发光的 3d 图表,类似于 echarts 中的示例? 为了实现类似的 3d 图表效果,需要引入 echarts-gl 库:https://github.com/ecomfe/echarts-gl。 echarts-gl 专用于在 webgl 环境中渲染 3d 图形。它提供了各种 3d 图…

    2025年12月24日
    000
  • 如何在 Element UI 的 el-rate 组件中实现 5 颗星 5 分制与百分制之间的转换?

    如何在el-rate中将5颗星5分制的分值显示为5颗星百分制? 要实现该效果,只需使用 el-rate 组件的 allow-half 属性。在设置 allow-half 属性后,获得的结果乘以 20 即可得到0-100之间的百分制分数。如下所示: score = score * 20; 动态显示鼠标…

    2025年12月24日
    100
  • CSS 最佳实践:后端程序员重温 CSS 时常见的三个疑问?

    CSS 最佳实践:提升代码质量 作为后端程序员,在重温 CSS/HTML 时,你可能会遇到一些关于最佳实践的问题。以下将解答三个常见问题,帮助你编写更规范、清晰的 CSS 代码。 1. margin 设置策略 当相邻元素都设置了 margin 时,通常情况下应为上一个元素设置 margin-bott…

    2025年12月24日
    000

发表回复

登录后才能评论
关注微信