视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
SQLSERVER中KeyHashValue的作用(上)
2020-11-09 07:26:19 责编:小采
文档


SQLSERVER中KeyHashValue的作用(上) SQLSERVER中KeyHashValue的作用(下) 原文的标题是:SQLSERVER在索引下如何找到哈希值的随想 现在知道KeyHashValue的作用了,所以就改了标题~ 测试环境:SQLSERVER2005 开发者版 真的不好意思,我做实验的时候到最后还

SQLSERVER中KeyHashValue的作用(上)

SQLSERVER中KeyHashValue的作用(下)

原文的标题是:SQLSERVER在索引下如何找到哈希值的随想

现在知道KeyHashValue的作用了,所以就改了标题~

测试环境:SQLSERVER2005 开发者版

真的不好意思,我做实验的时候到最后还是没有找到这个问题的答案

问题是这样的:

当通过聚集索引查找和非聚集索引查找的时候,通过哈希码来匹配,然后找到相应记录的

既然通过哈希码来匹配,那么就需要一个hash bucket把所有索引页面的所有key/value全部加载到hash bucket

既然要全部加载到hash bucket就需要读取所有的索引页

我的测试脚本,我使用SET STATISTICS IO ON来测试是否有读取索引页的情况,但是到最后还是找不到规律

 1 --sql在聚集索引下如何找到哈希值的随想
 2 
 3 USE master
 4 GO
 5 --新建数据库IAMDB
 6 CREATE DATABASE SCANDB
 7 GO
 8 
 9 USE SCANDB
 10 GO
 11 
 12 
 13 
 14 --DROP TABLE clusteredtable
 15 --DROP TABLE nonclusteredtable
 16 
 17 
 18 --建立测试表
 19 CREATE TABLE clusteredtable(c1 INT IDENTITY(1,1), c2 VARCHAR (900))
 20 GO
 21 CREATE TABLE nonclusteredtable(c1 INT IDENTITY(1,1), c2 VARCHAR (900))
 22 GO
 23 
 24 
 25 --建立索引
 26 CREATE CLUSTERED INDEX cix_clusteredtable ON clusteredtable([C2])
 27 GO
 28 CREATE INDEX ix_nonclusteredtable ON nonclusteredtable([C2])
 29 GO
 30 
 31 
 32 --插入测试数据
 33 DECLARE @a INT;
 34 SELECT @a = 1;
 35 WHILE (@a <= 100)
 36 BEGIN
 37 INSERT INTO clusteredtable VALUES ( CAST(@a AS NVARCHAR(2))+replicate('a', 880))
 38 SELECT @a = @a + 1
 39 END
 40 
 41 
 42 DECLARE @a INT;
 43 SELECT @a = 1;
 44 WHILE (@a <= 100)
 45 BEGIN
 46 INSERT INTO nonclusteredtable VALUES ( CAST(@a AS NVARCHAR(2))+replicate('a', 880))
 47 SELECT @a = @a + 1
 48 END
 49 
 50 
 51 
 52 
 53 --查询数据
 54 SELECT * FROM clusteredtable ORDER BY [c1] ASC
 55 SELECT * FROM nonclusteredtable ORDER BY [c1] ASC
 56 
 57 
 58 CREATE TABLE DBCCResult (
 59 PageFID NVARCHAR(200),
 60 PagePID NVARCHAR(200),
 61 IAMFID NVARCHAR(200),
 62 IAMPID NVARCHAR(200),
 63 ObjectID NVARCHAR(200),
  IndexID NVARCHAR(200),
 65 PartitionNumber NVARCHAR(200),
 66 PartitionID NVARCHAR(200),
 67 iam_chain_type NVARCHAR(200),
 68 PageType NVARCHAR(200),
 69 IndexLevel NVARCHAR(200),
 70 NextPageFID NVARCHAR(200),
 71 NextPagePID NVARCHAR(200),
 72 PrevPageFID NVARCHAR(200),
 73 PrevPagePID NVARCHAR(200)
 74 )
 75 
 76 TRUNCATE TABLE [dbo].[DBCCResult]
 77 
 78 INSERT INTO DBCCResult EXEC ('DBCC IND(SCANDB,nonclusteredtable,-1) ')
 79 
 80 SELECT * FROM [dbo].[DBCCResult] ORDER BY [PageType] DESC 
 81 
 82 DBCC TRACEON(3604,-1)
 83 GO
 84 DBCC PAGE(SCANDB,1,,3) 
 85 GO
 86 
 87 checkpoint 
 88 DBCC DROPCLEANBUFFERS
  DBCC freesystemcache('all')
 90 GO
 91 -----------------------------------
 92 SET STATISTICS IO ON
 93 GO
 94 --聚集索引查找
 95 SELECT * FROM clusteredtable WHERE [c2]='18aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
 96 SET STATISTICS IO OFF
 97 GO
 98 
 99 
