`

(转帖)数据库的哈希设计

阅读更多

1 用哈希的key代替字符串上的索引,提高查询效率。

索引时一种最为常见的提高查询效率的方式,但在长字符串字段上的索引效果较差,如果多个这样的字段上的联合索引则效果更差。例如,

column name data type
name1 varchar(50)
name2 varchar(100)
other column varchar(32)

如果经常需用name1和name2上查询,比如经常执行

select * from table where name1=? and name2=?
这种情况就需要在这两个字段上加索引,
create index myindex on table1 (name1,name2)

 

索引提高性能,但当数据量巨大时比如上亿,查询性能就会降低的很快,而且索引也占用存储空间,超大数据量也会使索引占用空间膨胀。比如在name1和 name2上建立的联合索引一行就要使用最少150个字符的空间,数据量大了后空间的蚕食可想而知。据一篇文章说“建立索引,系统要占用大约为表的1.2倍的硬盘和内存空间来保存索引”

因此,我们来使用hash key

1.1 什么是hash

简单说,就是给个字符串,通过一种算法,返回一个整数。返回的整数和给出的字符串就相当于有了一种对应关系。哈希算法有很多种,详见这里 http://en.wikipedia.org/wiki/Hash_function#Hash_function_algorithms

SQLSever中提供了checksum()函数来产生hash key.例如:

select checksum('abc','Tom and Jack')
返回:1094199073
在SQLServer2005上测试结果(以下同)

 

用这种哈希函数,你就可以把长字符串列映射到一个哈希值上来,这个值是唯一的,如果改动字符串的内容,哪怕一个字母,新的哈希值也会不同。实际上,你还可以映射非字符串列,因为哈希算法本质上是对任意长度二进制的映射算法,一切数据类型都可以用二进制来表示。

需要注意的是,实际应用中,有可能出现两段不同的二进制分别经过哈希运算,竟然生成同一个哈希key,这种几率不大,这称为hash collision.它的出现原因也不复杂,主要是由于实际应用中的哈希输出一般都有固定的最大长度限制,超过此长度的哈希输出可能会被进行剪断等操作。这样就有可能本来不应该相同的哈希key经过剪断变成相等的了。具体可参看相关资料,这里不详述,但我们需要当心实际中的哈希值不一定唯一这一点。

1.2 如何在数据库设计中使用hash

让我们继续开始的那个表。当数据量爆多时,建立在name1和name2字段上的索引就有性能问题了。那么根据hash原理,我们对这个表增加一列哈希列,然后对这一列增加索引,而不是对name1和name2增加索引,成为这样,

column name data type
name1 varchar(50)
name2 varchar(100)
other column varchar(32)
hashkey integer
create index hashkeyindex on table(hashkey)
hashkey的内容是name1和name2的哈希key,可以通过数据库的checksum来实现。
update table set hashkey=checksum(name1,name)

上面这条语句就会给所有的哈希列赋值。当然更好的办法是建立触发器,每次在插入一条新的记录的时候都对哈希列通过checksum(name1,name2)计算而赋值。

这样,本来建立在两个字段上的索引变成了建立在一个字段上的索引,更重要的是hashkey只有几个byte的大小,比起原来name1+name2=150个字符的大小来说,索引的存储量大大降低了。其实,索引本身也需要一定的搜索,而索引本身越小,搜索一定也就越快。以按名称查询为例,有了哈希列后就可以这么查询

select * from table where hashkey=checksum('张三','地方是飞啊飞鹅的')

由于前面我们提到的哈希key的不一定唯一性,即多个输入可能会被运算成同一个输出(即哈希key),反过来说,一个哈希key有可能对应多条记录,那么上面的这个查询就可能查出不止一条记录,怎么做呢?很简单

select * from table where hashkey=checksum('张三','地方是飞啊飞鹅的') and name1='张三' and name2='地方是飞啊飞鹅的'

where子句中的哈希key的条件已经使查询大大的减少了返回的行数,再通过真实条件过滤,很快就能查到你真正想要的。这种做法有人会觉得不错,但毕竟在数据库中增加了一列,而且还要写个触发器每次往新增列里更新哈希值。有些麻烦。那这里就可以利用计算列来解决这样的问题。

只是,为什么数据库不直接在联合索引中存放哈希值呢?使用者不就更省事了吗?

1.3 使用计算列

什么是计算列?就是通过计算而得来的列,并非真实存在列(但可以使用 PERSISTED关键字把计算列实际存放在表中)。比如第三列是前两列的和,那么第三列就可设计成计算列,每次查询出的第三列的值实际上是通过计算前两列之和而得出的。这种设计可减少额外存储,减小DB size.

在哈希设计中,就可以创建一个哈希计算列,然后对该哈希计算列创建索引,这样它就不需要真正的存放在表里,实现了只是用来提高查询效率的哈希列与业务表完全分离。

对计算列创建索引需要一些条件,比如计算列的表达式必须有确定性、精确、不能用字符串列等,而哈希key都容易满足。

CREATE TABLE mytable(
    name1 varchar(50),
    name2 varchar(150),
    myhashkey AS checksum(name1,name2)
)
Go
CREATE INDEX hashkeyIndex ON mytable(myhashkey)

1.4 如何在多表关联中使用哈希

多表关联中可以使用hash join,是否使用hash join一般是有数据库系统自己决定。也可以自己手工指定,手工指定方式如下

SELECT a.au_id FROM A a JOIN B b
ON a.name = b.name OPTION (HASH JOIN)

2 库表散列

当数据库表大的一定程度的时候,比如大到上亿或几百亿的记录,查询的性能就会骤降,即使正确的使用索引也无济于事。这个时候就需要使用库表散列的方式来进行处理,实质上就是对表进行拆分,然后按照一定的策略对拆分的表进行查询和其它处理。这里我主要讲哈希策略。

哈希策略就是通过哈希计算,直接定位表。例如:用户表,数据量巨大,需要拆分。以用户的id为哈希计算的关键字,比如userid 是32位的字符串. SDDKSL3KDKSLDKE2FKQKSLEJF9ELAPEK. 最简单的哈希策略是用userid左边第一位的字母作为新表明的后缀。这样,就有了 USERS表。26个字母+10个阿拉伯数字,这样就可以把用户表拆分成36个表。如果查询 userid=UDKLSK3DKSLKDE2FKQSKLEJ9FELAPEKC的记录,可以马上定位到USERU表 上去。这只是举个简单的例子来说明库表散列,实际中往往用用户名来查,比如根据张三,李四来查,这需要数据库迅速定位张三、李四分别在哪张表中。为了使多个用户均匀的分布在不同的表中,需要一个比较好的哈希函数,我们就不自己设计了,就用oracle的orahash函数来举例。

2.1 ORAHASH 函数

 

函数的用法:
select orahash('张三',100,0) from dual

 

结果:79

 

含义:oracle的哈希函数计算出'张三'的哈希值,然后把它放进79号桶中。 100意味着最多只能允许有100个桶。0是种子,用来计算哈希值和安排不同的桶。返回值就是分配的桶号。所谓桶,无非就是组,即把不同的哈希值均匀的放进不同的组里。

 

标准描述:orahash(exp,maxbucket,seedvalue)

 

2.2 哈希拆分例1

有了这个现成的函数,hash拆表就比较简单了。如果你想拆成1000个表,那只需要把maxbucket设置成1000即可。

首先,要建1000个user表,分别是 user000,user001,user002,user003….user999. 用户插入的时候,先根据用户名计算它要插入那个表。比如用户名是:'王老虎'.

ora_hash('王老虎',1000,0)的结果是159,那么就需要把该条记录插入到 user159表中。

insert into user159 (name) values ('王老虎') –实际中还有别的字段
查询的时候,要先定位表,比如查找用户名为 '王老虎', id='SDFEW23SDF' 的记录,同理先通过orahash函数计算,得到159,然后就执行:

 

select * from user159 where name='王老虎' and id='SDFEW23SDF'

其它数据库没有orahash这样的函数,所以要自己设计或借用其它的散列函数。 实际上orahash函数可能被oracle的哈希分区(hash partition)功能使用。而 hash partition就是oracle的一种把大表数据分而治之的方式。即把一个大表划分成好多个分区。所以在oracle数据库设计中,也就不需要把一个达标拆分成多个小表,而是直接把一个大表拆分成多个分区,然后让oracle自动去管理就行了。但如果做分布式数据库,那么很多工作还是需要自己来做。

2.3 哈希拆分例2

还有一种方式是在每次插入数据的时候才进行判断是否新建hash表。比如有一个叫王刚的记录要被插入,伪码如下:

String userHashName = yourHashFun('王刚');
String tableName = "user_"+userHashName;
if (!exists(tableName)){
    create table...
}
insert into ...

这样做,由于需要做表是否已经存在的判断,所以有一定的性能损耗。

2.4 除留余数法

orahash函数好,但属于oracle专有,离开了oracle,有时我们需要设计自己的hash函数。除留余数法简单易用。

H(k)=k mod p  p<=m

如果userid是数字,则可直接对userid进行此法。比如想要拆分10个表,就取p=10. 比如userid=11,11/10余1,那么就把userid=11的记录分配到表 user1。如userid=23,23/10余3,那么把userid=23的记录分配到表 user3. 除留余数法的好处就是1,简单;2,能相对均匀的把数据分配到不同的子表中去(前提是userid本身比较均匀)。

如果userid不是数字,那么就先对userid进行hash计算(你可以使用现成的 hash计算,比如md5,也可以自己设计),得到数字值,然后再对数字值进行除留余数法。是否均匀,也取决于hash计算的结果是否均匀。

它有一个缺点,原来拆了10个表,系统运行一段后需要拆成50个表会不容易实现。

2.5 拆库

除了如前所述的拆表操作,还可以进行拆库操作。就是把一个大表拆分成多个子表放入不同数据库中。例如user表中的数据可以分别放入5个DB中,分别是 DB1,DB2,DB3,DB4,DB5,每个DB中都有一个user表。5个DB中的user记录加起来就是所有user的记录。hash拆库与拆表方式相同。明白了拆表,拆库也就明白了。所不同的是,定位库和定位表方式有差异。库有可能分布在不同的机器上,所以需要多个数据源。

下面以一段示例性代码说明这种情况

public void test(){
    String userid = "123456";
    String hashvalue = hash(userid);
    String db_connection = getConnection(hashvalue);
}
//用户自己可以实现这个函数(这个函数可以做的很复杂,数据源可以做成动态的)
public abstract Connection getConnection(String db_mark);

//比如下面的一个实现
public Connection getConnection(String db_mark){
    String username="admin";
    String password="admin";
    String ip = "localhost";
    String dbname = "db"+db_mark; //拼凑db name. 
    Class.forName(driveString).newInstance();
    conn = DriverManager.getConnection("jdbc:inetdae7:localhost:1433", username, password);
    conn.setCatalog(dbname);
    return conn;
}

不关怎么实现,只要你能想办法找到你要定位的数据库就行。这个过程就是 DB路由。路由策略根据实际业务情况即可以做的很简单,也可以做的功能强大、复杂无比(因为要负载均衡、要读写分离、要便于统计、要个摘要表、要个缓冲表,要这个要那个,元素越来越多,设计就越来越复杂)。

Hash就是把一大堆乱七八糟的东西用一个小小的东西(一段数字)来代表。这种一大一小的对应的关系极大的缩减了运算空间,所以极大的提高了运算效率。这可以看作是种压缩,看做一种以小御大(想象一下你的食指一动,对面的五指山的食指也一动,多么壮观)。hash后的key们,就如同是对一个庞大世界的精简映像。这正好适合于成万上亿的大数据量的处理。而数据库中的这种被代表现象无处不在。

  1. id. 每个表都有一个id或类似id的唯一标识。代表某一行数据,缩减运算空间。对id的各种运算要比对整条记录的数据进行运算快的多。
  2. 索引。缩减查询运算空间。
  3. Hash.可以看做你自己实现的能跨越多表多库德索引,缩减查询运算空间。

    以小御大,运用之妙,存乎一心。

分享到:
评论

相关推荐

    数据库索引设计和优化

    数据库索引设计与优化是数据库管理系统中至关重要的一个环节,它直接影响到数据查询的效率、存储空间的使用以及系统的整体性能。在这个主题中,我们将深入探讨数据库索引的基础概念、设计原则、优化策略以及实际应用...

    数据库课程设计-前端+后端实现用户登录等一系列操作(源码)

    总的来说,这个课程设计项目涵盖了前端开发、后端开发、数据库设计与操作、网络安全、版本控制和部署等多个IT领域的知识点,对于提升学生的综合技能有着极大的帮助。通过实践,学生可以更深入地理解这些概念,并为...

    数据库可扩展哈希课程设计

    建立索引是将输入的每一条记录根据指定的键值放入合适的哈希桶内,当哈希桶已满时,需要进行分裂。查询时根据输入的键值返回具有相同键值的记录,返回的记录需要进行排序且可能有不止一条。输入的记录为tpc-h生产的...

    数据库课程设计-银行储蓄系统

    数据库课程设计是IT教育中的重要组成部分,特别是在学习关系型数据库管理和开发时。在这个"银行储蓄系统"的项目中,学生通常会被要求构建一个模拟银行储蓄服务的数据库应用程序,以理解和应用基本的数据库概念、SQL...

    哈希表的设计与实现(C语言课程设计)

    1. 数据库索引:哈希表可以用于数据库索引,快速检索数据。 2. 缓存系统:哈希表可以用于缓存系统,快速存储和检索数据。 3. 字典实现:哈希表可以用于字典实现,快速检索和存储数据。 七、总结 哈希表是一种高效...

    数据库课程设计.zip

    数据库课程设计是计算机科学与技术专业中一门重要的实践性课程,旨在让学生深入理解数据库系统的工作原理,掌握数据库设计、管理及应用开发的基本技能。在这个项目中,学生通常需要完成从需求分析、概念模型设计、...

    数据库设计指南-数据库设计教程

    数据库设计是IT领域中的核心技能之一,特别是在软件开发和数据管理中扮演着至关重要的角色。这份"数据库设计指南-数据库设计教程"很可能包含了如何高效、有效地构建和优化数据库的宝贵信息。下面,我将根据标题和...

    哈希值计算

    哈希值计算是计算机科学中一个重要的概念,它在数据完整性、信息安全以及文件校验等领域发挥着关键作用。哈希值,也称为散列或消息摘要,是通过特定算法将任意长度的数据转换成固定长度输出的过程。这个过程是单向的...

    数据库物理设计(1).docx

    数据库物理设计是数据库系统设计的关键步骤,其目的是根据特定计算机系统(包括数据库管理系统DBMS和硬件)的特性,为已有的逻辑数据库模型设计高效的存储结构和存取策略。这一阶段的目标是既要保证物理数据库占用...

    校园小商品交易数据库课程设计

    这个课程设计涵盖了数据库设计的基本原理、关系数据库模型、SQL语言以及软件工程中的系统分析与设计方法。 首先,我们需要理解数据库设计的基础。数据库设计包括需求分析、概念设计、逻辑设计和物理设计四个阶段。...

    佛大数据库课程设计

    在这个项目中,学生将面临一系列关于数据库设计、实现、管理和优化的任务,旨在提升其在实际工作中的数据库技能。以下是一些关键的知识点: 1. **数据库基础理论**:首先,了解数据库的基本概念至关重要,包括关系...

    哈希表设计 哈希表 哈希表

    了解并熟练掌握哈希表的构造方法,不仅能够提高程序的运行效率,还能帮助解决许多实际问题,如数据库索引、缓存系统、数据去重等。在编程实践中,理解和优化哈希函数、冲突解决策略以及动态扩容机制是提升哈希表性能...

    哈希表课程设计数据结构实验报告——哈希表设计

    哈希表课程设计数据结构实验报告——哈希表设计 针对某个集体中的人名设计一个哈希表,使得平均查找长度不超过R,完成相应的建立和查表程序. 1.2 人名为汉语拼音形式,最长不超过18个字符(如:庄双双 ...

    哈希表设计 哈希表的具体实现代码

    5. **哈希表的应用**:哈希表广泛应用于数据库索引、缓存系统、编程语言的字典结构(如Python的字典、JavaScript的对象)、集合和映射等场景。 6. **具体实现代码**:哈希表的实现通常涉及到以下步骤: - 初始化...

    数据结构哈希表设计实验报告

    在“数据结构哈希表设计实验报告”中,我们可能会涉及到以下几个关键知识点: 1. **哈希函数**:哈希函数是哈希表的核心,它的作用是将键转化为数组的索引。一个好的哈希函数应该尽可能使得不同的键映射到不同的...

    数据结构课程设计 哈希表设计

    针对某个集体(比如你所在的班级)中的“人名”设计一个哈希表,使得平均查找长度不超过R,完成相应的建表和查表程序。 假设人名为中国人姓名的汉语拼音形式。待填入哈希表的人名共有30个,取平均查找长度的上限为2...

    华中科技大学数据库课程设计

    2. **数据库设计**:这包括需求分析、概念设计(如ER图)、逻辑设计(转换为关系模式)和物理设计(考虑存储效率)。你将学习如何通过正常化减少数据冗余,以及如何使用范式理论优化设计。 3. **数据库管理系统...

    基于一致性哈希算法的分布式数据库高效扩展方法.pdf

    如何保证在节点状态发生变化时,系统仍能对外提供稳定的服务,并保持数据的一致性,是分布式数据库设计与维护中必须面对的关键问题。 文章最后强调,本研究工作的版权为作者所拥有,并且按照Creative Commons ...

    数据库设计文档(PDF)

    这份“数据库设计文档(PDF)”提供了一个详细的学习资源,涵盖了数据库设计的基础理论、最佳实践和实际案例,对于理解数据库设计理念和技术具有重要意义。 首先,数据库设计通常包括需求分析、概念设计、逻辑设计...

    哈希表设计程序设计+数据结构实验报告

    哈希表设计程序设计+数据结构实验报告 1.1 针对某个集体中的人名设计一个哈希表,使得平均查找长度不超过R,完成相应的建立和查表程序. 1.2 人名为汉语拼音形式,最长不超过18个字符(如:庄双双 zhuangshuangshuang)...

Global site tag (gtag.js) - Google Analytics