次の方法で共有


为什么生产环境会跑得比测试环境还慢

一套数据库应用系统,在测试环境里运行得非常理想,性能不错。生产环境中的服务器比测试环境要高档,价钱更贵,应该会跑得更快吧?可是在某些情况下,事与愿违,整个应用,或者应用的某个功能组件,在生产环境里跑得比测试环境反而还慢。如果项目实施的时候有这种事情发生,真是很尴尬,令人难以接受。是SQL
Server的问题么?还是应用程序的问题?有些时候,两者都不是。

 

我们先来探讨一个前提,为什么同一个应用,生产环境中的服务器比测试环境要高档,价钱更贵,就应该会跑得更快?一般来讲,服务器“高档”,体现在下面几个明显的特征。

  • CPU数目更多

             或者

  • 内存更大

             或者

  • 有磁盘阵列(SAN/RAID)

 

更多的系统资源,应该能帮助SQL
Server运行得更快。所以在绝大部分情况下,自然是高档的服务器跑得更快。那为什么有些用户会遇到生产环境跑得比测试环境还慢这样的“悲剧”呢?常见的原因有以下几种。

 

1.  CPU数目虽多,但是单个CPU的能力不一定强;而跑得慢的操作,需要消耗一定的CPU资源,同时SQL
Server又是用单线程完成的。

在OLTP类型的应用里,语句相对比较简单,操作的数据量比较少,SQL
Server会选择用单个线程完成。也就是说,每个操作,SQL
Server都是用单个CPU做的。CPU数目多,可以使得SQL
Server能够在同一个时间,处理更多的并发请求。但是对于单个操作的时间长短,则是由单个CPU的能力决定的。

现在服务器上的CPU,往往一个就包含4核、8核,而且常常设置成Hyper-Threading。结果是在Windows里看上去,CPU的数目很多。但是这些CPU都是逻辑CPU。它们单个的处理能力怎么样?最好能测试一下。

 

2.  内存虽多,但是SQL
Server用不上。

如果内存足够多,SQL
Server会把所要访问和处理的数据都缓存在内存里。所以更多的内存绝大多数时候能够提高性能。但是如果数据库比较小,所要访问和处理的数据还没有物理内存大的时候,再增加内存就没有什么意义了。不能说内存越大,SQL性能就一定越好。

 

3.  服务器是NUMA结构,而问题语句所需要的内存,大于单个NUMA节点的内存数

现在的很多服务器都采用了NUMA技术,将CPU和内存分组,每个NUMA节点包含一部分的CPU和内存。在节点内部,CPU访问内存会很快。但是如果CPU需要访问本节点之外的内存里的数据,会慢一点。

这样的设计,对于并发用户比较多的应用,是很有好处的。如果某个客户发给SQL
Server一个“变态”的请求,那基本只会把某一个NUMA节点忙死。而其他节点还能够正常处理其他用户的请求。这可以有效避免一个客户把整台服务器搞垮掉的事情发生。但是对于那些“变态”的语句(语句要访问和处理的数据量大于单个NUMA节点里的内存数目),由于得到的资源比非NUMA的机器要少,跑得有可能会慢一点。一般情况下,如果执行某个语句产生的执行计划不够好,有可能导致使用超过NUMA节点内CPU数目的并行;这种情况下,跨NUMA节点的并行,由于NUMA体系结构的原因(跨NUMA节点访问内存比访问节点内内存慢很多),可能导致语句执行速度更慢。在这种情况下,我们有两种解决方案,一是优化语句,以避免产生非常大的并行执行计划,二是我们可以设置MaxDOP以限制所有语句,即使真的需要也只在NUMA节点内进行并行。

 

4.  虽然有磁盘阵列,但是整体IO速度,不是那么强。

磁盘阵列是有很多配置选项的。如果配得不优化,最终的性能不一定就好。微软有个测试工具,叫SQLIO,可以模拟SQL
Server 做IO的方式,进行磁盘压力测试。如果怀疑磁盘有问题,可以用它来比较一下。

 

5.  对于单个的小型write操作,磁盘做得不是很快。

这是一个比较常见的问题。磁盘阵列,对于每次大批量的读写(比如copy一个大文件),一般能做得比较好。但是SQL
Server日志文件的操作方式,是很特殊的。为了保证事务的ACID,SQL
Server在commit一个transaction之前,必定要确保这个transaction所对应的日志记录,已经写入了物理磁盘。而且,日志记录,是严格按照时间前后,顺序写入日志文件的。基于这种特性,日志文件所在的磁盘读写,有这样的特征:

       A.SQL对日志文件基本只做写,很少做读

       B.同一个时间最多只有一个写请求。就算是磁盘来不及写,其队列也维护在SQL
       Server内部,在磁盘上不会看到disk queue length >1

       C.每次写请求,一般都不会很大。但是只有这次写完成了,SQL Server才会发出下一次的写请求

如果磁盘阵列在配置时,没有考虑到这一类操作,那往往是体现不出磁盘阵列的优势的。我不止一次遇到过,客户服务器上的速度,还比不过我自己的PC机。不过大部分客户没有遇到问题。因为大部分类型的数据库应用,查询的操作整体会比增、删、改要多。日志文件写入还不是系统瓶颈。

如果您的应用有非常频繁增、删、改动作,那就要检测一下存放日志文件的磁盘的日志记录吞吐能力了。比较简单的方法,是对一个字段不是很多的空表,用insert语句插入1万到10万条数据。Insert语句循环的外面,千万不要套”begin
tran”和”commit
tran”。这样每个插入动作都是一个小的事务,都会触发磁盘写操作。通常情况下,哪台机器的日志记录吞吐能力高,哪个先做完。

 

另外,还有以下几个常见的原因,也能导致同一个操作在不同的机器上速度不同。

1.  应用所访问的数据库里,数据量不一样,或者数据的值的分布情况不一样

2.  生产服务器上还有其他工作负荷,而测试服务器是没有其他人用的

3.  一台服务器应用跑在SQL
Server本地,一台服务器应用跑在SQL
Server远端

4.  由于硬件配置不同,SQL
Server在两台机器上选择了不同的执行计划

5.  一台服务器SQL
Server开着SQL
trace,一台服务器没有开

6.  两台服务器的Windows或者SQL
Server,在版本,或者某些配置上有不同

 

总之,问题的原因可能性还真不少。在遇到这种情况的时候,千万不要草率地将其归结于软件问题,或者是硬件问题。找到问题的根本原因,然后再做调整。这样才能使您的服务器物有所值,发挥最大的功效。