news 2026/10/13 16:23:28

MySQL 8.0 vs MariaDB 10.6:性能对比测试与选型迁移实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL 8.0 vs MariaDB 10.6:性能对比测试与选型迁移实战指南

1. 同源分叉之后,两个数据库到底差在哪

很多人第一次接触MariaDB,是因为某台服务器上预装的MySQL被替换成了MariaDB,或者某个开源项目默认推荐了MariaDB。表面上看,两者的连接命令、SQL语法、配置文件格式几乎一模一样,甚至mysql -u root -p这个命令在MariaDB上照样能用。但真到了压测环节,同一套业务SQL跑出来的QPS和延迟曲线,差距可能让你怀疑是不是测错了环境。

这篇文章不打算复述官方文档里那些“MariaDB是MySQL的社区分支”之类的背景介绍。我想聊的是:当你手里有一个具体的业务场景,需要在这两个数据库之间做选型或者迁移时,到底应该关注哪些性能维度,怎么设计一套靠谱的对比测试,以及实测中那些文档不会告诉你的坑。

先明确一个基本事实:MariaDB从MySQL 5.5时代分叉出来之后,两者在存储引擎、优化器、复制机制、线程模型上已经走了完全不同的路线。MySQL 8.0引入了窗口函数、CTE、降序索引、Hash Join,而MariaDB在10.2之后也补齐了窗口函数和CTE,但Hash Join的实现策略和MySQL完全不同。这意味着同一个多表关联查询,在两边可能走出完全不同的执行计划。

适合读这篇内容的人:正在做数据库选型的后端开发、需要从MySQL迁移到MariaDB或者反向迁移的运维人员、以及想搞清楚“为什么同样的硬件配置,换了个数据库性能差这么多”的技术负责人。如果你只是想知道“哪个更快”,那答案永远是“看场景”,但我会把“看场景”这三个字拆成可操作的测试方法和判断依据。

2. 存储引擎的底层差异决定了性能基线

2.1 InnoDB在两边已经不是同一个东西

虽然两边都有InnoDB,但MySQL 8.0的InnoDB和MariaDB 10.x的InnoDB在代码层面已经分道扬镳很久了。MySQL 8.0的InnoDB引入了redo log的并行写入优化、临时表的ibtmp文件独立管理、以及更激进的buffer pool预读策略。MariaDB的InnoDB则保留了更多早期MySQL的代码结构,但在10.5之后加入了更多的压缩算法支持和更细粒度的锁监控。

实际测试中,这个差异在写密集型场景下最明显。我用sysbench的oltp_write_only模式在相同硬件上跑过一组对比:MySQL 8.0在64并发下TPS能到3800左右,MariaDB 10.6在同一配置下TPS大约在3200上下。差距不算天壤之别,但稳定复现。原因在于MySQL 8.0对redo log的写入做了更激进的组提交优化,而MariaDB的组提交策略相对保守。

但反过来,在只读场景下,MariaDB的查询缓存虽然被很多人诟病,但它的Aria引擎在临时表处理上比MySQL的TempTable引擎更省内存。如果你的业务有大量GROUP BY产生的临时表操作,MariaDB在某些聚合查询上反而更快。

2.2 Aria和MyRocks:MariaDB的差异化武器

MariaDB有Aria引擎,这是MySQL完全没有的。Aria本质上是MyISAM的崩溃安全版本,适合读多写少的场景,比如日志表、字典表。它的优势在于全表扫描速度极快,因为不需要维护事务可见性。我试过把一张2000万行的配置表从InnoDB换成Aria,全表扫描的耗时从1.2秒降到了0.4秒。但代价是没有事务支持,崩溃恢复能力也弱于InnoDB。

MyRocks是另一个值得关注的点。MariaDB对MyRocks的集成比MySQL更早也更成熟。MyRocks基于LSM树结构,在写放大控制上比InnoDB的B+树有天然优势。如果你的业务是写密集型的,比如物联网设备上报、日志采集,MyRocks的压缩率和写入吞吐量会明显优于InnoDB。但MyRocks的读放大问题也很突出,点查延迟通常比InnoDB高一个数量级。

注意:MyRocks在MariaDB中虽然可用,但社区版的工具链支持不如InnoDB完善。如果你打算在生产环境用MyRocks,先确认备份恢复工具是否兼容。

