首先我们来看一个带排序的查询,点击工具栏的显示包含实际的执行计划。

1 SELECT * FROM AdventureWorks2008R2.Person.Person WHERE FirstName LIKE 'w%' ORDER BY 1

从执行计划里可以看出,SELECT运算符包含了内存授予(Memory Grant)信息(一般情况下不会出现,这里是因为我们的语句包含排序操作)。内存授予是KB为单位,是当执行计划中的一些运算符(像Sort/Hash等运算符)的执行,需要使用内存来完成——因此也被称为查询内存(Query Memory) 。

在查询正式执行前,查询内存必须被SQL Server授予才可以。对于提供的查询,查询优化器根据查询对象的对应统计信息来决定需要多少查询内存。现在的问题就是,当统计信息过期了,SQL Server就会低估要处理的行数。在这个情况下,SQL Server对于提供的查询还是会请求更少的查询内存。但当查询真正开始后,SQL Server就不能改变授予的内存大小,也不能请求更多的内存。查询必须在授予的查询内存里完成操作。在这个情况下,SQL Server需要把Sort/Hash运算符涌进TempDb,这就意味我们原先在内存里快速操作变成物理磁盘上慢速操作。SQL Server Profiler可以通过Sort WarningsHash Warning这2个事件来跟踪查询内存溢出(Query Memory Spills)。

很遗憾在SQL SERVER 2008(R2)没有提供这样的扩展事件来跟踪内存溢出事件。在SQL Server 2012里才有来解决这个问题。在这个文章里我会向你展示一个非常简单的例子,由于统计信息过期,你是如何产生内存溢出(Query Memory Spills)。我们来创建一个新的数据库,在里面创建一个表:

 1 SET STATISTICS IO ON
2 SET STATISTICS TIME ON
3 GO
4
5 -- Create a new database
6 CREATE DATABASE InsufficientMemoryGrants
7 GO
8
9 USE InsufficientMemoryGrants
10 GO
11
12 -- Create a test table
13 CREATE TABLE TestTable
14 (
15 Col1 INT IDENTITY PRIMARY KEY,
16 Col2 INT,
17 Col3 CHAR(4000)
18 )
19 GO
20
21 -- Create a Non-Clustered Index on column Col2
22 CREATE NONCLUSTERED INDEX idxTable1_Column2 ON TestTable(Col2)
23 GO

TestTable表包含第1列的主键,第2列的非聚集索引,第3列的CHAR(4000)列。接下来我们要用第3列来做ORDER BY,因此在执行计划里,查询优化器必须生成明确的排序运算符。下一步我会往表里插入1500条记录,表里数据的所有值在第2列会平均分布——在表里每个值只出现一次。

 1 -- Insert 1500 records
2 DECLARE @i INT = 1
3 WHILE (@i <= 1500)
4 BEGIN
5 INSERT INTO TestTable VALUES
6 (
7 @i ,
8 REPLICATE('x',4000)
9 )
10
11 SET @i += 1
12 END
13 GO

有了这样的数据准备,我们可以执行一个简单的查询,会在执行计划里好似用独立的排序运算符:

1 DECLARE @x INT
2
3 SELECT @x = Col2 FROM TestTable
4 WHERE Col2 = 2
5 ORDER BY Col3
6 GO

当我们在SQL Server Profiler里尝试跟踪Sort WarningsHash Warning这2个事件时,会发现跟踪不到。

你也可以使用DMV sys.dm_io_virtual_file_stats,看下num_of_writes列和num_of_bytes_written列,来看下刚才查询在TempDb是否有活动。当然,这个只有你一个人在使用当前数据库时有效。

 1 -- Check the activity in TempDb before we execute the sort operation.
2 SELECT num_of_writes, num_of_bytes_written FROM
3 sys.dm_io_virtual_file_stats(DB_ID('tempdb'), 1)
4 GO
5
6 -- Select a record through the previous created Non-Clustered Index from the table.
7 -- SQL Server retrieves the record through a Non-Clustered Index Seek operator.
8 -- SQL Server estimates for the sort operator 1 record, which also reflects
9 -- the actual number of rows.
10 -- SQL Server requests a memory grant of 1024kb - the sorting is done inside
11 -- the memory.
12 DECLARE @x INT
13
14 SELECT @x = Col2 FROM TestTable
15 WHERE Col2 = 2
16 ORDER BY Col3
17 GO
18
19 -- Check the activity in TempDb after the execution of the sort operation.
20 -- There was no activity in TempDb during the previous SELECT statement.
21 SELECT num_of_writes, num_of_bytes_written FROM
22 sys.dm_io_virtual_file_stats(DB_ID('tempdb'), 1)
23 GO

