影响SQL Server数据库应用性能的几个常见因素 (2012/1/18)

2023-05-16

转自:http://blogs.msdn.com/b/apgcdsd/archive/2012/01/18/sql-server-2012-1-18.aspx


影响SQL Server数据库应用性能的几个常见因素

性能问题是困扰数据库用户的常见问题之一。经常会有人因为遇到性能问题,质疑SQL Server处理大型数据应用的能力。其实,作为一个在市场上经营了二十多年,出了好几代版本的数据库产品,SQL Server作为一个企业级数据库的能力,是毋庸置疑的。在实际应用中,数据量达到几百GB,甚至上TB级,并发连接数超过1、2千个,每秒钟处理的请求数量超过1000多个的SQL Server,现在已经很多了。在国内的一些大客户那里,我们也越来越多地看到这样的SQL Server。

那为什么有些SQL Server能跑得那么强劲,而很多用户的数据库还只有几十GB,就感觉跑不动了呢?在谈性能问题的常见原因之前,我们先谈谈SQL Server的几个和性能有关的重要特性。了解这些特性,对设计一个高效的数据库应用,是非常必要的。

1.      SQL Server要访问的数据,是一定要缓冲在内存里的

不管是要查询的数据,还是要修改的数据,SQL Server在运行客户端发过来的语句,处理这些数据之前,都要检查其访问的数据是否在内存中。如果不在内存中,SQL Server会先把存储数据的页面从磁盘调入内存,然后再做真正的数据处理。

数据处理完毕后,SQL Server不会马上把这段数据缓存丢弃。只要SQL Server不缺内存,先前访问过数据页面就会一直缓存在SQL  
Server进程的地址空间里。这样,如果下次有其他用户要访问同样的数据记录,SQL 
Server就可以马上在内存中进行处理,而不需要再到磁盘上去找。

这样的设计,可以最大程度的重用内存,提高SQL Server的处理速度。同时也决定了,SQL Server是一个非常喜欢大内存的应用程序。

2.      如果SQL Server没有空闲的内存,而用户需要访问新的数据,SQL会把以前缓存的数据挑选一部分从内存中清除,腾出空间来缓存新的数据

SQL Server不会无限制地申请内存。它会根据用户的设置,以及系统的内存数量,计算自己的最大内存数。如果当前的内存大小以及达到了这个上限,SQL就不会再向Windows申请更多的内存。这样的机理,可以保证SQL Server和Windows,以及运行在同一台机器上的其他应用程序和平共处。

当SQL Server的内存数已经达到最大值,内存空间已经缓存满各种各样的数据页面,而用户又要访问新的、还没有缓存在内存里的数据时,SQL 
Server会根据现有数据访问的频度,把最老的、最不经常被人访问的数据,从内存里清除,从而腾出空间来缓存现在客户要访问的新数据。

所以最理想的状态,是用户要访问的数据永远都缓存在内存里,SQL Server从来都不需要到磁盘上去找。这也是数据库性能最佳的情况。这时候SQL Server几乎从来不需要做磁盘读。

如果经常发生用户要访问的数据不在内存里的情况,SQL Server就会被迫不停地在内存和磁盘之间倒腾数据,性能会受到严重的影响。而这时候,你会看到SQL在经常地作磁盘读的动作。

3.      表格中数据的组织与访问,和聚集索引的选择密切相关

SQL Server的一个显著的特点,是表格里数据的存储,是按照聚集索引所在字段的值排序的。而非聚集索引是建立在聚集索引结构之上的。如果一张表格没有聚集索引,数据是按照堆的方式存储,没有任何顺序。

对于同样的数据量,SQL Server对一个有聚集索引的表格的管理,远远比没有聚集索引的表格要有效。绝大多数情况下,一张大表如果想要有良好的性能,就必须有一个合适的聚集索引。没有聚集索引,只加非聚集索引,也不能够达到优化的性能。

这是SQL Server的一个很重要的特点。

4.      在缺省的事务隔离级别下,同一条记录上的读操作和写操作是互斥的

SQL Server实现的是ANSI 标准的四个隔离级别。在Read Committed这个缺省的隔离级别上,读操作会申请S锁,修改操作会申请X锁,S锁和X锁互斥。所以同一条记录上不能同时进行读操作和写操作。