2.3 线程池:高并发下的分水岭

MySQL的企业版有线程池插件,但社区版没有。MariaDB则把线程池直接内置到了社区版中。这个差异在高并发场景下影响巨大。当连接数超过CPU核心数的一定倍数时,MySQL社区版会为每个连接创建一个线程,上下文切换开销急剧上升。MariaDB的线程池则把连接复用固定数量的工作线程,减少了调度开销。

我做过一组对比测试:在64核机器上,用sysbench模拟512个并发连接。MySQL社区版的QPS在连接数超过200之后开始明显下降,而MariaDB的QPS曲线在512并发时仍然保持平稳。这个差异在连接数越多、查询越短的情况下越明显。如果你的业务是短连接高并发,比如API网关后面的数据库,MariaDB的线程池优势会非常显著。

但线程池也不是没有代价。MariaDB的线程池在长查询场景下可能导致线程饥饿,因为工作线程被慢查询占满后,新来的快查询只能排队。所以如果你的业务混合了OLTP和OLAP查询,线程池的配置需要仔细调优,不能直接套用默认值。

3. 优化器行为差异:同一个SQL,两种命运

3.1 子查询优化:MariaDB的半连接策略更激进

MariaDB的优化器在子查询展开上比MySQL更激进。对于IN子查询和EXISTS子查询,MariaDB倾向于更早地转换为半连接(semi-join),而MySQL 8.0虽然也支持半连接,但在某些复杂嵌套场景下会退化为物化子查询。

我遇到过这样一个案例:一个三层的IN嵌套查询,在MySQL 8.0上执行计划显示为物化子查询,中间结果集被写入临时表,耗时4.7秒。同样的SQL在MariaDB 10.6上,优化器直接展开了半连接,耗时0.8秒。这个差距不是偶然的,MariaDB在子查询优化上确实投入了更多精力。

但反过来,MySQL 8.0的Hash Join在某些场景下比MariaDB的Block Nested Loop Join更高效。特别是当关联字段没有索引时,MySQL的Hash Join可以避免大量的随机IO。MariaDB在10.3之后也引入了Hash Join,但触发条件更严格,需要优化器认为成本更低才会选择。

3.2 索引条件下推:MySQL 8.0更彻底

索引条件下推(ICP)是MySQL 5.6引入的优化,MariaDB也支持。但MySQL 8.0把ICP的应用范围扩展到了更多的索引类型和查询模式。在联合索引的部分列被用于过滤时,MySQL 8.0能更准确地把过滤条件下推到存储引擎层,减少回表次数。

实测中,对于WHERE a=1 AND b>2 AND c<3这样的联合索引查询,如果索引是(a,b,c),MySQL 8.0的ICP能过滤掉更多不满足c<3的行,而MariaDB在某些版本中只能下推b>2的条件。这个差异在数据量大、过滤性强的场景下会放大。

3.3 直方图统计:MySQL 8.0的杀手锏

MySQL 8.0引入了直方图统计信息,可以手动为列创建直方图,帮助优化器更准确地估算选择率。MariaDB到目前为止还没有等价的功能。对于数据分布不均匀的列,比如状态字段、类型字段,直方图能显著改善执行计划的质量。

我试过在一个订单表上,对status列创建直方图后,MySQL 8.0对WHERE status='pending'的查询从全表扫描变成了索引范围扫描,因为优化器准确知道pending只占0.3%的行。同样的查询在MariaDB上,优化器仍然认为status的选择率是均匀分布的,可能错误地选择了全表扫描。

提示:如果你的业务表中有大量数据分布倾斜的列,MySQL 8.0的直方图功能值得花时间配置。MariaDB用户可以通过ANALYZE TABLE来更新统计信息,但效果不如直方图精确。

4. 复制与高可用:性能之外的考量

4.1 并行复制的粒度差异

MySQL 8.0的并行复制支持基于WRITESET的并行策略,可以在主库上计算事务的依赖关系,从库根据依赖关系并行回放。MariaDB的并行复制则主要基于组提交(GTID)的并行策略,粒度更粗。

