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这样的调试语句,如果碰巧有个死 ...
随机推荐
- github/gitee使用办法
github/gitee只要添加SSH公钥都是可以连接上的 比如把某个文件上传gitee 首先肯定要有权限 否则会一直提醒failed伤心心 接下来说常用语句 git config --list ...
- python------面向对象进阶 异常处理
一. 异常处理 try: pass except KeyError as e : #注3.x用as ,except KeyError, e ,2.x 用逗号. print("No this ...
- container and Injection
1.容器的历史 容器概念始于 1979 年提出的 UNIX chroot,它是一个 UNIX 操作系统的系统调用,将一个进程及其子进程的根目录改变到文件系统中的一个新位置,让这些进程只能访问到这个新的 ...
- oracle-------window安装
安装虚拟机(没难度,傻瓜装机) 然后右键左边 新建虚拟机 自定义------下一步------- 稍后安装操作系统------下一步 下一步 下一步 下一步 下一步,完成 然后启动,就可以启动一个系 ...
- Python小练习(一)
1:有一个列表,其中包括10个元素,例如这个列表是[1,2,3,4,5,6,7,8,9,0],要求将列表中的每个元素一次向前移动一个位置,第一个元素到列表的最后,然后输出这个列表.最终样式是[2,3, ...
- flask-appbuilder +echarts 展示数据笔记
pip install flask-appbuilder fabmanager create-app cd newapp fabmanager create-admin fabmanager run ...
- CSS之a标签锚点
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- centos7+nginx+rtmp+ffmpeg搭建流媒体服务器(保存流目录与http目录不要随意配置,否则有权限问题)
搭建nginx-http-flv-module升级代替rtmp模块,详情:https://github.com/winshining/nginx-http-flv-module/blob/master ...
- 初识docker-镜像
前言: 以前学习docker 都是零零碎碎的,只知道用,有些莫名其妙的报错自己也没有思路去解决,所以基于一本专业的介绍docker的书籍,重新开启学习,该博客就记录下我自己的学习过程吧. 1.dock ...
- tfs项目管理
同一个地址下有多个项目,但同一个文件只能映射一次.有两种方式: 1.只添加一次映射,即只给根目录添加映射,如下图,这样西面的具体的项目就不需要挨个添加了. . 2.每个项目挨个添加映射,使用这种方式要 ...