100 
101 (1 行受影响)
102 表 'clusteredtable'。扫描计数 1,逻辑读取 4 次,物理读取 2 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
103 
104 
105 
106 
107 ----------------------------------------------------------------------------------------
108 checkpoint 
109 DBCC DROPCLEANBUFFERS
110 DBCC freesystemcache('all')
111 GO
112 -----------------------------------
113 SET STATISTICS IO ON
114 GO
115 --索引查找 、RID查找 、嵌套循环
116 SELECT * FROM nonclusteredtable WHERE [c2]='17aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
117 SET STATISTICS IO OFF
118 GO
119 
120 
121 
122 (1 行受影响)
123 表 'nonclusteredtable'。扫描计数 1,逻辑读取 5 次,物理读取 1 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

View Code

聚集索引表的情况

非聚集索引表的情况

今天中午跟高文佳兄讨论了很长时间,我把关键讨论部分贴出来,大家参考参考,讨论的最后结果是:还没有解释到keyhashvalue字段实际的作用

感谢高文佳,头脑非常灵活

ō笑东风ō 9:27:10

对了 你那个hash的问题
ō笑东风ō 9:27:21

感觉你研究的方向不对

ō笑东风ō 9:28:05

当通过聚集索引查找和非聚集索引查找的时候,通过哈希码来匹配,然后找到相应记录的

桦少 9:28:53

请指教

ō笑东风ō 9:29:00

查找时不会使用hash来查找 因为hash值没有排序 无法最快查找

桦少 9:29:18

ō笑东风ō 9:29:20 应该是按照key来查找

桦少 9:33:52
ō笑东风ō 9:29:20
应该是按照key来查找
桦少 9:33:55
说说你的思路
ō笑东风ō 9:34:18
在索引里已经按照KEY排序 对吧
ō笑东风ō 9:34:34
而按照key排序 能最快找到想要的值
桦少 9:35:38
key排序有争议
桦少 9:35:42
又怎样
桦少 9:35:58
你还没有说清楚

ō笑东风ō 9:37:21
先说key查找的

我记得你有篇blog里说过hashjoin
桦少 9:39:38
哪三个经典连接没有写
ō笑东风ō 9:40:56
反正我的观点是key查找最快 无须再使用hash来定位
ō笑东风ō 9:41:26
而只有在hash join才会用到hash

笑东风ō 9:40:56
反正我的观点是key查找最快 无须再使用hash来定位
而只有在hash join才会用到hash

桦少 12:50:35
你想好啦吗
ō笑东风ō 12:51:57
嗯 我还是认为聚簇索引和非聚簇索引只存在key lookup
桦少 12:55:46
key lookup
的原理是什么
桦少 12:55:52
操作步骤是怎样的
桦少 12:55:56
你知道吗
ō笑东风ō 12:59:31
就是平衡树的原理
桦少 13:00:31

ō笑东风ō 13:00:38
使用平衡树 对上百万的INT值进行查找只需要4步
桦少 13:00:58
你查找的时候是否需要从磁盘读取索引页面到内存
ō笑东风ō 13:01:05

桦少 13:01:06
先不说他用多少步
桦少 13:01:08
性能有多好
桦少 13:01:26
从磁盘读取整个表的索引页面到内存
桦少 13:01:29
整个表
桦少 13:01:41
然后构成你说的所谓的平衡树

桦少 13:01:46

对吧

ō笑东风ō 13:02:06

桦少 13:02:52

我的问题就是这个

桦少 13:03:01

我用statictis io

桦少 13:03:10

看不出他会读取所有的索引页面

ō笑东风ō 13:04:25

一次seek 当然不会读取所有的页面

ō笑东风ō 13:04:48

只有scan才会读取所有页面

桦少 13:05:36

你还是不明白我问的问题

桦少 13:06:18

我说的是索引页

桦少 13:06:26

不是数据页

ō笑东风ō 13:06:45

索引也一样

ō笑东风ō 13:06:50

等等我给你做个demo

桦少 13:08:00

还有 ō笑东风ō 13:08:30

我现在有[BackupTestDB].[dbo].[TB1] 表中数据有245461条

桦少 13:08:43

你说用二叉树

ō笑东风ō 13:08:44

桦少 13:08:47

如果是这样

桦少 13:08:59

那么,keyhashvalue就没有意义了

ō笑东风ō 13:09:06

不是二叉树 是B树

桦少 13:09:15

桦少 13:09:23

b树 桦少 13:09:51

所以我从hash bucket的角度去思考

ō笑东风ō 13:10:22

hash桶这个概念是为了HASH JOIN才产生的

桦少 13:10:36

如果用b树,从第一个最左边的叶子节点开始从磁盘读取索引页面,组装一棵B树

ō笑东风ō 13:11:05

继续

桦少 13:11:23

如果是这样,keyhashvalue这个字段根本不需要

桦少 13:12:12

用到keyalue的都可以用桶这个概念啊

桦少 13:12:19

我觉得

桦少 13:12:47

我觉得不用死磕书本

桦少 13:13:06

死磕书本等于读死书
ō笑东风ō 13:14:28

桦少 13:14:49

桦少 13:15:58

我以前做实验的时候也看到过keyhashvalue全部为null

桦少 13:16:47

想写在SQLSERVER聚集索引与非聚集索引的再次研究(上)文章的最后面的

桦少 13:16:58

