MySQL和PostgreSQL的比较

5580阅读 5评论2016-11-09 skykiker
分类:Mysql/postgreSQL

1. 前言

MySQL和PostgreSQL是目前主流的两个开源关系数据库,同样都是开源产品,我们该如何选型呢?MySQL长期以来被认为是更加快速但支持的特性较少;而PostgreSQL则提供了丰富的特性经常被描述为开源版的Oracle。MySQL已经由于它的快速和易用变得非常流行,但PostgreSQL正得到越来越多来自Oracel或SQL Server背景的开发人员的追从。 但是很多假设已经变得过时或者不正确了,MySQL已经加入了很多高级特性,PostgreSQL也大大提高了它的速度。本文针对最新的MySQL 5.7 and PostgreSQL 9.5进行对比。

2. 约定

3. 综合比较

3.1 许可

3.2 开发模式

3.3 社区分支

3.4 版本更新

3.4.1 MySQL

MySQL的版本号由3组数字组成,比如5.7.1,每个数字的含义如下

第一个数字(5)是major version,描述了文件格式。所有MySQL 5发布拥有有相同的文件格式。
第二个数字(7)是release lever。和major version合在一起组成发布系列号(release series number)。
第三个数字(1)是发布系列号中的version number,每次新版本发布增长,大多数情况下一个系列中最近的version是最佳选择。  
对小的更新,增长最后的数字。当有大的新特性或者对前一版本有小的不兼容,增长第二个数字,如果文件格式发生了变更,增长第一个数字。 

以上翻译自MySQL的官方手册2.1.1 Which MySQL Version and Distribution to Install

MySQL的版本系列又分为开发版和General Availability (GA)版,只有GA版适合用于生产环境。

MySQL的目标是每18~24个月发布一个新发布系列的GA版本,最近几个发布系列的第一个GA版本的发布时间如下。

MySQL 5.0.15: 2005-10-19
MySQL 5.1.30: 2008-11-14
MySQL 5.5.8:  2010-12-03
MySQL 5.6.10: 2013-02-05
MySQL 5.7.9:  2015-10-21 

升级方法
* In-place升级: 停机,替换MySQL二进制文件,重启MySQL,运行mysql_upgrade。 
* 逻辑升级:mysqldump导出数据,安装新版MySQL,加载数据到新版MySQL,运行mysql_upgrade。

MySQL不支持跨发布系列升级,新旧版本跨越多个发布系列时,必须依次升级。比如如果想从5.5升级到5.7,必须先升级到5.6再升级到5.7。

参考MySQL官方手册2.11.1 Upgrading MySQL

3.4.2 PostgreSQL

PostgreSQL的版本号由3组数字组成,比如9.5.1。其中前两个数字构成了主版本号,第3个数字代表小版本号。小版本发布通常只是Bugfix,并且从不改变内部存储格式并且总是与之前的相同主版本的发布版本兼容。

主版本发布大约1年1次,维护周期为5年,小版本升级大约2个月1次。最近的几个主版本发布时间如下

PostgreSQL 9.0: 2010-09-20
PostgreSQL 9.1: 2011-09-12 
PostgreSQL 9.2: 2012-09-10
PostgreSQL 9.3: 2013-09-09
PostgreSQL 9.4: 2014-12-18
PostgreSQL 9.5: 2016-01-07 

升级方法
主版本升级(特性增强)

小版本升级(BugFix)
替换新版本数据库软件后重启PostgreSQL服务

参考PostgreSQL官方手册

3.4.3 评价

PostgreSQL的主版本更新的周期更短,平均一年一次;而MySQL的主版本升级平均2年一次。PostgreSQL小版本升级只是Bugfix不会增加新特性;而MySQL的小版本更新会增加和修改功能;并且,PostgreSQL提供的升级工具支持跨主版本升级,MySQL不可以。因此PostgreSQL的版本升级风险更低也更加容易实施。

3.5 流行程度

MySQL显然更加流行,有观点认为,PostgreSQL之所远不如MySQL流行,是一些因素共同作用的结果。

  1. 互联网行业兴起时期(2000年前后),MySQL作为一个轻量快速易用的开源数据库正好适配的互联网的需求,因而被大量使用。
  2. 互联网巨头的示范作用进一步促进了MySQL的普及。
  3. 早期的PostgreSQL在性能和易用性不如MySQL,比如:
    • PostgreSQL直到8.0(2005-01-19)才推出Windows版本。
    • PostgreSQL 8.3(2008-02-04)之前的版本在性能和可维护上尚不如人意(比如没有HOT更新)。
    • PostgreSQL直到9.0(2010-09-20)才推出流复制功能。

但PostgreSQL凭借其丰富的特性和日益提升的性能,用户量正呈现出快速上升的势头。并且在PostgreSQL接受程度较高的日本市场,PostgreSQL的用户量和MySQL相当,甚至超过。

