分布式数据库调优实践

数据库调优实践案例linux

数据库做为基础数据支撑层的核心部分,对于应用和平台总体性能表现有着决定性的影响。所以,数据库性能优化能够说是最考验DBA能力的工做了。本文咱们就由数据库内核专家来,以 SequoiaDB 5.0 内核的部分性能优化为例,带领各位数据库爱好者揭开数据库性能优化的“神秘面纱”。数据库

一般优化思路:缓存

提升数据库性能的方式有不少,总结起来从易到难无外乎以下三种:性能优化

  1. 最简单直观的是经过使用数据库提供的工具,找到SQL语句执行中消耗资源最大或耗时最长的部分,也即性能瓶颈。而后经过调整数据自己或数据库配置解决这些性能瓶颈。好比说发现数据分布不均匀,咱们能够经过切分数据(split)达到数据均衡(rebalance);再好比咱们发现某些网络时延较长,在肯定不是网络自己的问题后,咱们能够经过调整链接端口数和通信处理线程提升数据库消息处理能力;再好比单点磁盘IO过多,须要调整缓存或调整部分数据的分布。SequoiaDB提供了图形化的性能诊断工具SequoiaPerf,能够协助用户完成上述的调优。
  2. 业界经验证实,效果最明显,成本最低的方法实际上是SQL语句的调优,一般是经过理解分析访问计划,对比实际语句执行时的开销来判断语句是否优化。好比对比索引读和表读的个数判断否建立使用了合适的索引;对比访问计划的打分和时间执行开销来判断表/集合/索引的统计信息是否反映当前最新的状态;观察锁等待时间来判断系统中是否存在应用持锁时间过长阻塞其余应用而;对比join两边表的返回数据集以及使用的过滤条件判断使用join的类型是否合理。SequoiaDB 提供了完善监控功能,经过结合图形化的sequoiaPerf 与snapshot,用户能够相对容易的定位和实现SQL语句的调优。
  3. 前两种方式一般是DBA或应用开发者就能完成的任务,第三种是数据库内核的优化。这主要是数据库厂商在不断的实践中,经过各类相对底层的性能诊断工具,定位和优化数据引擎的性能。

内核调优网络

在数据库内核的调优中,开发人员一般会跑必定的workload或benchmark,使用操做系统或三方提供的工具,持续监控系统各种资源的使用状况,在高并发系统中,也会关注并发控制中使用的锁和原子变量带来的开销。下面咱们经过TPCC场景下的逐步优化SequoiaDB内核的过程,来了解咱们是如何使用工具来定位优化数据引擎的。并发

1. CPU usage高并发

咱们常使用两大神器观察CPU使用状况:top 和 perf。top能动态的显示linux 系统中各进程/线程以及内存使用的汇总信息。工具

/性能

以上图为例,咱们知道这台机器的CPU基本上被用满了,其中系统CPU占13%,用户CPU占81.7%。若是CPU出现过多的空闲,每每意味着系统要么还能够增长负载提升性能,要么有瓶颈致使CPU上不去,好比说并发很差,太多等待,串行化太多。在这个例子中,咱们没有看到等IO的状况,idle的比例也很是小,这都是好的现象。在CPU用满的状况下,优化系统也意味着要尽可能减小开销,让系统能尽量的跑多点任务。须要注意的是,若是系统CPU太高,意味着CPU不是在执行跟程序逻辑相关的指令,也能够理解为是overhead。根据以往的经验,这里系统CPU占比仍是偏高。使用线程模式,更进一步分析,咱们能够看到潜在的问题多是在系统调用,context switch和并发控制的mutex上。优化

至于更精确的定位,就要perf出马了。注意的是SequoiaDB 的代码编译时加入了debug symbol,这样会带来必定的性能损失,但可以极大的方便问题诊断和定位。

perf 是linux提供的一种基于event的性能搜集分析工具,可以分析CPU/内存/锁等资源的统计信息。perf自己已经提供了至关完整的文字的报表输出功能。

好比这里能看到system_call 也是跟sys_futex 相关的,一般是线程/进程同步共享资源互踩时形成的,还有部分是通信线程相关的。这样咱们的方向就能够从各类锁冲突入手。Perf也能提供锁冲突的信息。

