|
上周帮一个朋友排查问题,战高他的战高订单表在促销活动期间查询慢到离谱,一个简单的战高按时间范围拉取数据的语句跑了将近40秒。我让他把执行计划发过来一看,战高好家伙,战高全表扫描,战高索引压根没走。战高更离谱的战高是,那张表上明明建了索引。战高 这种情况我遇到过太多次了。战高SQL Server 的战高查询优化器不是万能的,它有时候会做出让人摸不着头脑的战高选择。比如统计信息过期了,战高优化器基于旧数据估算出行数只有几百条,战高觉得走聚集索引扫描更划算,战高结果实际数据量是几百万行。你看着执行计划里那个粗粗的箭头,就知道大事不妙。 索引不是建了就完事很多人建索引的逻辑是:这个字段经常出现在 WHERE 里,加个索引。听起来没毛病,但忽略了几个关键点。复合索引的字段顺序决定了它能不能被有效利用。比如你建了 我一般会建议把选择性最高的字段放在复合索引的最前面。但也不是绝对的,得看具体查询模式。如果 Status 只有三四个值,CreateTime 跨度又大,那 CreateTime 放前面更合理。这事儿没有万能公式,得看数据分布。 那个让人又爱又恨的 NOLOCK说到实战,不得不提 NOLOCK 这个提示。很多开发人员把它当万能药,哪里查询慢就往哪里加。加了之后确实快了,因为不等待锁了,直接读。但代价是可能读到脏数据,甚至出现数据重复或丢失的情况。我见过一个报表系统,因为大量使用 NOLOCK,月底对账时数字对不上,财务那边差点报警。 读已提交快照隔离(RCSI)是更优雅的方案。开启之后,读操作不会阻塞写操作,写操作也不会阻塞读操作,而且读到的是一致性快照。代价是 tempdb 的压力会增大。对于大多数 OLTP 系统来说,这个代价是值得的。改一行数据库配置就能解决大量锁等待问题,比到处加 NOLOCK 靠谱得多。 参数嗅探:同一个存储过程,两种命运有个案例我印象特别深。一个存储过程,传不同的参数值,执行时间能从200毫秒跳到15秒。原因就是参数嗅探。SQL Server 在第一次执行时根据传入的参数值生成了执行计划,然后把这个计划缓存起来。后面再来一个数据分布完全不同的参数值,它还是用旧计划,结果就是灾难。 解决办法有好几种。可以用 别忽视等待统计性能问题不一定出在查询本身。我习惯先看一眼等待统计, 有一次我遇到一个系统,CPU 一直跑满,查询本身都不慢,但整体吞吐量上不去。查等待统计发现 SQL Server 的性能调优,说到底就是理解它怎么思考、怎么执行、怎么等待。工具都在那里,DMV、执行计划、扩展事件,关键是你得知道什么时候该看哪个。经验这东西,踩的坑多了自然就有了。 |