参考:

3.6 用户案例

MySQL

MySQL在WEB类业务上非常流行,尤其在互联网行业几乎是标配。也有用于其它业务的案例。 可参考:http://www.mysql.com/customers/

PostgreSQL

PostgreSQL在各个行业均有应用案例。国内的知名用户包括:阿里巴巴,腾讯, 平安,移动,华为, 中兴,去哪儿, 斯凯, 云游, 顺丰,高德,探探等。一些用户看中了PostgreSQL的稳定可靠,复杂查询和GIS处理能力,特别是GiS,从一些实际用户的选型和反馈来看,PostgreSQL+PostGiS被认为是目前最有优势的开源GiS数据库方案。 可参考:

3.7 资料和文档

MySQL

MySQL由于非常流行,国内相关技术书籍和资料非常多。但缺少较新的中文手册,目前可以找到的最新的MySQL中文手册是MySQL 5.1。

PostgreSQL

早年PostgreSQL相关的中文技术资料和书籍匮乏,但目前已经比较丰富,并且每年都有新的中文PostgreSQL书籍诞生,PostgreSQL相关的技术交流和分享也很活跃。另外,PostgreSQL官方手册相当精良无论想入门还是深入均可受益,并且PostgreSQL中国用户会还在其官网上提供了中文版的手册。

4. 架构与实现

4.1 多线程vs多进程

MySQL是多线程模型,PostgreSQL是多进程模型。

多线程相比多进程的优点

多线程相比多进程的缺点

点评

客户端到数据库的连接通常使用连接池,并且数据库的最大连接数通常会被限制在1000以内,这可以大大缓解多进程创建删除和切换成本高的问题。所以PostgreSQL的多进程模型更占优势。(需要注意的是,由于PostgreSQL的多进程模型,在Windows上的性能受到了一定的影响。)

4.2 多引擎 vs FDW

MySQL的SQL层和存储层分离,支持多种存储引擎,例如InnoDB, MyISAM, NDB。PostgreSQL和绝大多数数据库一样是单存储引擎,但是PostgreSQL可以通过FDW支持其它的储存形式,比如csv,mysql,hadoop等。

MySQL多引擎的优点

MySQL多引擎的缺点

PostgreSQL FDW的优点

PostgreSQL FDW的缺点

注:MySQL也支持FDW,但目前只有mysql的FDW,不是主流。

4.3 binlog + redo log + undo log vs WAL + MVCC

MySQL

MySQL在SQL层通过binlog记录数据变更并基于binlog实现复制和PITR。另外在Innodb存储层有和其它数据库类似的redo log和undo log以支持事务的ACID。由于MySQL有两套日志,为了确保严格的持久性,以及保证2个日志中事务顺序的一致,每次事务提交需要刷3次盘,严重影响性能。这就是所谓的双1问题(sync_binlog=1,innodb_flush_log_at_trx_commit=1)

  1. Innodb Prepare
  2. binlog Commit
  3. Innodb Commit

虽然MySQL通过Group Commit的优化措施可以在高并发时大大减少了刷盘的次数,但双1的配置对性能的影响仍然存在。所以不少生产系统并未设置双1,冒了主备数据不一致的风险。

PostgreSQL

PostgreSQL和其它常见数据库(比如Oracle)不一样的地方在于,它只有redo log(PostgreSQL中称之为WAL)没有undo log。数据发生变更时,PostgreSQL通过记录在堆表的每个行版本的事务id,识别被更新或删除的旧数据。因为旧数据就在保存在堆表中,不需要undo log就可以支持事务的回滚。 PostgreSQL的MVCC实现方式的优点

  1. 事务回滚和故障恢复很快,在没有发生磁盘故障时很少出现宕机后不能启动的情况。
  2. 不会出现由于undo log空间不够而导致大事务失败的情况
  3. 不用写undo log所以日志写入量少

PostgreSQL的MVCC实现方式的缺点

  1. 堆表和索引容易膨胀,需要定期执行VACUUM 利用好HOT更新技术,可以减少表的膨胀,但索引的膨胀往往比表膨胀更严重。 恰当的配置auto vacuum通常可以有效的消除膨胀,但定期的表膨胀检查和处理也是不可少的。 PostgreSQL9.6和开发中的版本10,对表膨胀的处理都有重大改进。

4.4 binlog复制 vs 流复制

MySQL的复制传输的是SQL层的binlog记录,binlog记录的是数据的逻辑变更(SQL语句或基于行的数据变更),属于逻辑复制;PostgreSQL的复制传输的是WAL记录,WAL记录的是数据块的变更,属于物理复制。

binlog复制的优点

binlog复制的缺点

PostgreSQL流复制的优点

PostgreSQL流复制的缺点

4.5 数据存储

MySQL