业界的有些其他数据库产品,缺省使用行版本控制方式实现事务隔离,如果一个用户在修改某一条记录、但是没有提交事务,而另一个用户要读同一条记录,它会让第二个用户读到第一个人修改之前这条记录的值。所以读操作和写操作是可以同时进行的。

这种版本控制的隔离级别,并发度当然比SQL Server的要高,读写操作之间产生阻塞的几率要小。可是它的事务隔离效果和SQL是不同的。���如,对于某个银行账户的查询,假设账户里原来有1万元,用户A开始一个事务,将账户里的1万元转出。在转出这个动作还没有完成时,用户B来查询账户余额。使用版本控制,用户B可以马上得到结果:1万元,但是这个结果其实很有可能已经过时。使用SQL Server,用户B必须等到用户A转帐完成才能查到余额,但是他得到的一定是一个最新的值。

这两种隔离级别其实反映了两种用户需求,不能讲哪种好,哪种不好。喜欢版本控制这种隔离级别的用户,如果想要在SQL 
Server里有同样的并发度,可以选择SQL里面的Snapshot Isolation Level(这个功能在SQL 2005里引入)。这样,SQL里的读写操作就不互斥了。

上面的这几个特征,决定了SQL Server的很多行为特点。如果跑在SQL Server上应用程序没有很好地按照上述的特征设计,就容易遇到各种各样的性能问题。

在现实应用中,SQL Server性能问题的常见原因有下面几点。

1.      索引的设计不够优化,从而迫使语句经常使用全表扫描的执行计划

如果没有好的索引帮助,SQL Server查询任何一条记录都有可能不得不把整张表都扫描一遍。这个在数据库比较小的时候影响不大,因为SQL能够把所有数据都缓存在内存里,就算是全表扫描也不会太慢。随着数据量增大,内存会放不下。全表扫描的负担会越来越重,到最后会严重影响SQL 
Server的整体性能。

这是一个很常见的SQL Server越跑越慢的原因。

2.      大的表格没有聚集索引,或者聚集索引建立在不合适的数据列上

对于没有聚集索引的表格,如果里面的记录数以万计或者更多,管理和查询都会增加很多开销。一个经验是,对于一个要经常使用的任何大表格,请建立聚集索引。索引所在的数据列,应该是一个或者几个重复性的记录不是很多的数据列。

业界的一些其他数据库可能对聚集索引这个概念不是非常强调,或者使用其他的方法管理表格。所以如果把非SQL 
Server的数据库迁移到SQL Server里,一定要重新检查索引结构,按照SQL Server的特点,调整索引设置。否则迁移后在SQL Server上得不到好的性能,是很正常的。

3.      用户需要经常访问的数据量,远大于机器的内存数

前面我们已经说过,SQL  
Server要操作的数据,是必须要缓存在内存里的。如果终端所有用户要经常访问的数据量的总和,远远超出SQL Server所拥有的内存数量,那SQL就会不得不在内存和磁盘间反复换页。这时候的SQL性能下降,可能就不是一两个数量级了。

用户需要经常访问的数据量的大小,通常跟以下几个因素有关。

a.用户发过来的语句定义

如果语句里有良好的条件约束,那数据量就能得到控制。如果条件约束性不强,那数据量就会随着表格里的记录数增长而增长。一个例子是,用户总是查询“今年的某某数据”。那在一年开始的时候,查询肯定很快。到年底时候的数据量可是一月份的12倍。那时候访问的数据量也很可能就是12倍。

b.SQL Server是否能够利用索引,使用Seek的方式找到数据,而不是扫描全表

如果没有好的索引,哪怕用户要访问的数据只是表格里的很小一部分,SQL可能也需要遍历整个表格。在这种情况下,也会出现表格越大,SQL跑得越慢的情况。如果索引设计地比较优化,SQL Server的数据访问量,不应该跟表格的大小有着绝对的关系。

c.应用程序的性质

数据库应用基本可以分两大类:OLTP类型的,和Data Warehouse类型的。前者会处理大量的小事务,比如病人挂号、超市结帐、仓库进货等。这种应用用户发过来的请求不会很复杂,每次要处理的数据量也比较小,但是要求的响应速度要很快。有可能0.5秒的等待都会带来性能问题。所以对这种应用,其访问的数据应该保证总是缓存在内存里。

