2. 宏观差异先看清两套体系的底层逻辑2.1 openGauss的“PG血统”决定了差异的广度openGauss诞生于华为内核源于PostgreSQL 9.2后来社区持续演进。这一点决定了它的整体气质和MySQL截然不同数据类型体系、系统表结构、事务模型、甚至错误码设计全都带有浓重的PG印记。很多MySQL DBA第一次接触openGauss时第一个直觉就是“这不就是PostgreSQL换了个壳”——这个直觉基本正确但又不完全正确因为openGauss在PG基础上做了大量企业级增强比如列存、ABC压缩、全密态等。所以准确地说openGauss是“PG的血统、华为的企业级内核”而绝不是一个接口兼容MySQL的数据库。这就引出了核心问题MySQL DBA如果还用MySQL思维去操作openGauss几乎每一步都会碰壁。不要指望像某些国产数据库那样提供MySQL兼容模式openGauss生态里没有这个东西。它的SQL方言、事务控制、权限模型、存储过程语言都更接近PostgreSQL而不是MySQL。我们先从最大的几个分水岭说起。2.2 大小写敏感性与标识符处理习惯的颠倒这是MySQL转openGauss最容易被恶心到的第一道坎。MySQL在Linux上默认lower_case_table_names0但大多数生产环境为了跨平台会把它设成1也就是说表名不区分大小写开发者习惯写SELECT * FROM users磁盘上存成users也没关系。而openGauss遵循PG传统标识符默认会被折叠成小写除非你用双引号把它“锁住”。这个差异的杀伤力体现在哪直接反映在SQL编写规范上。比如你从MySQL迁移过来原SQL里写了SELECT * FROM UserInfo到了openGauss里它会老老实实去找userinfo这张表如果表名在DDL里是UserInfo用了双引号那你这条SQL就报“relation does not exist”。同一个项目里如果建表脚本里的人一会儿用驼峰、一会儿用下划线然后有些地方加了双引号有些地方没加迁移阶段的“relation does not exist”错误会密集到让你怀疑人生。实操建议非常明确新项目在openGauss上一律使用小写加下划线的命名规范例如user_info、order_detail。迁移存量表时必须将驼峰表名统一转换成小写下划线或者在所有SQL语句中保持一致的双引号用法但双引号方案后期非常痛苦不建议。使用information_schema或openGauss自带的系统视图如pg_tables来核验实际表名避免凭直觉写SQL。2.3 自增列实现方式AUTO_INCREMENT不只是换个名字MySQL里建一张自增Id表是心流操作CREATE TABLE t_user ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) );openGauss里如果你照着同样思路写会直接在语法解析阶段报错openGauss没有AUTO_INCREMENT关键字。它的自增方案有两种主流选择一是SERIAL伪类型二是IDENTITY列PG 10引入openGauss支持。最推荐的写法是使用GENERATED BY DEFAULT AS IDENTITY因为SERIAL底层会创建隐式序列后期做备份恢复或数据导入时序列的当前值可能和表数据不同步导致主键冲突。而IDENTITY列的序列管理在openGauss内部做得相对干净。参考写法CREATE TABLE t_user ( id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, name VARCHAR(50) );注意这里我写的是BY DEFAULT不是ALWAYS。二者区别在于BY DEFAULT允许你显式插入id值比如数据迁移时保留原主键而ALWAYS则禁止手动插入只能由系统生成。如果你的表有历史数据要导入使用ALWAYS会非常痛苦。还有一个隐藏差异MySQL的AUTO_INCREMENT列只能有一个且必须是索引列openGauss的IDENTITY或SERIAL则没有这个限制。但如果你沿用MySQL习惯给非主键列也加了IDENTITY后期逻辑会变得混乱建议还是只在主键列上使用。3. DDL与DML语法差异逐项拆解3.1 字段默认值CURRENT_TIMESTAMP和ON UPDATE都不见了MySQL里建表时一个create_time字段经常这样写create_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP这个ON UPDATE CURRENT_TIMESTAMP是MySQL为人诟病但也离不开的“坏朋友”它能让你省掉在业务代码里维护更新时间的心智负担。openGauss不支持ON UPDATE子句MySQL迁移过来的建表语句凡是带这个子句的直接语法报错。正确姿势是用触发器或者干脆把“更新维护”交给应用层。如果一定要数据库层兜底可以使用openGauss的触发器实现。比如CREATE OR REPLACE FUNCTION update_modified_time() RETURNS TRIGGER AS $$ BEGIN NEW.update_time CURRENT_TIMESTAMP; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_t_user_update BEFORE UPDATE ON t_user FOR EACH ROW EXECUTE FUNCTION update_modified_time();我把这段代码主动写出来是有原因的openGauss存储过程语法基于PL/pgSQL函数和触发器就是这么一套写法。它不是MySQL的CREATE TRIGGER那一套简洁风格初看会很不适应但逻辑非常清晰自定义程度高。默认值函数方面也值得注意。MySQL的DEFAULT 0、DEFAULT abc这类字面量默认值openGauss完全支持。但如果你用了DEFAULT UUID()、DEFAULT NOW()这种MySQL特色写法到了openGauss里要么换要么改成DEFAULT current_timestamp这类表达式。凡是MySQL的专用函数openGauss一概不认。3.2 UPDATE多表关联语法一个把两类用户都坑过的差异MySQL支持这条高频SQLUPDATE t_order o JOIN t_user u ON o.user_id u.id SET o.user_name u.name WHERE u.status 1;这种“UPDATE...JOIN”语法在MySQL里非常好用但在openGauss里会被拒绝。openGauss确切地说是PG系对UPDATE的语法定义更严格不支持UPDATE多表关联。要实现同样效果标准做法是使用UPDATE...FROM子句这在PG系里是正统方案UPDATE t_order o SET user_name u.name FROM t_user u WHERE o.user_id u.id AND u.status 1;很多人看到这个写法会问WHERE条件里既有表关联又有过滤条件会不会乱实际执行时逻辑其实很清楚FROM子句里的表负责提供关联数据WHERE负责控制哪些行需要更新。值得注意的是关联字段匹配写进了WHERE而SET子句可以引用FROM子句中的任何列。这是PG系一个较大的便捷特性能把复杂的子查询更新简化成多表连接。还有DELETE也有类似差异。MySQL的DELETE FROM a USING a JOIN b...到了openGauss里也存在兼容性问题PG系的标准写法是DELETE FROM t_order o USING t_user u WHERE o.user_id u.id AND u.status 0;我强烈建议所有准备从MySQL转openGauss的团队先把团队内部常用的JOIN更新和删除SQL全部跑一遍兼容性测试这两类语法是最容易在迁移测试后期突然爆雷的。3.3 字符串和日期处理函数名不同行为也不同MySQL的日期函数可以用DATE_FORMAT以各种格式输出时间openGauss里原生对应的是TO_CHAR(timestamp, YYYY-MM-DD HH24:MI:SS)这个PG神仙函数。举几个最常见的对照MySQL的NOW()openGauss用CURRENT_TIMESTAMP或者NOW()也能用但要注意它返回的是带时区的时间戳。MySQL的DATE_ADD(date, INTERVAL 1 DAY)openGauss对应date INTERVAL 1 day注意这里的INTERVAL后面跟的是字符串字面量不是独立的关键字表达式。MySQL的STR_TO_DATE(str, format)openGauss很少用这个一般直接用TO_DATE(str, format)。MySQL的SUBSTRING_INDEX(str, delim, count)openGauss里官方推荐用SPLIT_PART(str, delim, n)而且n是从1开始计数的不像MySQL的count可以传负数表示从右侧取。字符串连接MySQL里CONCAT函数好用而openGauss里的||运算符是正宗PG风格注意虽然openGauss也支持CONCAT函数但遇到NULL值时的行为和MySQL不同MySQL的CONCAT里有一个参数为NULL结果就是NULL而openGauss的||也是同样的行为但两者混用容易让新手困惑。再把一个容易忽略的点讲透字符集和排序规则。MySQL里utf8mb4是事实标准而openGauss的初始化模板通常使用UTF8也就是PG里的UTF-8编码排序规则默认是C.UTF-8中文排序基本不是你想的那种拼音序。如果需要中文排序你得显式指定COLLATE zh_CN之类但很多OpenGauss安装场景下并没有装对应locale所以如果你有拼音排序需求建议直接考虑应用层排序不要指望数据库排序规则这一条放到迁移实战中能帮你省下大量的调试时间。3.4 分页查询与LIMIT语法LIMIT/OFFSET是兼容的但隐藏一个坑MySQL里LIMIT 10 OFFSET 20openGauss完全支持这一点两边的语法几乎一致。但PG系有个传统技巧是LIMIT ALL这个在openGauss里也用MySQL没有对应概念。真正需要警惕的是超大分页性能问题。两边的优化器在处理深度分页时都有局限但openGauss因为是PG优化器出身对OFFSET的翻页优化手段相对较少一旦翻到几十万页之后性能会急剧下降。我在实际项目中测试过一张200万行的表从MySQL迁移过来后在openGauss里用LIMIT 100000, 20翻页执行计划直接给seq scan响应时间从原来的几十毫秒炸到几秒。MySQL在同等条件下会有更激进的优化比如用索引条件下推但openGauss的标准答案是“换一种翻页范式”-- 键集分页Keyset Paging是PG系的推荐做法 SELECT * FROM t_user WHERE id 10023 ORDER BY id LIMIT 20;这个方案在两边数据库里都高效openGauss尤其需要这样写因为它对OFFSET的物理跳过实现比较“朴素”几乎不做什么预取。所以迁移后如果发现原来MySQL上“勉强能用”的超大分页接口直接瘫痪不要慌改用键集分页性能回暖会非常明显。4. 存储过程与PL/pgSQL落差最大的领域4.1 MySQL存储过程到openGauss的迁移思路热搜词里反复出现“mysql存储过程”“mysql声明存储过程”说明大量团队正在存储过程层面做迁移适配。这里我直接点明一个事实把MySQL存储过程原封不动搬到openGauss里成功率趋近于零。因为openGauss的存储过程过程语言是PL/pgSQL语法规格和MySQL的复合语句语法差得太远。两个最典型的语法分水岭第一变量声明位置。MySQL的DECLARE只能放在BEGIN...END块的开头openGauss支持在任何语句块内声明变量。很多人在openGauss里沿用MySQL习惯全部堆在块开头没错能跑但没发挥出PL/pgSQL的块结构优势。第二SELECT...INTO语义完全不同。MySQL的存储过程里SELECT col INTO var FROM table是给变量赋值的常规操作。而在openGaussPL/pgSQL中SELECT INTO默认是创建一张新表PG的原生语义你必须写成SELECT col INTO var FROM table的特定形式并且要确保这个INTO目标是一个记录变量、行类型或标量变量列表而不是一张新表。这个语义反转极其容易踩坑我自己的团队就曾在迁移时把一个“取用户最大Id”的过程写成SELECT MAX(id) INTO v_max_id FROM t_user;结果第一次执行直接报语法或者行为异常后来才回想起来在PL/pgSQL里这个写法其实是对的但如果你不加PERFORM而且变量类型不匹配也会出现诡异报错。总之PL/pgSQL中所有“SELECT直接取值”的操作都要重新审视。4.2 循环、条件与异常处理的写法差异MySQL里写一个FOR循环一般用WHILE或者LOOP而openGauss的PL/pgSQL提供了一种非常顺手、但MySQL DBA完全陌生的写法——FOREACH遍历数组更普通的是FOR i IN 1..10 LOOP这种循环区间写法。以一个经典需求“逐行处理游标结果”为例MySQL风格DECLARE cur CURSOR FOR SELECT id FROM t_user; OPEN cur; REPEAT FETCH cur INTO v_id; ... UNTIL done END REPEAT; CLOSE cur;openGauss风格FOR rec IN SELECT id FROM t_user LOOP -- 直接使用 rec.id END LOOP;这是体系层面的差异MySQL相对“基础”一切自己控制openGauss的FOR...IN...LOOP直接帮你管理游标的打开、取数和关闭不仅写起来短还天然避免了游标未关闭导致的资源泄漏。如果你接手的是一个充满了手写游标的老MySQL存储过程迁移到openGauss时大胆地重构成FOR LOOP会让代码短一半。异常处理差异也很关键。MySQL的DECLARE EXIT HANDLER FOR SQLEXCEPTION到了openGauss变成了BEGIN ... EXCEPTION WHEN OTHERS THEN -- 异常处理逻辑 END;PL/pgSQL的WHEN OTHERS相当于MySQL的SQLEXCEPTIONWHEN unique_violation对应SQLSTATE 23505这类异常代码体系是PG生态的MySQL的1062 Duplicate entry在openGauss里根本不存在。做存储过程迁移的人建议先整理一个常用SQLSTATE对照表比如23505唯一约束冲突、23503外键约束冲突、22012除零错误不要指望报错信息里的文本和MySQL一致。4.3 事务控制与提交方式MySQL的存储过程默认是“每条语句自动提交”除非你显式START TRANSACTION。而openGaussPG系的行为是存储过程整体运行在事务块中默认不自动提交除非使用事务控制语句显式提交。迁移后如果你发现原来MySQL存储过程里“改了一半报错回滚”的行为在openGauss里变成了“所有变更都还在”不要惊讶这是因为openGauss的过程默认在事务块内如果不在异常处理时回滚数据修改在过程结束时可能被提交。这一点在数据修复类脚本中尤其危险。我的建议是业务过程的入口处显式声明事务边界并对异常块做明确的ROLLBACK控制不要依赖数据库的默认行为。5. 索引与性能调优从EXPLAIN语法到优化习惯5.1 索引语法与部分索引能力差异MySQL和openGauss在基础索引创建上差异不大CREATE INDEX idx_name ON table(col)两边通用。但openGauss继承了PG的两个核心杀手锏MySQL原生做不到部分索引Partial IndexCREATE INDEX idx_active_user ON t_user(status) WHERE status 1;表达式索引Expression IndexCREATE INDEX idx_lower_name ON t_user(lower(name));这两类索引在查询性能优化上威力巨大。MySQL 8.0虽然也支持函数索引8.0.13起但部分索引这种“只给热点数据建索引”的思路MySQL至今没有原生产物。迁移后如果原MySQL表上有状态字段过滤需求比如WHERE status 1 AND ...到了openGauss完全可以设计成部分索引索引体积急剧缩小命中率飙升。但这里有一个反直觉提醒openGauss的索引代价模型比MySQL更复杂GIN、BRIN这些索引类型都有各自的适用场景。如果你习惯了MySQL“多个索引优化器会自己选”在openGauss里可能发现它选的执行计划远非最优甚至全表扫描还更快。这不是bug而是PG系优化器对统计信息更敏感。迁移后第一件事是执行ANALYZE全库把统计信息刷新一遍否则执行计划一定乱套。5.2 EXPLAIN的解读差异MySQL的执行计划用EXPLAIN输出一个表格有key、rows、Extra等直观字段。openGauss也有EXPLAIN但它默认输出的是树形文本计划术语完全不同没有Using where、Using index这些MySQL黑话取而代之的是Seq Scan、Index Scan、Hash Join、Nested Loop。如果你只会看MySQL的EXPLAIN到了openGauss会明显觉得“抓瞎”。举一个必须掌握的核心概念Seq Scan顺序全表扫描要警惕。Index Scan普通索引扫描。Index Only Scan覆盖索引扫描你想要的。Bitmap Index Scan位图索引扫描适合低选择性条件。Nested Loop嵌套循环连接小表驱动大表场景常见。Hash Join哈希连接大表等值连接场景常见。调优的方向上openGauss更强调规划器提示SET enable_seqscan off这种参数级干预而MySQL则习惯用FORCE INDEX或者改写SQL。两套方法论差异很大团队里如果有人只会MySQL优化套路迁移到openGauss后必须重新学习执行计划解读这没有捷径。5.3 统计信息与VACUUM一个MySQL DBA最容易忽视的新增负担MySQL的InnoDB有MVCC机制但对旧版本数据的清理是内部自动完成的DBA基本不用操心。openGaussPG系里有一个概念叫VACUUM必须定期执行否则表的物理文件膨胀查询越来越慢。事务ID回卷风险极端情况下会触发强制VACUUM甚至数据库进入只读保护。openGauss虽然提供自动VACUUMautovacuum默认开启但很多从MySQL转过来的团队完全没有这个概念导致运行几周后发现数据文件翻了好几倍性能直线下落。这里的实操建议是在openGauss的postgresql.conf里打开autovacuum on默认就是on。对于高频update/delete的大表建议手动设置更积极的autovacuum阈值。大版本升级或大批量导入后手动执行VACUUM FULL ANALYZE整理物理文件。很多MySQL DBA对VACUUM FULL最大的疑惑是“为什么空间没有还给操作系统”这和PG系表存储实现有关VACUUM FULL会重建表并压缩空间而普通VACUUM只标记死元组可复用。这个差异直接决定了你在openGauss里磁盘空间管理的思路不要盲目执行VACUUM FULL它会产生表级锁大表上操作要安排在维护窗口。6. 用户权限与系统结构从“数据库”到“数据库/schema”双层映射6.1 实例、数据库、Schema的概念错位MySQL的逻辑结构是“实例 - 数据库(database) - 表”一个实例下可以建多个数据库库与库之间的隔离相对干净USE dbname切换起来非常爽。openGauss的逻辑结构是“实例 - 数据库(database) - 模式(schema) - 表”中间多了一层Schema。默认情况下一个数据库下有一个public模式你建表时如果不指定Schema它就放public下。从MySQL迁移过来的开发者很容易把MySQL里的“database”直接映射到openGauss的“database”但事实上当你执行\c dbname切换数据库后原来USE dbname所带来的“自动定位表空间”的感觉会弱化很多因为真正的表级归属在Schema上。这个差异的直接后果是跨Schema查询MySQL里跨库查询要带库名前缀db1.table1openGauss里跨Schema查询要带schema.table比如public.t_user。权限模型错位MySQL的授权粒度大多是“库级”或“全局”权限openGauss则是“schema级”和“表级”权限GRANT语句的表达方式差异也很大。连接串差异MySQL JDBC连接串是jdbc:mysql://host:3306/dbnameopenGauss是jdbc:postgresql://host:5432/dbname而且openGauss的JDBC驱动要求url里必须指定数据库名驱动类名是org.opengauss.Driver也有org.postgresql.Driver的兼容版但不推荐混用。我碰到过很多从MySQL迁移过来的运维同学在openGauss里习惯性地把所有表都建在public模式下然后权限一放开就是整个public模式全部可访问。这种做法在开放环境下风险极大。建议从第一天起就按业务域划分好schema并且把每个schema的权限独立管理千万别把MySQL的库习惯直接平移到Schema上。6.2 大小写和对象名的搜索路径search_pathMySQL的USE dbname之后“当前数据库”对用户是透明隐式的。openGauss没有“当前数据库”这一说你始终在一个数据库里但能访问的时空由search_path控制。默认的search_path是$user, public意思是先找你用户名同名的Schema找不到再找public。这个机制导致一个奇怪的坑如果某个迁移账号叫app_user而且不小心建了一个app_user这个Schema同时public里也建了相同表名的表那么SQL访问时会被python重定向到app_user模式下去了。排查这种“表明明存在却反复报不存在或数据不对”的问题优先看SHOW search_path;。6.3 备份恢复和系统表查询的对应关系MySQL里的信息获取习惯是SHOW TABLES、SHOW CREATE TABLE、SHOW INDEX FROM。openGaussPG系用这些\dt列出当前schema下的表。\d table_name查看表结构。pg_tables系统视图查询表信息。information_schema.tables兼容SQL标准两个数据库都有但细节字段不同。SHOW CREATE TABLE在openGauss中对应pg_dump --schema-only -t table_name或者用\d查看细节。如果你在MySQL里习惯用SHOW CREATE TABLE来复制建表语句到了openGauss里得学会用系统函数拼接DDL或者干脆依赖备份工具。7. 运维实操安装部署、连接配置与备份恢复7.1 安装流程的基本盘热词里反复出现“mysql安装教程”“opengauss安装部署流程”说明部署环节就是第一道坎。MySQL的安装在Linux上相对轻量下载tar包解压配置就能跑。openGauss的安装要“重”很多它的发布形态是一个包含数据库内核和服务端组件的完整安装介质安装方式大体分三种极简版单机安装适合测试环境脚本化部署流程较快。企业版om安装正式生产推荐通过om工具进行初始化和启动支持一主多备。容器化部署openGauss官方提供Docker镜像适合快速体验但生产环境性能、网络、持久化都需要仔细设计。我自己常用的单机体验部署方式是极简版步骤大概是创建omm用户openGauss不允许用root直接运行。解压安装包到指定目录比如/opt/opengauss。执行gs_initdb初始化数据目录。用gs_ctl start启动数据库。修改postgresql.conf的端口、listen_addresses等参数后重启。这里有个新手必踩的坑openGauss默认安装后监听地址是localhost从远程用JDBC连接的时候报“Connection refused”。你以为防火墙问题其实检查顺序应该是postgresql.conf里listen_addresses是否已经是*pg_hba.conf里是否添加了客户端IP的访问规则然后才是系统防火墙。这个排错思路和MySQL完全不同MySQL的bind-address默认也限制但pg_hba.conf的概念在MySQL里完全不存在很多DBA到了openGauss会把所有时间花在系统网络排查上。pg_hba.conf是openGauss接入认证的核心配置类似于MySQL的user表skip-grant-tables管理的混合体。格式长这样host all all 192.168.1.0/24 sha256这是“允许192.168.1.0/24网段的机器以sha256加密方式连接所有数据库的所有用户”。注意openGauss默认加密方式是sha256不是MySQL的mysql_native_password或caching_sha2_password客户端侧的驱动必须支持对应认证协议。如果你用老版本JDBC或者某些只支持MySQL协议的中间件连接时会直接Authentication failed。7.2 数据库URL、驱动与连接参数MySQL的连接串和openGauss连接串的差异值得单独列出来因为这块是应用改造成本最高的部分之一。MySQL典型写法jdbc:mysql://127.0.0.1:3306/testdb?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueopenGauss典型写法jdbc:opengauss://127.0.0.1:5432/testdb?currentSchemapublicloggerLevelOFF注意驱动类名和URL前缀完全不同。openGauss官方JDBC驱动的主类是org.opengauss.Driver但也保留了兼容org.postgresql.Driver的能力依赖包里都有。如果项目中同时有PostgreSQL驱动和openGauss驱动务必注意classpath中只能保留一个否则驱动注册会互相覆盖产生“connection refused”或者“Invalid URL”的怪错。useSSL参数在MySQL里控制是否加密连接openGauss对应的是sslmode参数disable、allow、prefer、require等很多人照搬MySQL的useSSLfalse到了openGauss连接串里变成无效参数不会报错但SSL并没有被关闭。想明确关闭加密正确写法是sslmodedisable。7.3 备份与恢复mysqldump vs gs_dumpMySQL备份首选mysqldump或者mysqlpumpopenGauss对应工具叫gs_dump逻辑设计和pg_dump一致。基本用法# 导出整个数据库 gs_dump -U omm -W your_password -F p -f backup.sql testdb # 只导出表结构和数据不导出权限 gs_dump -U omm -W your_password -F p -f backup.sql --no-owner testdb # 导出为自定义归档格式便于恢复时选择性导入 gs_dump -U omm -W your_password -F c -f backup.dmp testdb恢复则用gs_restore或者直接gsql -f backup.sql执行SQL文件。这里有一个MySQL DBA特别容易踩的坑openGauss的gs_dump导出的SQL文件中包含了很多PL/pgSQL函数定义和触发器语句用gsql执行时必须确保当前session的search_path正确否则函数会建到错误schema里或者报找不到表。建议恢复时在SQL文件头部显式加上SET search_path public;或者恢复前在gsql中执行SET search_path xxx;。另一个坑是大小写。MySQL的mysqldump导出的建表语句如果原库大小写混合到了openGauss就会遇到前面说的“relation does not exist”。所以迁移流程建议这样梳理先用MySQL导出结构不导出数据。用脚本把建表语句里的表名转换成小写下划线。在openGauss中执行。再单独导出数据做导入。在应用层修复SQL语句大小写、函数差异、分页写法等。7.4 常用运维命令对照速查表这个表格我直接做成速查卡贴在公司wiki里无数次每次迁移项目都有人问我同样的问题。操作场景MySQL命令openGauss命令登录客户端mysql -u root -pgsql -U omm -W密码 -d postgres查看数据库列表SHOW DATABASES;\l 或 SELECT datname FROM pg_database;切换数据库USE dbname;\c dbname查看表列表SHOW TABLES;\dt查看表结构DESC table_name; 或 SHOW CREATE TABLE;\d table_name\d table_name查看当前用户SELECT user();SELECT current_user;查看版本SELECT VERSION();SELECT version();查看连接数SHOW PROCESSLIST;SELECT * FROM pg_stat_activity;终止连接KILL id;SELECT pg_terminate_backend(pid);索引CREATE INDEX ...相同但支持部分索引和表达式索引备份mysqldumpgs_dump恢复mysql backup.sqlgsql -f backup.sql 或 gs_restore这个表只是应急速查真正的深度差异在文中各节已有展开。不要以为命令差异就是全部SQL语法、权限模型、事务行为、存储过程这些“看不见”的差异才是迁移中最耗时的地方。8. 常见错误信息与排查技巧实录8.1 从MySQL带到openGauss后最常见的5个错误ERROR: relation xxx does not exist原因大小写、schema归属或search_path不对。排查顺序查表真实名称检查当前schema看search_path。技巧用\dt xxx或者\d TableName验证。ERROR: syntax error at or near ON原因大概率是MySQL特有的ON UPDATE CURRENT_TIMESTAMP或INSERT ... ON DUPLICATE KEY UPDATE语法不被支持。技巧openGauss里没有INSERT ... ON DUPLICATE KEY UPDATE对应方案是INSERT ... ON CONFLICT DO NOTHING/DO UPDATE。这个差异值得单独提一句ON CONFLICT的灵活度远超MySQL的DUPLICATE KEY比如你可以指定ON CONFLICT (id) DO UPDATE SET name EXCLUDED.name但语法细节不同迁移时注意改写。ERROR: column xxx specified in USING clause does not exist in left table原因JOIN用法差异PG系不允许某些USING和ON混用的情况。技巧统一用ON指定连接条件少用USING缩写。ERROR: function to_char(timestamp without time zone, unknown) does not exist原因to_char的格式串没走对或者数据类型不匹配。技巧openGauss的to_char对格式模板很严格比如HH24不能写成HH月份MM和分钟MI千万不能混。FATAL: no pg_hba.conf entry for host 192.168.x.x, user app, database postgres原因远程访问策略没放开这是openGauss安全机制的正常拦截。技巧在pg_hba.conf中增加对应主机网段的访问规则并reloadopenGauss是gs_ctl reloadMySQL对应的是FLUSH PRIVILEGES完全不是一套概念。8.2 连接类问题的排查顺序MySQL连接不上时大家习惯先查bind-address、skip-networking、max_connections最后才看防火墙。openGauss的排查逻辑顺序建议固定为postgresql.conf里listen_addresses是否允许所需监听IP。pg_hba.conf里是否有对应客户端IP的认证条目认证方式对不对。系统防火墙防火墙放行端口。JDBC驱动版本是否与openGauss服务端版本匹配版本跨度太大时认证协议可能不兼容。数据库是否正常启动gs_ctl status。有几次线上问题排查到最后发现是JDBC驱动版本太老不兼容服务端的sha256密码认证。这个问题在MySQL体系里不存在因为MySQL的认证插件协议相对稳定但openGauss迭代过程中对加密协议有过调整所以升级服务端后旧驱动直接连不上建议定期更新官方JDBC驱动。8.3 性能类差异的排查思维转变MySQL里遇到慢查询习惯先看索引是否被使用如果没用到就FORCE INDEX。openGauss里你如果也这么做等于放弃了PG优化器的SET参数调整能力比如SET enable_nestloop off;强制不使用嵌套循环连接。SET enable_seqscan off;强制不使用顺序扫描。SET work_mem 64MB;增大排序和哈希操作的可用内存。不过这些参数调整是会话级的生产环境应该谨慎使用。迁移后最常见的性能问题是统计信息陈旧导致执行计划错判。注意MySQL 8.0的统计信息更新相对自动但openGauss的autovacuum在部分自定义场景下触发不及时。一个很实用的习惯是每次大批量数据导入后立即执行ANALYZE tableName;否则下一次查询的执行计划大概率是错的。这个习惯在MySQL时代几乎没人会做但在openGauss里它属于基本功。9. 迁移项目实操建议与团队协作心得9.1 从MySQL迁移到openGauss的技术预检清单我参与过的几个真实迁移项目最后都形成了一个固定的checklist每次项目启动前直接拿来当模板用结构评估把所有表结构导出后扫描AUTO_INCREMENT、ON UPDATE CURRENT_TIMESTAMP、字符集排序规则、引擎相关属性ENGINEInnoDB等在openGauss里不认。SQL语法评估重点扫描JOIN UPDATE、JOIN DELETE、ON DUPLICATE KEY、GROUP BY的非标准写法、IFNULL和LIMIT大偏移量。存储过程/函数/触发器评估准备PL/pgSQL重写方案不要指望自动化工具全量转换。应用层改造评估JDBC连接串、驱动类名、SQL语句中的函数名和日期格式写法。数据迁移方案选型小数据量可以手工导中等数据量用mysqldump导出后转CSV再COPY导入大数据量必须用工具或分批次ETL。其中COPY是openGauss数据导入的超级工具PG系标准能力格式像COPY t_user FROM /tmp/user.csv WITH (FORMAT csv, HEADER true);。它的导入速度远远快于逐条INSERTMySQL里的LOAD DATA INFILE虽然也有类似的批处理能力但openGauss的COPY在配合\copy命令时对权限限制更宽松适合通过客户端执行导入这类细节在迁移文档里很容易被忽略但能节省大量工时。9.2 团队知识结构调整与技术文化转变从MySQL转到openGauss最难的部分往往不在技术而在团队已有的思维定式。我的经验是让团队里的MySQL高手在openGauss上写一个周的小项目把存储过程、分区表、全文索引、大页查询都过一遍建立体感。让运维同学把openGauss排错思路和MySQL排错思路并列写成一个对照表每天晨会花10分钟过三个差异点。禁止一切“MySQL能这样凭什么openGauss不行”的抱怨式讨论技术选型既定就全力以赴适配。很多人把国产数据库迁移看成“换一个存储引擎”这是根本性误解。openGauss和MySQL虽然都是关系型数据库但一个是PG生态一个是MySQL生态两者的SQL标准支持程度、优化器实现、过程语言模型差异极大。如果团队执行力不够迁移项目很容易在“语法改写到心态崩溃”的阶段戛然而止。9.3 迁移过程中的风险控制和回退策略任何迁移项目都要给自己留后路我的建议是灰度迁移先切读流量再切写流量写流量切过去后保留至少一个月的数据双写或每日反向同步。全量比对迁移后每天晚上跑一次核心表的数据比对任务字段级别比对差异不能只比对行数。回退预案openGauss侧的数据可以每天导出备份万一业务要切回MySQL必须有完整的反向迁移链路。我个人在实操中体会最深的一点是迁移工具和自动化脚本可以减少工作量但无法替代人对语义差异的理解。openGauss和MySQL之间的不兼容点不是改改关键字就能解决的它牵涉到事务语义、权限模型、SQL方言、存储过程语言、运维体系、甚至团队心智模型的全方位切换。做迁移比做新选型更麻烦因为你周围的每一个人都带着“MySQL怎么做”的经验惯性。本文里列出的对比表是帮你快速建立“openGauss会怎么做”的新惯性真正动起手来你会遇到比这篇文字多十倍的具体问题但只要把差异背后的原理想明白排错的速度会快很多。
