InfoQ Uber为什么放弃Postgres选择迁移到MySQL?( 六 )


4
MySQL的其他优势
到目前为止 , 我们介绍了Postgres和MySQL的磁盘架构 。 MySQL还有其他一些重要方面也让它的性能明显优于Postgres 。
缓冲池
首先 , 两个数据库的缓存方式不同 。 Postgres为内部缓存分配了一些内存 , 但是与计算机上的内存总量相比 , 这些缓存通常很小 。 为了提高性能 , Postgres允许内核通过页面缓存自动缓存最近访问的磁盘数据 。 例如 , 我们最大的Postgres副本有768GB的可用内存 , 但实际上只有25GB被用作Postgres的进程RSS内存 , 这样就为Linux页面缓存留出了700GB以上的可用内存 。
这种设计的问题在于 , 与访问RSS内存相比 , 通过页面缓存访问数据实际上开销更大 。 为了从磁盘上查找数据 , Postgres进程发出lseek和read系统调用来定位数据 。 这些系统调用中的每一个都会引起上下文切换 , 这比从主存储器访问数据的开销更大 。 实际上 , Postgres在这方面甚至还没有完全进行优化:Postgres并未利用pread系统调用 , 这个系统调用会将seek和read操作合并为一个系统调用 。
相比之下 , InnoDB存储引擎通过缓冲池实现了自己的LRU 。 从逻辑上讲 , 这与Linux页面缓存相似 , 但它是在用户空间中实现的 。 尽管InnoDB缓冲池的设计比Postgres的设计要复杂得多 , 但它具备一些优势:
可以实现自定义LRU 。 例如 , 可以检测出可能会破坏LRU的访问模式 , 并防止其造成更大问题 。
较少的上下文切换 。 通过InnoDB缓冲池访问的数据不需要进行用户/内核上下文切换 。 最坏的情况是发生TLB未命中 , 这些开销相对较小 , 可以通过使用大页面来缓解 。
连接处理
MySQL通过一个连接一个线程的方式来实现并发连接 。 这种开销相对较低 , 每个线程都有自己的栈内存和分配给特定连接的缓冲堆内存 。 在MySQL中使用10000个左右的并发连接 , 这种情况并不少见 , 实际上 , 在我们现有的某些MySQL实例上 , 连接数已经接近这个数字 。
但是 , Postgres采用的是一个连接一个进程的设计 , 这比一个连接一个线程的设计要昂贵得多 。 派生新进程比生成新线程占用更多的内存 。 此外 , 进程之间的IPC比线程之间的IPC也昂贵得多 。 Postgres9.2通过SystemVIPC原语实现IPC , 而不是使用轻量级的futex 。 futex比SystemVIPC更快 , 因为通常情况下 , futex不存在竟态条件 , 因此无需进行上下文切换 。
除了内存和IPC开销 , Postgres似乎也无法很好地支持大量连接 , 即使有足够的可用内存 。 我们在Postgres中使用数百个活动连接时遇到了大问题 。 Postgres文档建议采用进程外连接池机制来处理大量连接 , 但没有详细说明是为什么 。 因此 , 我们使用pgbouncer来处理Postgres的连接池 。 但是 , 我们的后端服务偶尔会出现bug , 导致它们打开的活动连接过多 , 从而延长了宕机时间 。
5
结论
在Uber早期 , Postgres为我们提供了很好的服务 , 但是随着公司规模的增长 , 我们遇到了伸缩性问题 。 现在 , 我们仍然保留了一些旧的Postgres实例 , 但大部分数据库都建立在MySQL之上(通常使用Schemaless层) , 或者在某些特殊情况下会使用像Cassandra这样的NoSQL数据库 。
英文原文
https://eng.uber.com/postgres-to-mysql-migration/
本周公众号宝藏文章
点个在看少个bug


推荐阅读