MySQL的数据库名直接对应于文件系统的目录名,表名直接对应于文件系统的文件名。这导致MySQL的数据库名和表名支持的字符以及是否大小写敏感都依赖于文件系统。 MySQL的表定义存储在特别的.frm文件中,DDL操作不支持事务。 MySQL的所有Innodb表数据可以存储在单个.idb文件中,也可以每个表一个.idb文件(通过参数innodb_file_per_table控制),即使每个表一个.idb文件,同一个表的数据和索引都在这一个文件里。 MySQL的Innodb表的存储格式是Btree索引组织表,每个表必须有主键,如果创建表时没有指定主键,MySQL会创建一个内部主键。索引组织表的优点是按主键查询快,但数据插入时如果主键不是递增的,会导致Btree树大量分裂影响性能。

MySQL的二级索引中存储的行位置不是Innodb表中的物理位置而是Innodb表中的主键值。所以MySQL通过二级索引查找记录需要执行两次索引查找。并且如果主键太长,会过多占用二级索引的存储空间,所以有些场景下,放弃自然主键而采用额外的自增字段作为主键效果会更好。

PostgreSQL

PostgreSQL中的数据库对应于文件系统的目录,表对应于文件系统的文件,但目录名和文件名都是PostgreSQL内部的id号,不存在非法字符和大小写的问题。 PostgreSQL的每个表的数据和每个索引都存储在单独的文件中,并且文件操过1GB时,每个GB再拆分为一个单独的文件。

PostgreSQL中存储元数据的系统表和普通表的存储格式完全相同,这使得PostgreSQL很容易扩展,并且PostgreSQL的DDL操作也支持事务。

PostgreSQL对长度大于127字节的字段值采用被称为TOAST (The Oversized-Attribute Storage Technique)的技术进行存储。即将这些大字段切分为若干个chunk存储在这个堆表对应的toast表中,每个chunk占一行,最大2000字节,原始表中仅仅存储一个指针。并且PostgreSQL可能会对长度大于127字节的数据自动进行压缩。得益于TOAST技术,PostgreSQL能够很好的处理包含大字段的表。

5. SQL特性

MySQL早期的定位是轻量级数据库,虽然后来做了很多增强,比如事务支持,存储过程等,但和其它常见的关系数据库比起来SQL特性的支持仍比较弱,目前宽泛的SQL 99的子集。 PostgreSQL的定位是高级的对象关系数据库,从一开始对SQL标准的支持比较全面,目前支持大部分的SQL:2011特性。

关于SQL特性支持情况的对比,可以参考

 PostgreSQL | Oracle | DB2 | SQL Server | MySQL
支持的SQL特性       94           77      69       68        36
不支持的SQL特性     16           33      41       42        74 

下面是MySQL和PostgreSQL的SQL特性支持差异的说明

5.1 仅MySQL支持的SQL特性

MySQL支持而PostgreSQL不支持的特性有5个

5.2 仅PostgreSQL支持的特性

PostgreSQL支持而MySQL不支持的特性有62个,如下。

5.2.1 Queries

5.2.2 Regular Expressions

5.2.3 Constraints

5.2.4 Indexing

5.2.5 DML

PostgreSQL可以通过CASCADE联级TRUNCATE。

 postgres=# truncate tb;
    ERROR:  cannot truncate a table referenced in a foreign key constraint
    DETAIL:  Table "tbp" references "tb".
    HINT:  Truncate table "tbp" at the same time, or use TRUNCATE ... CASCADE.
    postgres=# truncate tb CASCADE;
    NOTICE:  truncate cascades to table "tbp"
    TRUNCATE TABLE 

另外发现,MySQL创建外键有个BUG,下面的语句表面上成功了,但实际上并没有创建FK。

 mysql> create table tbp(id int,pid int REFERENCES tb(id) on delete RESTRICT);
    Query OK, 0 rows affected (0.05 sec)

    mysql> show create table tbp;
    +-------+-----------------------------------------------------------------------------------------------------------------------+
    | Table | Create Table                                                                                                          |
    +-------+-----------------------------------------------------------------------------------------------------------------------+
    | tbp   | CREATE TABLE `tbp` (
      `id` int(11) DEFAULT NULL,
      `pid` int(11) DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
    +-------+-----------------------------------------------------------------------------------------------------------------------+
    1 row in set (0.00 sec) 

5.2.6 Data Types

5.2.7 DDL

5.2.8 Temporary Tables

5.2.9 Programming

5.2.10 Views

5.2.11 JOINs and Operators

5.2.12 Other

5.2.13 NoSQL Features

5.2.14 Security

6. 性能相关特性

6.1 索引

6.2 SQL优化

6.3 查询缓存

6.4 线程池/连接池

7. 参考

上一篇:关于MySQL的GTID和crash safe
下一篇:关于MySQL的分区索引

文章评论