首页 > 编程知识 正文

分布式数据库优化,分布式数据库查询处理和优化算法

时间:2023-05-05 14:43:53 阅读:181382 作者:2841

数据库调优实践案例

数据库作为基础数据支撑层的核心部分,对整个应用和平台的性能表现起着决定性的影响。 因此,优化数据库性能可以说是最考验DBA能力的工作。 本文介绍数据库核心专家以 SequoiaDB5.0核心部分性能优化为例,揭开数据库性能优化的“神秘面纱”。

通常优化思路:有很多方法可以提高数据库的性能,但归纳起来,从容易到困难有以下三种。

最简单、最直观的方法是使用数据库提供的工具来查找执行SQL语句占用资源最多或最长的部分,即性能瓶颈。 然后,通过调整数据本身或数据库配置来解决这些性能瓶颈。 例如,在发现数据分布不均匀的情况下,可以通过分割数据(split )实现数据的均衡化,例如,在发现一部分网络的延迟较长的情况下,在判断为不是网络本身的问题之后, 可以通过调整连接端口数和通信处理线程来提高数据库消息处理能力。例如,如果某个单点磁盘的I/o过多,则需要调整缓存或调整某些数据的分布。 SequoiaDB提供图形化性能诊断工具SequoiaPerf,帮助用户完成上述调整。 业界经验表明,效果最明显、成本最低的方法其实是调整SQL语句,通常是了解和分析访问计划,并与实际语句执行时的开销进行比较,以确定语句是否已优化。 例如,比较索引读取和表读取的数量,以确定是否创建了适当索引的访问计划评分和运行时间成本,从而确定表/集合/索引统计信息是否反映了当前最新的状态; 观察锁定等待时间,以确定APP到系统的应用程序的锁定时间是否过长而阻塞了其他APP应用程序; 通过将join两侧表的返回数据集与要使用的过滤条件进行比较,可以确定使用join的类型是否有效。 SequoiaDB提供了高级监视功能,通过将图形sequoiaPerf与快照结合起来,用户可以相对轻松地定位和调整SQL语句。 前两种方法通常是DBA或APP应用程序开发人员可以执行的任务,第三种方法是数据库内核优化。 它主要是在数据库供应商持续实践的过程中,使用各种比较底层的性能诊断工具来定位和优化数据引擎的性能。

对于内核调优数据库内核的调谐,开发者通常要跑一定数量的workload或benchmark,使用操作系统或三方提供的工具来实现系统各种资源的高并发系统还关注并发控制中使用的锁定和原子变量带来的开销。 让我们看看如何通过在TPCC方案中逐步优化SequoiaDB内核的过程,使用工具来查找优化的数据引擎。

1. CPU usage

我们经常使用两个大神器观察CPU的使用情况。 顶级和顶级。 top动态显示linux系统中每个进程/线程和内存使用的摘要信息。

//

以上图为例,我们知道这台机器的CPU基本上已满。 其中,系统CPU占13%,用户CPU占81.7%。 如果有太多的CPU可用空间,在大多数情况下,系统可以增加负载以提高性能,或者遇到瓶颈,CPU无法启动。 例如,并发运行不顺利、等待太久、序列化太久等。 在这个例子中,看不到等待IO,idle的比例也非常少,这是一个好现象。 如果CPU已满,则优化系统意味着将开销降至最低,并允许系统执行尽可能多的任务。 请注意,如果系统的CPU太高,则可以将其理解为overhead,而不是执行与程序逻辑相关的指令。 根据以往的经验,这里系统CPU的占有率很高。 通过使用线程模式进行进一步分析,可以看到潜在的问题可能出在系统调用、上下文交换机或并发mutex上。

关于更准确的对位,perf将参选。 请注意,在编译SequoiaDB的代码时内置了调试symbol。 虽然这会造成一定的性能损失,但可以大大方便地诊断和定位问题。

perf是linux提供的基于event的性能收集和分析工具,用于分析资源统计信息,如CPU/内存/锁。 perf本身已经提供了相当完整的文本报告输出功能。

例如,这里的system_call也涉及sys_futex,通常发生在线程/进程同步共享资源互相踩踏时,部分涉及通信线程。 这样我们的方向可以从各种各样的摇滚冲突开始。 Perf也可以提供锁定冲突的信息。

为了方便直观地分析结果,我们使用flame graph以图形方式表示结果,以便更快地发现问题。 以下两幅图分别显示了CPU和锁的使用统计信息。 从那里,我发现确实有几个热的Latch/mutex。 例如,在内存分配过程中使用共享内存池会导致等时现象,可以通过使用线程上独占的内存池来解决,而且某些内部表的物理锁冲突严重,通过增加锁的控制粒度来避免冲突通过一系列的优化,实现了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。我们通过分析代码逻辑,对于某些常用的确不需要时时精确的值,我们可以在程序逻辑开始存为本地变量,避免过多的直接访问。对于一些只需要单线程访问的变量,我们也避免使用原子变量。

 

小结:

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

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。