ORACLE查询内存溢出
首先我们来看一个带排序的查询,点击工具栏的显示包含实际的执行计划。
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 Warnings和Hash 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 Warnings和Hash 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查询内存溢出的更多相关文章
- JDBC的批量查询报告内存溢出解决方法
由于表中的数据过多(我的超过了50W+),查询select * from table ....报告内存溢出 Exception in thread "main" java.lang ...
- php大量数据 10M数据从查询到下载 【内存溢出,查询过慢】解决方案
功能描述:做数据导出 功能分析:1.采用csv的格式,因为csv的格式比excel小 2. 3W条数据,100个字段需要全部导出 开始 直接查询 //此处使用的laravel框架,具体含义一看就懂 t ...
- php查询mysql返回大量数据结果集导致内存溢出的解决方法
web开发中如果遇到php查询mysql返回大量数据导致内存溢出.或者内存不够用的情况那就需要看下MySQL C API的关联,那么究竟是什么导致php查询mysql返回大量数据时内存不够用情况? 答 ...
- 解决查询access数据库含日文出现“内存溢出”问题
ACCESS有个BUG,那就是在使用 like 搜索时如果遇到日文就会出现“内存溢出”的问题,提示“80040e14/内存溢出”. 会出问题的SQL: where title like '%" ...
- Java内存溢出优化性能优化
高性能应用构成了现代网络的支柱.LinkedIn有许多内部高吞吐量服务来满足每秒数千次的用户请求.要优化用户体验,低延迟地响应这些请求非常重要. 比如说,用户经常用到的一个功能是了解动态信息——不断更 ...
- java内存溢出的解决思路
原文地址:https://www.cnblogs.com/200911/p/3965108.html 内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能 ...
- 如何写出让java虚拟机发生内存溢出异常OutOfMemoryError的代码
程序小白在写代码的过程中,经常会不经意间写出发生内存溢出异常的代码.很多时候这类异常如何产生的都傻傻弄不清楚,如果能故意写出让jvm发生内存溢出的代码,有时候看来也并非一件容易的事.最近通过学习< ...
- java内存溢出和内存泄露
虽然jvm可以通过GC自动回收无用的内存,但是代码不好的话仍然存在内存溢出的风险. 最近在网上搜集了一些资料,现整理如下: —————————————————————————————————————— ...
- 一次偶然的Java内存溢出引发的思考
据说一次SQL查询返回太多数据,会引起服务器内存溢出. 不过,我现在碰到的情况是,调用一个Postgresql 存储过程,很复杂,那么在其中有很多raise notice这样的调试语句,如果碰巧有个死 ...
随机推荐
- [CSL 的字符串][栈,模拟]
链接:https://ac.nowcoder.com/acm/contest/551/D来源:牛客网题目描述 CSL 以前不会字符串算法,经过一年的训练,他还是不会……于是他打算向你求助. 给定一个字 ...
- python第三方模块的导入
模块搜索路径 当我们尝试加载一个模块时,Python会在指定的路径下搜索对应的.py文件,如果找不到,就会报错: >>> import module1 Traceback (most ...
- BSS, DATA, TEXT, HEAP, STACK
BSS, block start segment, static memory, to store the global data which are not initialized. DATA, d ...
- oracle删除当前用户以及当前用户所有表、索引等操作
ORACLE删除当前用户下所有的表的方法 如果有删除用户的权限,则可以: drop user user_name cascade; 加了cascade就可以把用户连带的数据全部删掉.删除后再创建该用户 ...
- 集合总结五(Hashtable的实现原理)
一.概述 上一篇介绍了Java8的HashMap,接下来准备介绍一下Hashtable. Hashtable可以说已经具有一定的历史了,现在也很少使用到Hashtable了,更多的是使用HashMap ...
- 第二章 C#语法基础(2.1 C#语言的数据类型一)
C#的数据类型 [案例]本案例实现3位评委给一位选手评分,通过键盘输入各位评委的打分,通过屏幕输出该选手的平均分. [案例目的] (1)掌握变量的定义方式; (2)掌握常用的数据类型; (3)掌握数据 ...
- Windows下将文件打包压缩成 .tar.gz格式
1.下载 “7-ZIP”,安装完成后进入需要打包的文件夹 2. 右击选择“添加到压缩包” 3.压缩格式:tar 4. 得到.tar文件,将其打包 5. 压缩格式为:gzip 6. 得到tar.gz格式 ...
- Batch Close process
@echo offecho.rem Kill all chrome drivertaskkill /im chromedriver.exe /f pause
- DotNetBar创建的Ribbon、标签式多文档界面
1.创建一个form作为主窗体,继承自:DevComponents.DotNetBar.RibbonForm 设置属性:IsMdiContainer为true 2.创建一个form,作为子窗体,也继承 ...
- MySQL数据库中统计一个库中的所有表的行数?
今天公司两个远端的数据库主从同步有点问题,查看下wordpress库下所有表的表的条目? mysql> use information_schema;Database changedmysql& ...