哈希算法在数据库中的用途
转载自:http://blog.sina.com.cn/s/blog_624f972301013bau.html
1 上次提到了使用哈希算法做密码的单向加密,其实哈希算法的用途还有很多,比如传输文件时做校验,对数据进行签名等。除了这些,在数据库里面还有什么地方可以用到哈希算法呢?最重要的一个是哈希查找,由于哈希算法能够很好的把数据做比较均匀的分布,所以哈希查找的速度要比B+ Tree查找的速度要快。哈希查找的时间复杂度是O(1),而B+ Tree的时间复杂度是O(h)。当然,B+ Tree的优势是在范围查找,不然也不会成为关系型数据库的基本算法了。 2 SQL Server在做联接(JOIN)的时候,如果两个表都不是很小,而且没有在关联列上排序,就很可能使用哈希联接。哈希联接使用的就是哈希查找,它的性能并不差,但是要注意的一点是,哈希联接需要先构造一个哈希表,而哈希表需要消耗不小的内存空间,如果数据库服务器的内存不足的话,SQL Server就只好使用“优雅的哈希联接”(Grace HASH JOIN)或者递归哈希联接(Recursive Hash Join),这样性能就会受到影响了。在设计数据库的时候,我们应该注意建立/更新适当的索引和统计信息(STATISTICS),以便SQL Server可以准确的估计联接的输入大小,以便选择正确的算法。 3 在使用哈希联接的时候,SQL Server的查询优化器会选择算法,通常我们并不需要做任何的指令,编写任何代码。但是在其它一些需要用到哈希算法的时候,就需要认真的选择算法,甚至可能需要自己写哈希算法的代码。 4 在业务系统中,可能生成的业务键(Business key)会比较长,例如某电商网站的订单号会类似这样:202210782169AC7G。这样的业务键如果用作主键的话,会占据16字节,显得有点浪费。如果只是单一系统使用,可以考虑用自动增量的数字作为主键。但是如果在多个系统中使用,比如在一个数据仓库系统中,使用自动增量作为代理键(Surrogate key),就必须在处理事实表的ETL过程中,用新的自动增量替换掉订单表和详单表中的订单号,这会带来两个大表的JOIN,是一个相当耗时的操作。在这种情况下,我们就可以考虑使用哈希算法来生成代理键,只需要在订单表和详单表都使用同样的哈希算法,就可以保证得到的代理键是可以正确联接的。 5 熟悉哈希算法的人都知道,哈希算法是可能产生碰撞(collision)的,简单的说,就是多个不同的输入可能得到一个相同的输出。如果我们使用哈希算法生成代理键,就可能会出现错误。一种解决方案是用不同的桶(bucket)来存放这些哈希值。不过对于数据仓库系统来说,可以考虑忽略碰撞。毕竟数据仓库的主要用途是统计分析,而不是需要精确对账。 6 SQL Server内置的哈希算法,MD2、MD4 和 MD5 输出是 16 个字节,SHA 和 SHA1 输出是 20 个字节。如果用来做这个用途的话,显得就得不偿失了。我们可以考虑编写其它的哈希函数,输出更短,速度也更快,不过要注意的是,输出越短,碰撞的可能性就越高。 7 下面介绍一个哈希算法的代码,这个算法叫FNV1a,(Fowler-Noll-Vo variant "1a"),速度很快,碰撞也较低,适合用来对较短的字符串做哈希。这段代码里面给出了三个不同长度的结果,分别是16位(2字节,SMALLINT)、32位(4字节,INT),64位(8字节,BIGINT),在实际应用时请根据对碰撞率和性能的需求选用。该代码C++完全来自www.sqlservercentral.com。 8 .Net C++代码 9 using System; 10 using System.Data; 11 using System.Data.SqlClient; 12 using System.Data.SqlTypes; 13 using Microsoft.SqlServer.Server; 14 using System.Text; 15 16 public partial class UserDefinedFunctions 17 { 18 19 static readonly ulong prime64 = 1099511628211; 20 static readonly ulong offset64 = 0xcbf29ce484222325; 21 static readonly uint prime32 = 16777619; 22 static readonly uint offset32 = 2166136261; 23 24 [SqlFunction(IsDeterministic = true, IsPrecise = true, DataAccess = DataAccessKind.None)] 25 public static SqlInt64 xf_GetHash64(SqlString value) 26 { 27 return (SqlInt64)HashFNV1a_64((string)value); 28 } 29 30 [SqlFunction(IsDeterministic = true, IsPrecise = true, DataAccess = DataAccessKind.None)] 31 public static SqlInt32 xf_GetHash32(SqlString value) 32 { 33 return (SqlInt32)HashFNV1a_32((string)value); 34 } 35 36 [SqlFunction(IsDeterministic = true, IsPrecise = true, DataAccess = DataAccessKind.None)] 37 public static SqlInt16 xf_GetHash16(SqlString value) 38 { 39 return (SqlInt16)HashFNV1a_16((string)value); 40 } 41 42 private static long HashFNV1a_64(string value) 43 { 44 ulong hash = offset64; 45 byte[] bytes = Encoding.UTF8.GetBytes(value.ToLower()); 46 47 for (int i = 0; i < bytes.Length; i++) 48 { 49 hash = (hash ^ bytes[i]) * prime64; 50 } 51 return (long)(hash - long.MaxValue); 52 } 53 54 private static int HashFNV1a_32(string value) 55 { 56 uint hash = offset32; 57 byte[] bytes = Encoding.UTF8.GetBytes(value.ToLower()); 58 59 for (int i = 0; i < bytes.Length; i++) 60 { 61 hash = (hash ^ bytes[i]) * prime32; 62 } 63 return (int)(hash - int.MaxValue); 64 } 65 66 private static short HashFNV1a_16(string value) 67 { 68 uint MASK_16 = (((uint)1 << 16) - 1); 69 uint hash = offset32; 70 byte[] bytes = Encoding.UTF8.GetBytes(value.ToLower()); 71 72 for (int i = 0; i < bytes.Length; i++) 73 { 74 hash = (hash ^ bytes[i]) * prime32; 75 } 76 77 hash = (hash >> 16) ^ (hash & MASK_16); 78 return (short)(hash - short.MaxValue); 79 } 80 }; 81 编译该代码为dll后,将其封装为CLR函数。 82 CREATE ASSEMBLY HashFNV1a FROM 'C:\Documents\Visual Studio 2010\Projects\SQLCLR\SQLCLR\bin\Release\HashFNV1a.dll'; --请修改为你编译出来的dll完整路径 83 CREATE FUNCTION dbo.xf_GetHash16 (@str nvarchar(4000)) RETURNS SMALLINT 84 AS EXTERNAL NAME HashFNV1a.UserDefinedFunctions.xf_GetHash16; 85 CREATE FUNCTION dbo.xf_GetHash32 (@str nvarchar(4000)) RETURNS INT 86 AS EXTERNAL NAME HashFNV1a.UserDefinedFunctions.xf_GetHash32; 87 CREATE FUNCTION dbo.xf_GetHash64 (@str nvarchar(4000)) RETURNS BIGINT 88 AS EXTERNAL NAME HashFNV1a.UserDefinedFunctions.xf_GetHash64; 89 GO 90
http://www.zhihu.com/question/24421843
数据仓库中,什么是business key?跟surrogate key, primary key, foreign key 有什么区别联系?
维度建模理论中,维表里使用原业务中的主键作为主键就是业务键(Business Key 跟业务有关的);建立新的主键就是代理键(Surrogate Key 一般跟业务无关,跟原来的业务主键无关并,所以一般使用自增列作为Surrogate Key)。数据库理论中主键(primary key)是本表中是唯一的、不可为空的标识;外键(foreign key)和另一张表的主键关联。
Nvarchar可以设的最大长度是多少?想把Nvarchar(max)作为主键存文件地址之类的字符串,但是Nvarchar(max)不可以。有什么办法可以解决么?
https://social.technet.microsoft.com/Forums/zh-CN/aa7af274-d3a7-4077-969e-15113b33d5a7/nvarcharnvarcharmaxnvarcharmax?forum=sqlserverzhchs
Nvarchar可以设的最大长度是多少?想把Nvarchar(max)做主键,用来存储较长的文件地址信息,但是sql server不可以。有什么办法可以解决么?难道要用int类型作为主键么?我担心的主要是插入的重复问题,c#程序将数据集合向数据库的表格插入时会产生不重复的id和重复的地址。
另外如何判断插入的字符串超过了Nvarchar(max)的最大长度
嗨,
Pkey 最大長度為900byte,一般來說很少會使用那麼長的資料當作Pkey,
或許你可以改用Surrogate Key 試試看,及建立一個唯一值來讓資料庫索引,但不要顯示給使用者看。建立新的主键比如用自增列建立一个新的跟业务无关的主键叫代理键Surrogate Key
---------------------------------------------------------------
比如在一个数据仓库系统中,使用自动增量作为代理键(Surrogate key),就必须在处理事实表的ETL过程中,用新的自动增量替换掉订单表和详单表中的订单号,这会带来两个大表的JOIN,是一个相当耗时的操作。在这种情况下,我们就可以考虑使用哈希算法来生成代理键,只需要在订单表和详单表都使用同样的哈希算法,就可以保证得到的代理键是可以正确联接的。
http://mysql.taobao.org/monthly/2019/11/02/#jump
一个hash join算法实现需要三个步骤:
选择合适的连接参与表作为内表(build table),构建hash表;
然后使用另外一个表(probe table)的每一条记录去探测第一步已经构建完成的哈希表寻找符合连接条件的记录;
输出匹配后符合需求的记录;
哈希连接根据内存是否能够存放的下hash表