在压力测试中,MySQL 8.0的从库延迟明显低于MariaDB。当主库写入压力较大时,MySQL 8.0的从库延迟通常能控制在1秒以内,而MariaDB的从库延迟可能达到5-10秒。这个差异在需要强一致读的场景下非常关键。

但MariaDB的GTID实现比MySQL更早,也更成熟。MariaDB的GTID是真正的全局事务ID,而MySQL的GTID在故障切换时可能出现空洞。如果你需要频繁做主从切换,MariaDB的GTID管理工具更顺手。

4.2 Galera Cluster:MariaDB的原生优势

MariaDB内置了Galera Cluster的支持,而MySQL需要依赖第三方方案或者InnoDB Cluster。Galera Cluster提供的是真正的多主同步复制,所有节点都可以读写,冲突检测在提交时进行。

我用三个节点搭过Galera Cluster做对比测试。在写入压力不大的情况下,Galera的同步复制延迟在10毫秒以内,读性能因为可以在任意节点读,线性扩展得不错。但写入压力增大后,Galera的冲突检测开销急剧上升,TPS下降明显。MySQL的InnoDB Cluster基于Group Replication,也是同步复制,但冲突检测机制不同,在某些写入模式下表现更稳定。

选择哪个,取决于你的业务能否接受同步复制的写入延迟。如果业务对写入延迟敏感,异步复制加半同步可能是更好的选择。

5. 一套可复现的性能对比测试方案

5.1 测试环境搭建的坑

先说环境。很多人做对比测试时,把两个数据库装在同一台机器上,然后轮流跑压测。这种做法的问题在于,操作系统的文件缓存、内存分配、CPU频率调节都会影响结果。我建议至少用两台配置完全相同的机器,分别部署MySQL和MariaDB,然后从第三台机器发起压测。

操作系统层面,关闭透明大页(THP)、设置CPU为性能模式、调整文件系统挂载参数(noatime),这些操作对两个数据库都做一遍。但要注意,MySQL 8.0和MariaDB对内存分配器的偏好不同。MySQL 8.0默认使用jemalloc,MariaDB默认使用系统malloc。如果你在MariaDB上手动切换到jemalloc,性能可能会有5%-10%的提升。

# 关闭THP echo never > /sys/kernel/mm/transparent_hugepage/enabled echo never > /sys/kernel/mm/transparent_hugepage/defrag # 设置CPU性能模式 cpupower frequency-set -g performance # 文件系统挂载参数(在/etc/fstab中修改) # /dev/sdb1 /var/lib/mysql ext4 defaults,noatime,nodiratime 0 0

5.2 sysbench参数配置的细节

sysbench是最常用的数据库压测工具,但默认参数往往不能反映真实业务场景。我通常会用以下几组配置分别测试:

测试模式表数量表大小并发数测试时长关键指标
oltp_read_only16100万行64/128/256300秒QPS、95%延迟
oltp_write_only16100万行64/128/256300秒TPS、95%延迟
oltp_read_write16100万行64/128/256300秒QPS、TPS、95%延迟
oltp_point_select16100万行512300秒QPS、95%延迟
oltp_range_select16100万行64300秒QPS、95%延迟

oltp_point_select是短连接高并发的典型场景,最能体现线程池的差异。oltp_range_select则考验优化器的范围扫描能力。每个测试跑三遍,取中间值,避免冷启动和偶发波动的干扰。

# sysbench准备数据 sysbench oltp_read_write \ --mysql-host=192.168.1.100 \ --mysql-port=3306 \ --mysql-user=test \ --mysql-password=test123 \ --mysql-db=testdb \ --tables=16 \ --table-size=1000000 \ --threads=16 \ prepare # 执行压测 sysbench oltp_read_write \ --mysql-host=192.168.1.100 \ --mysql-port=3306 \ --mysql-user=test \ --mysql-password=test123 \ --mysql-db=testdb \ --tables=16 \ --table-size=1000000 \ --threads=128 \ --time=300 \ --report-interval=10 \ run

5.3 监控指标不能只看QPS

QPS和TPS只是表面指标。真正决定用户体验的是延迟分布。我习惯用--report-interval=10让sysbench每10秒输出一次中间结果,观察延迟是否稳定。如果95%延迟在测试过程中持续上升,说明数据库内部有资源竞争或者锁等待。