可以发现,查询执行前后没有任何改变。这个查询在我的系统里花费了1毫秒。

现在我们有了1500条记录的表,这就是说我们需要修改20% + 500的数据行才可以触发SQL Server来更新统计信息。我们来计算下,就可以知道我们需要需要修改800条行数据(500 + 300)。因此让我们来插入第2列值为2的799条数据。这样我们就改变了数据的分布情况,当SQL Server还是不会更新统计信息,因为还有一条数据没有更新,直到这条数据更新了才会触发SQL Server内部的统计信息自动更新!

我们再次执行刚才的查询:

 1 -- Check the activity in TempDb before we execute the sort operation.
2 SELECT num_of_writes, num_of_bytes_written FROM
3 sys.dm_io_virtual_file_stats(DB_ID('tempdb'), 1)
4 GO
5
6 -- Select a record through the previous created Non-Clustered Index from the table.
7 -- SQL Server retrieves the record through a Non-Clustered Index Seek operator.
8 -- SQL Server estimates for the sort operator 1 record, which also reflects
9 -- the actual number of rows.
10 -- SQL Server requests a memory grant of 1024kb - the sorting is done inside
11 -- the memory.
12 DECLARE @x INT
13
14 SELECT @x = Col2 FROM TestTable
15 WHERE Col2 = 2
16 ORDER BY Col3
17 GO
18
19 -- Check the activity in TempDb after the execution of the sort operation.
20 -- There was no activity in TempDb during the previous SELECT statement.
21 SELECT num_of_writes, num_of_bytes_written FROM
22 sys.dm_io_virtual_file_stats(DB_ID('tempdb'), 1)
23 GO

SQL Server就会把排序运算符涌进TempDb,因为SQL Server只申请了1K的查询内存授予(Query Memory Grant),它的估计行数是1——内存授予和刚才的一样。

DMV sys.dm_io_virtual_file_stats 显示在TempDb里有活动,这是SQL Server把排序运算符涌进TempDb的证据。

SQL Server Profiler也显示了Sort Warning的事件。

我们检查下执行计划里的估计行数(Estimated Number of Rows),和实际行数(Actual Number of Rows)完全不一样。

这里的执行时间花费了184毫秒,和刚才的1毫秒完全不一样。

现在我们往表里再插入1条记录,再次执行查询,一切正常,因为SQL Server会触发统计信息更新并正确估计查询内存授予(Query Memory Grant):

 1 -- Insert 1 records into table TestTable
2 SELECT TOP 1 IDENTITY(INT, 1, 1) AS n INTO #Nums
3 FROM master.dbo.syscolumns sc1
4
5 INSERT INTO TestTable (Col2, Col3)
6 SELECT 2, REPLICATE('x', 2000) FROM #nums
7 DROP TABLE #nums
8 GO
9
10 -- Check the activity in TempDb before we execute the sort operation.
11 SELECT num_of_writes, num_of_bytes_written FROM
12 sys.dm_io_virtual_file_stats(DB_ID('tempdb'), 1)
13 GO
14
15 -- SQL Server has now accurate statistics and estimates 801 rows for the sort operator.
16 -- SQL Server requests a memory grant of 6.656kb, which is now enough.
17 -- SQL Server now spills the sort operation not to TempDb.
18 -- Logical reads: 577
19 DECLARE @x INT
20
21 SELECT @x = Col2 FROM TestTable
22 WHERE Col2 = 2
23 ORDER BY Col3
24 GO
25
26 -- Check the activity in TempDb after the execution of the sort operation.
27 -- There is now no activity in TempDb during the previous SELECT statement.
28 SELECT num_of_writes, num_of_bytes_written FROM
29 sys.dm_io_virtual_file_stats(DB_ID('tempdb'), 1)
30 GO

嗯,这是个非常简单的例子,向你展示在SQL Server内部如何产生Sort Warning,其实一点也不神秘!

参考文章:

https://www.sqlpassion.at/archive/2011/10/19/query-memory-spills/

注:此文章为WoodyTu学习MS SQL技术,收集整理相关文档撰写,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错请点击下右下角的推荐,有了您的支持才能激发作者更大的写作热情,非常感谢!