但是因为解释不了这个现象

桦少 13:17:01

最后没有写

桦少 13:19:42

为什麽我提出这个想法

桦少 13:19:52

其实我也是从性能和速度考虑的

ō笑东风ō 13:20:09

骚等

桦少 13:20:21

我的想法是:sqlserver有可能不用你刚才说的B树来找记录

ō笑东风ō 13:20:31

我怀疑这个HASHvalus是为了在seek时做比较用的

桦少 13:20:45

我画图给你看

桦少 13:22:42

当我用聚集索引查找的时候

桦少 13:23:11

key的字段是id

桦少 13:23:25

表中的字段是id

桦少 13:23:32

id是聚集索引字段

桦少 13:23:50

value是数据页面号

桦少 13:24:12

我要找id为9的那条记录

桦少 13:24:58

等一下

桦少 13:25:02

图还没画好

桦少 13:26:27

桦少 13:26:51

我需要将索引页69,88,102读取到内存

桦少 13:26:57

构成一棵b树

桦少 13:27:13

从左到右,从上到下查找

桦少 13:27:30

直至找到key为9那条记录

桦少 13:27:59

如果我select的是id为3的那条记录

桦少 13:28:17

我就不用读取索引页88,102读取到内存

桦少 13:28:23

只需要读取索引页面69

桦少 13:30:29

改一下,数据页面编号没有英文字母的

桦少 13:30:30

桦少 13:30:37

睡醒再聊

桦少 14:05:59

当我找id为9的记录的时候

桦少 14:06:16

我需要扫描索引页面69和索引页面88

ō笑东风ō 14:06:28

不需要扫面69

ō笑东风ō 14:06:42

只需要扫描88和102

桦少 14:06:43

说错了

桦少 14:06:54

是的

桦少 14:07:15

但是你也需要从磁盘读取索引页面69吧

桦少 14:07:22

组装出一棵b树

桦少 14:08:56

逐行逐行扫描 索引页面88和102里的记录
桦少 14:09:08

直到扫描到id为9的那条记录才停止

桦少 14:09:14

我的想法是

桦少 14:09:51

我的想法是:sqlserver有可能不用你刚才说的B树来找记录
ō笑东风ō 14:09:53

页面内扫描是这样

桦少 14:10:59

将所有索引页面的key列和value列放进去hash桶

桦少 14:11:07

ō笑东风ō 14:11:31

我刚在我本地跑了下你的脚本 SQL SERVER 2008 SP2

桦少 14:11:32

通过算法查找到id为9的那一条记录

ō笑东风ō 14:11:38

没有hashkey

ō笑东风ō 14:11:59

你的平台是什么

桦少 14:12:04

这样就不用扫描:索引页面88和102里的记录
桦少 14:12:32

这个过程当中,也是需要读取页面69,88,102

桦少 14:12:39 但是他就不用扫描

桦少 14:12:47 sql2005

ō笑东风ō 14:13:14

我到时有一种猜测

桦少 14:13:57

不然无办法解释keyhashvalue这个字段

ō笑东风ō 14:14:07

当比如较大字符串的时候 如果将字符串先hash后比较hash值 如果hash值相同 在比较字符串 这样效率会高一些

桦少 14:16:54

这种方法有一个缺点

ō笑东风ō 14:17:17

什么缺点

桦少 14:18:10

如果我select的是id为3的那条记录 他都会把所有索引页面读取到内存
桦少 14:18:20

而不像B树

桦少 14:18:36

桦少 14:18:43

因为他需要在桶里面找

ō笑东风ō 14:19:53

如果按照你所想的这样 无法快读定位某一个值的行

ō笑东风ō 14:20:03

必须扫描所有页

ō笑东风ō 14:20:17

除非对hashvalue进行排序

桦少 14:21:01

扫描所有索引页面

桦少 14:21:20

把所有索引页面里的keyhashvalue读取到桶里面

桦少 14:21:22

然后查找

ō笑东风ō 14:26:27

而且到hash桶后 还需要排序 如果不排序 需要全部遍历

桦少 14:27:33

嗯嗯

桦少 14:27:43

所以我的文章标题是:随想

ō笑东风ō 14:29:36

我知道有一种程序设计 是这样做的 就是对大字段做hash 然后对hash作为一列存储 对hash列建立索引

ō笑东风ō 14:30:04

这样做等值查询时能提高查询效率

桦少 14:32:58

高兄你是不是想偏了

桦少 14:33:09

不是只有大字段才有hash

ō笑东风ō 14:33:38

我只是说这是一种设计思路

桦少 14:33:39

ō笑东风ō 14:34:14

任何数据都可以被hash

桦少 14:34:37

不过这里好像说不过去

ō笑东风ō 14:36:10

而且林兄你看到的这些都是非叶子节点哈

桦少 14:50:22

当然是非叶子节点啦

桦少 14:50:33

叶子节点就是数据页面

ō笑东风ō 14:51:28

这个hashvalue应该跟seek无关

桦少 14:52:39

所以我才说无办法解释嘛

ō笑东风ō 14:59:25

嗯嗯

下载本文
显示全文
专题