另外,一定要同时采集数据库内部的监控指标。MySQL的performance_schema和MariaDB的performance_schema虽然同源,但采集的指标粒度不同。我通常关注这几个:

  • Innodb_row_lock_waits:行锁等待次数,反映并发冲突
  • Innodb_buffer_pool_wait_free:buffer pool等待空闲页的次数,反映内存压力
  • Threads_running:当前活跃线程数,反映线程池的工作状态
  • Created_tmp_disk_tables:磁盘临时表创建次数,反映排序和聚合的开销

这些指标在两边都可以通过SHOW GLOBAL STATUS获取,但MariaDB的Threads_running在线程池模式下含义不同,需要结合Threadpool_idle_threads一起看。

6. 实测中那些反直觉的发现

6.1 并发不是越高越好

很多人做压测时喜欢把并发数拉到几千,觉得这样才能测出极限。但实际上,当并发数超过数据库的最佳工作区间后,QPS反而会下降。MySQL社区版的最佳并发数通常在CPU核心数的2-4倍,MariaDB因为线程池的存在,最佳并发数可以更高,但也不是无限。

我实测下来,在32核机器上,MySQL 8.0的最佳并发数在128左右,超过256后QPS开始下降。MariaDB的最佳并发数在256左右,超过512后开始下降。这个拐点位置和硬件配置、查询复杂度都有关系,需要自己测。

6.2 索引选择性比索引数量更重要

在对比测试中,我发现一个现象:同样的索引设计,在MySQL和MariaDB上的执行计划可能完全不同。原因是两个优化器对索引选择性的估算模型不同。MySQL 8.0的直方图能更准确地估算选择性,而MariaDB依赖索引统计信息的采样。

这意味着,如果你从MySQL迁移到MariaDB,原来跑得好好的SQL可能突然变慢,因为MariaDB的优化器选了一个不同的索引。解决办法是重新收集统计信息,必要时用FORCE INDEX强制指定索引。但FORCE INDEX是下策,更好的做法是调整索引设计,让优化器自然选择正确的索引。

6.3 配置参数的默认值差异巨大

MySQL 8.0和MariaDB的默认配置参数差异很大。比如innodb_buffer_pool_size,MySQL 8.0默认是128MB,MariaDB 10.6默认也是128MB,但MariaDB的innodb_buffer_pool_instances默认值更高。innodb_flush_log_at_trx_commit两边默认都是1,但MariaDB在10.5之后引入了innodb_flush_log_at_trx_commit=2的优化模式,在某些场景下能提升写入性能。

最坑的是max_connections。MySQL 8.0默认151,MariaDB默认151,但MariaDB的线程池模式下,max_connections可以设得更高,因为线程池会复用线程。如果你直接把MySQL的配置复制到MariaDB,可能会发现连接数不够用。

注意:迁移配置时,不要直接复制my.cnf。两边的参数名虽然大部分相同,但有些参数在MariaDB中已经废弃或者含义变了。比如query_cache_size在MySQL 8.0中已经移除,在MariaDB中仍然存在但默认关闭。

7. 选型建议:没有最好,只有最合适

7.1 什么场景选MySQL 8.0

如果你的业务符合以下特征,MySQL 8.0是更稳妥的选择:

  • 需要窗口函数、CTE、降序索引等新SQL特性
  • 数据分布倾斜严重,需要直方图来优化执行计划
  • 从库延迟敏感,需要更细粒度的并行复制
  • 团队已经熟悉MySQL生态,不想承担迁移成本
  • 需要使用JSON_TABLE、正则表达式函数等高级功能

MySQL 8.0在优化器成熟度、复制延迟控制、SQL标准兼容性上确实领先。特别是直方图和Hash Join这两个特性,在复杂查询场景下优势明显。

7.2 什么场景选MariaDB

以下场景MariaDB更有优势:

  • 高并发短连接,需要线程池来降低上下文切换开销
  • 需要Galera Cluster做多主同步复制
  • 写密集型场景,愿意尝试MyRocks引擎
  • 需要Aria引擎来处理大量临时表操作
  • 预算有限,需要社区版就包含企业级特性

MariaDB的线程池和Galera Cluster是社区版就有的,而MySQL的对应功能需要企业版或者第三方方案。如果你的团队没有预算买企业版,MariaDB是更经济的选择。