ORACLE查询内存溢出的更多相关文章

  1. JDBC的批量查询报告内存溢出解决方法

    由于表中的数据过多(我的超过了50W+),查询select * from table ....报告内存溢出 Exception in thread "main" java.lang ...

  2. php大量数据 10M数据从查询到下载 【内存溢出,查询过慢】解决方案

    功能描述:做数据导出 功能分析:1.采用csv的格式,因为csv的格式比excel小 2. 3W条数据,100个字段需要全部导出 开始 直接查询 //此处使用的laravel框架,具体含义一看就懂 t ...

  3. php查询mysql返回大量数据结果集导致内存溢出的解决方法

    web开发中如果遇到php查询mysql返回大量数据导致内存溢出.或者内存不够用的情况那就需要看下MySQL C API的关联,那么究竟是什么导致php查询mysql返回大量数据时内存不够用情况? 答 ...

  4. 解决查询access数据库含日文出现“内存溢出”问题

    ACCESS有个BUG,那就是在使用 like 搜索时如果遇到日文就会出现“内存溢出”的问题,提示“80040e14/内存溢出”. 会出问题的SQL: where title like '%" ...

  5. Java内存溢出优化性能优化

    高性能应用构成了现代网络的支柱.LinkedIn有许多内部高吞吐量服务来满足每秒数千次的用户请求.要优化用户体验,低延迟地响应这些请求非常重要. 比如说,用户经常用到的一个功能是了解动态信息——不断更 ...

  6. java内存溢出的解决思路

    原文地址:https://www.cnblogs.com/200911/p/3965108.html 内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能 ...

  7. 如何写出让java虚拟机发生内存溢出异常OutOfMemoryError的代码

    程序小白在写代码的过程中,经常会不经意间写出发生内存溢出异常的代码.很多时候这类异常如何产生的都傻傻弄不清楚,如果能故意写出让jvm发生内存溢出的代码,有时候看来也并非一件容易的事.最近通过学习< ...

  8. java内存溢出和内存泄露

    虽然jvm可以通过GC自动回收无用的内存,但是代码不好的话仍然存在内存溢出的风险. 最近在网上搜集了一些资料,现整理如下: —————————————————————————————————————— ...

  9. 一次偶然的Java内存溢出引发的思考

    据说一次SQL查询返回太多数据,会引起服务器内存溢出. 不过,我现在碰到的情况是,调用一个Postgresql 存储过程,很复杂,那么在其中有很多raise notice这样的调试语句,如果碰巧有个死 ...

随机推荐

  1. node day1 login

    https://blog.csdn.net/weixin_33901641/article/details/85967847 vue之node.js的简单介绍 http://nodejs.cn/ ht ...

  2. 阻塞队列 BlockingQueue 详解

    转自:https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247487078&idx=2&sn=315f39b6d53 ...

  3. Google - Find Most People in Chat Log

    1. 给你一个chatting log file,format大概是这样的: A: bla B: bla bla C: bla bla bla 要你找出说话最多(看word number) 的K个人 ...

  4. 【SpringBoot】搜索框架ElasticSearch介绍和整合SpringBoot

    ========================12章 搜索框架ElasticSearch介绍和整合SpringBoot ============================= 加入小D课堂技术交 ...

  5. Ajax(Asynchronous JavaScript )and xml

    JavaScript的两种任务执行模式--同步(synchronous)和异步(Asynchronous) 同步模式 JavaScript的执行环境是单线程的,意味着一次只能执行一个任务,如果有多个任 ...

  6. Linux服务器同步Intetnet时间

    Linux服务器同步Intetnet时间 在使用linux服务器(虚拟机)的过程中,经常会发现使用一段时间后,linux服务器的时间和我的宿主机的时间不一致,而宿主机的时间确实是internet时间, ...

  7. vagrant The specified host network collides with a non-hostonly network!

    换个ip scripts\homestead.rb config.vm.network :private_network, ip: settings["ip"] ||= " ...

  8. jenkins中如何实现执行脚本时的变量共享

    1.主要是利用EnvInject Plugin插件,所以要首先安装插件,安装好后如下图: 2.然后在“增加构建步骤”中,插入一个“Execute Python script” 代码我用的python3 ...

  9. Linux(centos 7)配置tomcat8、JDK1.8、lighttpd、ngnix、mysql

    JDK 下载好后使用 rpm -ivh jdk-7u25-linux-x64.rpm 进行安装. 安装好后编辑 /etc/profile 文件,在末尾加上: 1 2 3 export JAVA_HOM ...

  10. jdk8 Metaspace 调优

    简介 jdk8的元空间的初始大小是21M,如果启动后GC过于频繁,请将该值设置得大一些. 更多Meatspace内容见<Metaspace 之一:Metaspace整体介绍(永久代被替换原因.元 ...