SQL Server Insert 操作效率(堆表 VS 聚集索引表) (转)
2013-02-01 17:05
288 查看
SQL Server Insert 操作效率(堆表 VS 聚集索引表)
“SQL Server的Insert操作在堆表或者聚集索引表的时候,哪个效率更高?为什么高?”
之前有同事问过我这个问题,为了确保日志库的记录效率,于是我做了简单测试了,首先要先强调几点概念:
堆表:没有聚集索引的表,记录通过IAM页以及PFS页来确定哪页有空闲空间。
聚集索引表:有聚集索引的表,记录是根据聚集键值所在页的键值逻辑顺序维护的
Demo:如下
分别对堆表和聚集表进行5个并发线程,每个线程各10000次循环插入
1. 堆表测试
堆表Insert用时:34.127秒
2. 聚集索引表测试
聚集索引表Insert用时:22.885秒
结果:聚集索引的插入速度比堆表要快10秒以上(个人机器配置不同,时间差异也会高或低,我的本子性能较低)
分析
堆表插入:
每一次insert,总是被插入到表的任意可用空间上,通过IAM找到文件中的哪段区间属于目标表,通过PFS页找出这些区间内的哪些页面有可用空间,如果页面没有可用空间,需要通过GAM页和SGAM页查找将分配的某个表的可用区间。
聚集索引:
由于我的聚集键为自增id列,所以每次插入都将集中在最后一个数据页上。
总体来说:由于堆表插入的行的目标位置没有定义,因此确定在堆表中哪里放置行通常比在有聚集索引的表中放置行的效率低。
聚集索引表Insert的弊端
根据上面分析,聚集索引为自增列时,最后的数据页会成为集中insert的目标页,因此会成为热点,通时,SQL Server 使用闩锁,所以预测大并发insert操作会在最终页产生资源阻塞,实测确实如此:
(200个并发线程,每个线程执行100次insert操作)
执行过程中,查看等待资源情况
和预测情况一样,98个请求在等待闩锁资源。
那么,推断如果使用guid作为主键,插入时会分散各个数据页面,进而将热点页平铺开,这点确实有效果,但是拆分页的成本会相当的高,拆分页也是非常损伤性能的。
继续补充个情况,假如你需要长期大量insert操作,不如采用batch,效果会更快,将上面的脚本改为如下:
单次执行从原先的8秒降为3秒,有兴趣的朋友可以自己测试
原因简单说下,Insert操作时需要进行预写日志的步骤,每个单独的insert操作都要写一遍ldf文件,这样的性能很低,如果每5000条insert包含在一个事务中后提交,它把很多小的transaction合并成一个大的合适的 transaction来减少磁盘写操作,从而获得极大性能提升。Batch size究竟多大才是最佳的呢?这个取决您的机器,需要你自己测试。
http://www.cnblogs.com/SQLServer2012/archive/2013/01/30/2882815.html#2610890
“SQL Server的Insert操作在堆表或者聚集索引表的时候,哪个效率更高?为什么高?”
之前有同事问过我这个问题,为了确保日志库的记录效率,于是我做了简单测试了,首先要先强调几点概念:
堆表:没有聚集索引的表,记录通过IAM页以及PFS页来确定哪页有空闲空间。
聚集索引表:有聚集索引的表,记录是根据聚集键值所在页的键值逻辑顺序维护的
Demo:如下
分别对堆表和聚集表进行5个并发线程,每个线程各10000次循环插入
1. 堆表测试
--1. 创建一张堆表 create table Insert_Test (id int identity, name char(200)) go
堆表Insert用时:34.127秒
2. 聚集索引表测试
create table Insert_Test2 (id int identity primary key clustered, name char(200)) go
聚集索引表Insert用时:22.885秒
结果:聚集索引的插入速度比堆表要快10秒以上(个人机器配置不同,时间差异也会高或低,我的本子性能较低)
分析
堆表插入:
每一次insert,总是被插入到表的任意可用空间上,通过IAM找到文件中的哪段区间属于目标表,通过PFS页找出这些区间内的哪些页面有可用空间,如果页面没有可用空间,需要通过GAM页和SGAM页查找将分配的某个表的可用区间。
聚集索引:
由于我的聚集键为自增id列,所以每次插入都将集中在最后一个数据页上。
总体来说:由于堆表插入的行的目标位置没有定义,因此确定在堆表中哪里放置行通常比在有聚集索引的表中放置行的效率低。
聚集索引表Insert的弊端
根据上面分析,聚集索引为自增列时,最后的数据页会成为集中insert的目标页,因此会成为热点,通时,SQL Server 使用闩锁,所以预测大并发insert操作会在最终页产生资源阻塞,实测确实如此:
(200个并发线程,每个线程执行100次insert操作)
执行过程中,查看等待资源情况
select wait_type, count(*) as num_waiting_tasks, sum(wait_duration_ms) as total_wait_time_ms from sys.dm_os_waiting_tasks where session_id>50 group by wait_type order by wait_type
和预测情况一样,98个请求在等待闩锁资源。
那么,推断如果使用guid作为主键,插入时会分散各个数据页面,进而将热点页平铺开,这点确实有效果,但是拆分页的成本会相当的高,拆分页也是非常损伤性能的。
继续补充个情况,假如你需要长期大量insert操作,不如采用batch,效果会更快,将上面的脚本改为如下:
declare @i int set @i = 1 while @i <=10000 begin if @i %5000 = 0 begin if (@@TRANCOUNT>0) begin COMMIT TRAN BEGIN TRAN end end insert into Insert_Test2 select 'aaa' set @i =@i + 1 end if (@@TRANCOUNT>0) commit tran
单次执行从原先的8秒降为3秒,有兴趣的朋友可以自己测试
原因简单说下,Insert操作时需要进行预写日志的步骤,每个单独的insert操作都要写一遍ldf文件,这样的性能很低,如果每5000条insert包含在一个事务中后提交,它把很多小的transaction合并成一个大的合适的 transaction来减少磁盘写操作,从而获得极大性能提升。Batch size究竟多大才是最佳的呢?这个取决您的机器,需要你自己测试。
http://www.cnblogs.com/SQLServer2012/archive/2013/01/30/2882815.html#2610890
相关文章推荐
- SQL Server Insert 操作效率(堆表 VS 聚集索引表)
- SQL Server Insert操作中的锁
- MicroSoft SQL Server Manager 通过ODBC与VS进行连接操作示例程序
- SQL Server Insert操作中的锁
- 获取SQL-SERVER数据库insert into操作的主键返回值,SCOPE_IDENTITY
- 获取SQL-SERVER数据库insert into操作的主键返回值
- Mybatis + SqlServer 数据库操作insert 时获取当前主键值
- Sql Server CE的Pull操作
- 20080519 - 安装 Microsoft SQL Server 2000 时提示 创建挂起的文件操作
- sql serveri不用游标,适合于循环DML (update delete insert)的场合实例
- [转]VS 2013 连接数据库报错 未能加载文件或程序集 Microsoft.SqlServer.Management.Sdk.Sfc
- SQL2005的SQL Server Management Studio对大数据库操作超时解决方法
- Microsoft OLE DB Provider for SQL Server 错误 '80040e14' 全文操作运行失败。查询子句只包含被忽略的词。
- SQL Server 并行操作优化,避免并行操作被抑制而影响SQL的执行效率
- SQL2005的SQL Server Management Studio对大数据库操作超时解决方法
- SQL 游标使用的小例子,对于每行数据根据参数的不同进行Insert或Update操作.
- T-SQL操作几个数百万行到上千万行数据的表的过程中提高效率的几点体会
- MS-SQL中Insert操作插入的中文变成问号"???"的解决办法
- VS 2005中提供了显示SQL Server Report Service报表的控件:Report Viewer
- SQL Server nested loop join 效率试验