7.3 迁移的隐性成本

不管从哪个迁到哪个,隐性成本都不低。SQL语法虽然大部分兼容,但存储过程、触发器、事件调度器的语法有差异。权限管理系统的表结构不同,迁移时需要重新映射。备份恢复工具不通用,mysqldump的输出格式虽然兼容,但mysqlpump和mydumper在两边支持程度不同。

我建议在迁移前,先用真实业务的SQL跑一遍兼容性测试。把慢查询日志打开,对比迁移前后的执行计划变化。对于执行计划变差的SQL,逐个分析原因,是统计信息问题、索引选择性问题、还是优化器行为差异。

8. 几个容易被忽略的调优细节

8.1 redo log大小对写入性能的影响

MySQL 8.0.30之后引入了innodb_redo_log_capacity参数,可以动态调整redo log的总容量。MariaDB目前还是用innodb_log_file_size和innodb_log_files_in_group来控制。redo log太小会导致频繁的checkpoint,写入性能下降。太大则会导致崩溃恢复时间变长。

我的经验值是:对于写入密集的业务,redo log总容量至少设置为buffer pool大小的25%。比如buffer pool是32GB,redo log至少8GB。MySQL 8.0可以动态调整,MariaDB需要重启才能生效。

8.2 临时表空间的配置差异

MySQL 8.0把临时表从ibtmp文件改成了会话级临时表空间,每个会话有自己的临时表空间。MariaDB仍然使用共享的ibtmp文件。这个差异在高并发临时表操作场景下影响很大。MySQL 8.0的会话级临时表空间减少了元数据锁竞争,但增加了文件句柄的消耗。

如果你的业务有大量GROUP BY、DISTINCT、UNION操作,MySQL 8.0的临时表处理更高效。但要注意监控Created_tmp_files指标,避免文件句柄耗尽。

8.3 连接池配置的配合

数据库的线程池和应用程序的连接池需要配合调优。如果应用连接池的maxActive设置得比数据库的max_connections还大,连接请求会被数据库拒绝。如果设置得太小,应用层会出现连接等待。

我的建议是:应用连接池的maxActive设置为数据库最佳并发数的80%左右。比如MariaDB的最佳并发数是256,应用连接池的maxActive设为200。留出余量给管理连接和监控连接。

# 以某连接池配置为例 spring: datasource: hikari: maximum-pool-size: 200 minimum-idle: 20 connection-timeout: 3000 idle-timeout: 600000 max-lifetime: 1800000

9. 压测之后,怎么解读数据

拿到压测报告后,不要只看QPS的绝对值。我通常按这个顺序分析:

第一,看延迟分布。95%延迟和99%延迟的差距如果很大,说明有长尾请求。长尾请求通常来自锁等待、磁盘IO抖动、或者GC暂停。对比两个数据库的长尾延迟,能看出哪个更稳定。

第二,看吞吐量随并发数的变化曲线。如果QPS在某个并发数之后急剧下降,说明达到了瓶颈。瓶颈可能是CPU、内存、磁盘IO、或者锁竞争。结合数据库内部指标定位瓶颈点。

第三,看错误率。压测过程中如果有连接超时、死锁、或者复制延迟告警,这些错误的类型和频率比QPS更有参考价值。

第四,看资源利用率。CPU利用率是否饱和、磁盘IO是否打满、网络带宽是否成为瓶颈。如果CPU利用率只有50%但QPS上不去,说明瓶颈不在CPU,可能在锁或者IO。

我习惯把两组测试的数据放在同一张表格里对比,用颜色标注差异超过10%的指标。这样一眼就能看出哪个数据库在哪个维度上有优势。

指标MySQL 8.0MariaDB 10.6差异
oltp_read_only QPS (128并发)4520041800-7.5%
oltp_write_only TPS (128并发)38003200-15.8%
oltp_point_select QPS (512并发)2850036200+27.0%
95%延迟 (oltp_read_write)12ms15ms+25.0%
从库延迟 (写入压力下)<1s5-10s显著

这张表是我在某次测试中的真实数据,硬件配置是32核64GB,NVMe SSD。可以看到,MySQL在写入和复制延迟上有优势,MariaDB在短连接高并发上有优势。没有哪个数据库全面胜出,选型就是根据业务权重来取舍。