Data Warehouse类型的应用主要是做数据分析和整理,经常是为了产生一些报表。这种应用主要以查询为主,肯定会访问大量的数据,会出现数据库越大,访问的数据量越多的情况。但是客户端可以容忍一定时间的等待。对于这种应用,其访问的数据不在内存里关系也不大。

对于OLTP类型的应用,应该尽量保证用户经常要访问的数据能够长时间地缓存在内存里。所以在设计应用逻辑和数据库时,就要有所考虑,控制用户访问的数据量,建立有效的索引,避免全表扫描。在管理上,也要有历史数据归档的机制,控制整个数据库的大小。如果证明用户需要访问的数据量就是比内存大,升级内存也是需要考虑的方案。

对于报表类型的应用,因为访问的数据量会很大,磁盘换页是难以避免的。当然良好的索引设计,对性能也会很有帮助。

要避免的情况,是同一个SQL Server既在运行OLTP类型的应用,又时不时地在运行报表类型的应用。它们两者会产生很大的相互干扰,对OLTP应用的响应速度会产生破坏性的影响,而且很难用调整数据库设计,或者升级硬件的方法轻易解决。

4.      数据库应用是从其他非SQL Server的架构下直接移植过来的

前面已经谈过,SQL Server缺省的事务隔离级别是Read Committed,和其他数据库可能不一样。另外,很多应用是通过调用数据库控件的接口来访问数据库的,而不是直接调用SQL语句或者存储过程。要达到同一个目的,程序里有很多种实现的方法。有些方法可能在其他数据库上性能会不错,但是到SQL 
Server上有可能就要换一个做法,或者换一种驱动。再加上不同的数据库系统对指令的写法可能有不同的建议,表格上的索引要求也不一样。所以要在SQL 
Server上实现良好的性能,就必须对迁移过来的应用在SQL Server平台上进行优化。没有测试、优化而直接移植过来的应用,常常不能发挥SQL Server的长处,性能欠佳。

 

 

By 微软亚太区全球技术支持中心 数据库技术支持组

对话微软原厂技术专家,手把手教会你 SQL Server性能调优: http://support.microsoft.com/gp/anxin_training2/zh-cn

给SQL Server来个全套体检,有病治病,无病强身:http://support.microsoft.com/gp/anxin_guide6/zh-cn

SQL Server技术支持大全,根治纠结您N久的疑难杂症:http://support.microsoft.com/gp/anxin_sql/zh-cn


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