为了简单直观的分析结果,咱们还使用火焰图(flame graph)来用图形的方式展示结果,以利用更快的发现问题。下面两张图分别提供了CPU和锁的使用统计:从中咱们发现的确有几处热的Latch/mutex。好比内存分配时使用共享内存池,这是会形成等锁的现象,咱们能够经过使用线程上独享的内存池解决;还要部份内部表的物理锁冲突严重,咱们经过增长锁的控制粒度减小冲突;再有就是尽可能减小锁内操做,好比内存分配,磁盘IO尽量的搬出热锁保护范围。经过一系列优化,咱们实现了5%左右的性能提高。

CPU 火焰图

锁火焰图

2. Memory allocation

内存是个好东西,如今计算机系统内存愈来愈大,软件也尽可能经过使用内存来实现空间换时间以提升系统相应速度。可是动态内存分配经常成为了高性能软件的性能瓶颈。咱们经过perf 来抓取系统内存的使用状况,并用火焰图显示出来:

这里明显看到的是不少动态内存分配发生在一个set的插入过程当中。Std::set内部使用的红黑树,每次结点的插入都要进行内存分配。为了减小系统内存的动态分配与回收,SequoiaDB实现了一整套本身的内存管理机制。最开始尽可能在线程预分配好的内存池上分配空间,这点和tcmalloc的原理很接近,这时的开销最小,内存事先已经从操做系统分配好了,并且本线程上分配是无锁的。可是若是线程内存池用完了,咱们会到一个共享的预分配好的内存池上分配,这时会多一个锁的开销。但这两处都用完了,咱们才向操做系统申请。从火焰图上看,咱们基本上都走到向操做系统分配的分支中了。针对这种状况,咱们优化了set的实现。当set中结点数量较小时,咱们用一个flat的较小的array存放数据,避免了动态内存分配。当结点数较多时,咱们再转化成树型结构以提升查找效率。可是咱们会提升线程上容许的缓冲池的大小,特别是小结构线程池大小。最终咱们避免了绝大多少的动态内存分配与回收,提高了系统性能。经过这块的分析,咱们也反过来帮助肯定那些query会用到大量数据,并优化对应的query。

3. Cache line misses

你们知道现代CPU的主频很是高,常有超过3GHz,执行指令速度很是快。可是咱们存储访问速度始终跟不上,高速的内存又很是贵,这就是现代CPU里有几级不一样速度不一样大小内存的缘由,常见的CPU内集成有L1,L2,L3级缓存。CPU执行时须要从缓存中获取指令和数据。在咱们编译程序的时候,编译器会试图优化程序,使得CPU能有效的重用或预提取数据和指令。当CPU在缓存中找不到合适的指令和数据时,就不等不从主存甚至磁盘上读取他们,这样的开销很是大,咱们用CPU cache line miss来衡量这中状况出现的频繁程度。

咱们仍是经过perf命令来搜集cache line miss的状况,

详细信息分解开来,最大一块是由monitor引发的

而后咱们检查monitor相关的代码,发现代码中有个switch语句公有14个分支,但最经常使用的一个分支放在了后面。咱们只须要将其挪到前面,咱们的miss就有显著降低。

还有另一种状况形成严重的cache line miss,就是使用原子变量,特别是频繁使用的原子变量。由于一旦该变量被变动了,全部cache 里的值都会变成无效,那么CPU使用时必定会碰到cache line miss。咱们经过分析代码逻辑,对于某些经常使用的确不须要时时精确的值,咱们能够在程序逻辑开始存为本地变量,避免过多的直接访问。对于一些只须要单线程访问的变量,咱们也避免使用原子变量。

小结:

上面咱们经过几个例子,为你们展示了如何经过系统工具进行数据库内核性能优化,一样的思路也能够适用于其余底层软件的开发调试。在实际的实践过程当中,除了使用合适的工具,更重要的是还要细心,有耐心和钻研的精神,一步步的下手,从现象中抽丝剥茧,找到根本缘由。

相关文章
相关标签/搜索