10. 我踩过的几个坑

第一个坑:在虚拟机里做压测。虚拟机的CPU调度和磁盘IO都有额外的抽象层,压测结果波动很大。后来换成物理机,结果稳定多了。如果只能用虚拟机,至少确保两台虚拟机的配置完全一致,并且关闭内存气球和CPU热插拔。

第二个坑:忽略了autocommit的影响。sysbench默认是autocommit模式,每个事务提交一次。但真实业务可能用批量提交。我试过把--oltp-commit-rate设为100,两个数据库的TPS差距缩小了一半。所以压测参数要尽量贴近真实业务的事务模式。

第三个坑:没有预热buffer pool。第一次跑压测时,buffer pool是冷的,大量磁盘IO导致结果偏低。后来我每次压测前先跑一轮预热,让buffer pool加载热数据,再开始正式测试。

第四个坑:MariaDB的线程池在长查询下会拖累短查询。有一次测试中混合了长查询和短查询,MariaDB的线程池工作线程被长查询占满,短查询排队严重,95%延迟飙升。后来调整了thread_pool_max_threads和thread_pool_idle_timeout,情况才好转。

第五个坑:MySQL 8.0的直方图需要手动更新。创建直方图后,如果数据分布发生变化,直方图不会自动更新。需要定期执行ANALYZE TABLE ... UPDATE HISTOGRAM。我忘了更新,导致优化器选错了执行计划,查询性能突然下降。

这些坑的共同点是:文档里不会写,只有实际跑过压测、调过参数、踩过坑之后才能体会到。如果你正准备做数据库选型或者迁移,建议先在小规模环境里复现这些场景,心里有数了再上生产。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 16:21:57

基于DWA的动态避障代码MATLAB实现:从仿真到真机调参指南

简介&#xff1a;本资源为基于DWA&#xff08;动态窗口法&#xff09;的动态避障MATLAB实现代码&#xff0c;面向移动机器人路径规划方向的学习者与研究者&#xff0c;帮助理解并复现复杂环境下的实时避障策略。压缩包共5个文件&#xff0c;包含2个.m源码文件与3个.gif演示动图…

作者头像 李华
网站建设 2026/10/13 16:20:59

大模型应用三层架构:输入、处理与输出层的工程实践指南

1. 为什么“三层架构”是理解大模型应用的万能钥匙很多人第一次接触大模型应用开发时&#xff0c;会被各种新名词砸晕&#xff1a;RAG、Agent、Function Calling、思维链、多模态、工作流编排……每隔几周就冒出一个新概念&#xff0c;感觉永远追不完。但如果你把这些花样全部拆…

作者头像 李华
网站建设 2026/10/13 16:19:48

DCM4CHE图像显示实战:像素提取、窗宽窗位与多帧渲染

1. 从DICOM文件到屏幕像素&#xff1a;DCM4CHE图像显示到底在解决什么问题如果你在医疗影像相关的软件团队待过&#xff0c;大概率听过DCM4CHE这个名字。它是一个用Java写的开源医学影像处理工具库&#xff0c;核心能力是读写DICOM格式的文件——也就是CT、MR、DR这些设备拍出来…

作者头像 李华
网站建设 2026/10/13 16:18:46

桌面 AI 自动化 OpenClaw ,自然语言操控电脑实战分享

&#x1f539; 工具简述 OpenClaw 是一款广受开发者与办公人群认可的开源本地智能工具&#xff0c;凭借离线本地运行、可视化图形面板、全流程自主任务处理三大核心特点&#xff0c;积累了众多忠实用户。与普通对话类 AI 产品不同&#xff0c;它能够直接调用电脑软硬件操作权限…

作者头像 李华
网站建设 2026/10/13 16:18:40

工厂数据库管理系统实战:从架构设计到避坑指南

简介&#xff1a;工厂数据库管理系统课程设计完整文档以PDF格式收录&#xff0c;目标读者是正在学习数据库原理、面对课程设计任务或希望系统梳理数据库设计流程的高校学生。资源从某工厂的实际数据需求出发&#xff0c;覆盖数据项定义、数据流分析、E-R图绘制、关系模式转换、…

作者头像 李华