影响SQL Server数据库应用性能的几个常见因素 (2012/1/18) 的相关文章

  • 模式更新后 jOOQ 生成的类的运行时验证?

    我用org jooq util DefaultGenerator在构建过程中生成 jOOQ 类来表示我的数据库模式 当应用程序运行时 架构预计会在应用程序不知情的情况下发生更改 此类更改可能与已生成的代码兼容 也可能不兼容 如何在运行时检测
  • sql查询将两列与一列连接起来

    我在 MS Access 2010 中有 2 个表 如下所示 USERS u id u name LOAN l id l from ref users u id l to ref users u id l amount Users u id
  • 获取家庭成员

    假设以下家庭 其构建架构是 create table PersonConn child int parent int insert into PersonConn values 1 2 insert into PersonConn valu
  • java mysql 准备好的语句

    我正在尝试使用 java 向数据库中进行简单的插入 它告诉我我的 sql 语法已关闭 但是 当我复制打印出来的字符串并将其放入 phpmyadmin 中的 sql 命令中时 它会正确执行该命令 并且我似乎无法弄清楚 java 中的字符串查询
  • 如何在Oracle中使用Timestamp_to_scn和Scn_to_timestamp?

    我的查询结果是这样的 select cast to date a start time mm dd yyyy hh mi ss pm as timestamp date of call ora rowscn from calling tab
  • 使用两个日期之间的随机日期时间更新每一行

    我有一个专栏叫date created我希望每一行保存一个随机日期 日期距当前时间为 2 天 我正在运行以下查询 但它会更新具有相同随机日期的所有行 我希望每一行都是随机的并且不相同 update table set date create
  • 如何将可视选择的文本通过管道传输到 UNIX 命令并将输出附加到 Vim 中的当前缓冲区

    使用 Vim 我尝试将在可视模式下选择的文本通过管道传输到 UNIX 命令 并将输出附加到当前文件的末尾 例如 假设我们有一个 SQL 命令 例如 SELECT FROM mytable 我想做如下的事情
  • 更改迁移中的自动​​增量值(PostgreSQL 和 SQLite3)

    我有一个托管在 Heroku 上的项目 想要更改表的自动增量起始值 我在本地使用 SQLite3 Heroku 使用 PostgreSQL 这是我在迁移中所拥有的 class CreateMytable lt ActiveRecord Mi
  • Oracle:使用SQL或PL/SQL查找动态SQL中的错误位置

    如何在 PL SQL 或 SQL 中找到动态 SQL 语句中的错误位置 从 SQL Plus 中 我看到了错误的位置 例如 无效的 SQL DML 语句 SYS orcl gt SELECT 2 X 3 FROM 4 TABLEX 5 TA
  • 在一个数据访问层中处理多个连接字符串

    我有一个有趣的困境 我目前有一个数据访问层 它必须与多个域一起使用 并且每个域都有多个数据库存储库 具体取决于所调用的存储过程 目前 我只需使用 SWITCH 语句来确定应用程序正在运行的计算机 并从 Web config 返回适当的连接字
  • 如何从 PostgreSQL 中的时间戳列值提取一天中的时间(或小时)?

    我正在尝试从 PostgreSQL 中的 时间戳 列中提取一天中的时间 这是我的做法 但是 太糟糕了 知道如何做得更好吗 SELECT date part hour date demande text hours date part min
  • PHP 通过 SSL 连接到 MS SQL

    我想要实现的目标非常简单 我想通过安全连接从 PHP 脚本连接到外部 MS SQL 数据库 然而 这已被证明是有问题的 到目前为止 经过三个小时的研究 我不知所措 客户端的平台是Ubuntu 这意味着我无法使用SQLSRV 安全连接已经在不
  • 使用 ADODB 连接从关闭的工作簿中检索数据。某些数据被跳过?

    我目前正在编写一些代码 可以通过 ADODB 连接访问单独的工作簿 由于速度的原因 我选择了这种方法而不是其他方法 下面是我的代码 Sub GetWorksheetData strSourceFile As String strSQL As
  • 如何 md5 所有列(无论类型如何)

    我想创建一个 sql 查询 或 plpgsql 它将 md5 所有给定的行 无论类型如何 但是 在下面 如果 1 为空 则哈希为空 UPDATE thetable SET hash md5 accountid accounttype cre
  • 为表中的每个组选择前 N 行

    我面临一个非常常见的问题 即 为表中的每个组选择前 N 行 考虑一个表id name hair colour score列 我想要一个结果集 对于每种头发颜色 都能得到前 3 名得分手的名字 为了解决这个问题 我得到了我所需要的Rick O
  • 自加入表

    我有一张像这样的桌子 Employee name salary a 10000 b 20000 c 5000 d 40000 我想获取所有工资高于A工资的员工 我不想使用任何嵌套或子查询 在采访中被问及并暗示是使用自连接 我真的不知道如何实
  • 月份增量查询

    我想通过添加 1 个月来更新数据库中的月份 但我不知道如何在以下存储过程查询中添加月份 我不擅长 sql 请检查它 ALTER PROCEDURE dbo ChangePassword password varchar 20 epasswo
  • 如何使用 LAMBDA 表达式在 LINQ 中执行 IN 或 CONTAINS?

    我有以下 Transact Sql 我正在尝试将其转换为 LINQ 并且很挣扎 SELECT FROM Project WHERE Project ProjectId IN SELECT ProjectId FROM ProjectMemb
  • SQL - != 'NULL' 的解释

    我的SSMS代码如下 Select top 50 From FilteredContact Where statuscode 1 and emailaddress1 NULL and telephone1 NULL and address1
  • SQL Server 上的语法错误

    这可能是一个愚蠢的语法错误 但我只是继续阅读我的程序 但我无法弄清楚我的错误在哪里 消息 156 第 15 级 状态 1 第 41 行关键字附近的语法不正确 为了 这是我的代码 alter procedure LockReservation

随机推荐