数据库的哈希设计

数据库的哈希设计

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

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

column namedata type
name1varchar(50)
name2varchar(100)
other columnvarchar(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 namedata type
name1varchar(50)
name2varchar(100)
other columnvarchar(32)
hashkeyinteger
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' 的记录,同理先通过ora hash函数计算,得到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.可以看做你自己实现的能跨越多表多库德索引,缩减查询运算空间。

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

mmonkeyer@163.com

ora_hash() 与 get_hash_value 区别 一、ora_hash格式: ora_hash('strings', N, 0 )     strings: 输入值     N:最大hash bucket的值     0:起始hash bucket值,缺省是1 dbms_utility.get_hash_value(‘strings', min, max)     strings:输入值     min:hash bucke 阅读详情

相关推荐

Power BI应用案例:淘宝用户行为分析实战

一、案例背景 基于千万级的淘宝用户的使用记录,对淘宝用户的行为进行分析,具体指标包括:日PV、日UV、小时PV、小时UV、不同用户行为的PV、用户购买次数分布、用户购买频次分布、活跃用户消费情况等。通过本案例,可以基本看看Power BI都能分析达到怎么样的效果。 二、数据来源及结构 阿里天池官方发布的1万用户在2014年双十二期间的行为记录,数据量达1200万+。 数据下载地址:...

灰哥数据智能的专栏 1万+

ORA_HASH函数

oracle中的hash分区就是利用的ora_hash函数 partition by hash(object_id) 等价于 ora_hash(object_id,4294967295) ora_hash(列,hash桶) hash桶默认是4294967295 可以设置0到4294967295 ora_hash(object_id,4) 会把object_id的值进行hash运算

variab的博客 1万+

【AIGC】人工智能训练师(高级)考试题库-橙点同学

例如,在嘈杂的环境中进行声音转文字,即使是优秀的模型也可能会出现一些错误,但这并不意味着该模型本身质量差,在其他较为理想的环境下,它可能会有很好的表现。例如,在一些语音应用和系统中,会使用SSML来实现对语音合成的精细控制,如调整语速、语调、音量等,而这些应用和系统的开发往往会遵循W3C的相关标准和框架,其中就包括SSML这一语音合成标记语言。例如,在语音识别等场景中,像“去火车站怎么走”这样的语音指令,没有其他嘈杂的背景声音或模糊不清的表述,就属于无噪音的数据,有利于系统准确地进行处理和分析。

RisunJan的博客 9922

ora_hash

一、ora_hash格式: ora_hash('strings', N, 0 ) strings: 输入值 N:最大hash bucket的值 0:起始hash bucket值,缺省是1 dbms_utility.get_hash_value(‘strings', min, max) strings:输入值 min:hash bucke

CTO成长之路——Rosanu 7010

[pcie]gen6 下 L0p

000b~100b: 触发L0p 操作流程协商工作link width 到target link width.为111b:link width 硬件动态调整。硬件动态:Device control 3 register.target link width=111b当pcie链路检测到空闲状态时,设备可以主动进入L0p状态。L0p 是pcie gen6 新引入的链路状态,是L0的子状态。(5)LINK WIDTH:当LINK WIDTH CHANGE.cmd为Request时表示请求的链路宽度。

BIG_APPLE1的博客 1679

Matlab 2023b 科研党必备:MOSEK 10.1.25 安装与配置全记录(附License过期解决方案)

本文详细介绍了在Matlab 2023b中安装与配置MOSEK 10.1.25的全过程,特别针对科研用户提供学术许可申请、证书文件部署及环境集成的实用指南。文章还包含常见问题解决方案和性能调优建议,帮助用户高效完成优化计算任务。

weixin_30598225的博客 171

oracle使用哈希算法,ORA_HASH函数使用的算法是什么?

Dr Y Wit..20另一个似乎使用ORA_HASH的系统好吧,如果它"似乎在使用",那么进行一些逆向工程并检查究竟是什么调用和反汇编函数代码是有意义的.但是,如果您想深入了解Oracle内部,那么以下内容可能有所帮助.首先,您必须弄清楚内部C函数的调用.为此,您可以在一个会话中执行一些长时间运行的代码.我做了这个select avg(ora_hash(rownum)) id from(sele...

weixin_39627361的博客 3083

农经权二轮延包—地块示意图生成

本文介绍了地块示意图制作软件的使用方法及模板制作要点。软件通过设置六个参数(示意图模板、属性数据、地块DBF、导航图、地块图片及存放位置)自动生成Word文档,并可导出PDF。文章详细说明了5页Word模板的制作过程,包括页脚三栏设置和页码插入技巧,支持每户最多28个地块的情况。同时指出该软件生成的图片分辨率(300dpi)满足清晰度要求,并提及软件免费提供插件但收费提供后续服务。

weixin_47364423的博客 1116

Redis为何禁用多DB?从哈希槽与数据迁移看集群设计

在分布式系统设计中,数据分片与存储隔离常是架构演进的核心议题。Redis作为广泛使用的高性能缓存组件,其单机版提供的多数据库(DB)能力在集群模式下却被完全禁用,这一限制常令开发者困惑。本文从哈希槽(Hash Slot)这一数据分片基本原理出发,剖析集群如何通过CRC16计算将键映射到固定槽位,并依靠MOVED重定向实现请求路由。当引入多DB概念时,槽位计算、路由表维护及数据一致性将面临指数级复杂度增长,尤其在扩缩容的槽位迁移场景中,跨DB的原子性操作几乎无法实现。从工程实践角度看,这一限制实为架构设计的必

weixin_31102231的博客 328

三、STM32F103ZET6最小系统

stm32最小系统除去主控CPU外,需要包含,电源电路,复位电路,BOOT电路,时钟电路(因为stm32有内置时钟,对时钟要求不高的话,可以不要)。NRST引脚其实芯片已经用一个约40K的电阻上拉了,所以理论上来说是不需要再外接上拉电阻的,但我看大家都是有外接上拉的,心里没有底,不管这么多,先预留着。吃过亏,特意查看了芯片手册资料,下图是官方建议的供电方案,图片来自stm32f103zet6数据手册。首先明白目标,单纯的学习软件知识并不是我希望的,我还希望会硬件设计,这样后面才能摆脱开发板。

sevendecsdn的博客 6457

jmeter5.4.1 使用案例

jmeter主要用于接口压力测试,模拟海量请求并发访问接口 1.下载jmeter https://jmeter.apache.org/download_jmeter.cgi 2.启动jemter 双击即可 3.配置jmeter 1.先更改语言 2.创建线程组 配置线程执行规划 3.创建http请求默认值 4.创建http请求 5.创建http信息头管理器 指定 Content-Type:application/json 6.创建响应断言 7.创建察看结果数 直接点击 执行 按.

SpringHASh的博客 1222

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

Table of Contents 1 用哈希的key代替字符串上的索引,提高查询效率。 1.1 什么是hash 1.2 如何在数据库设计中使用hash 1.3 使用计算列 1.4 如何在多表关联中使用哈希 2 库表散列 2.1 ORAHASH 函数 2.2 哈希拆分例1 2.3 哈希拆分例2 2.4 除留余数法 2.5 拆库 ...

mxdxm8899的专栏 203

Luma AI技术浅析(一)

是一款基于人工智能的3D内容生成平台,旨在通过深度学习技术实现高效、高质量的3D场景、物体和动画的生成。Luma AI 主要利用了等技术,以实现从图像、视频到3D模型的转换和生成。

m0_75253143的博客 1732

数据库哈希设计

Table of Contents 1 用哈希的key代替字符串上的索引,提高查询效率。 1.1 什么是hash 1.2 如何在数据库设计中使用hash 1.3 使用计算列 1.4 如何在多表关联中使用哈希 2 库表散列 2.1 ORAHASH 函数 2.2 哈希拆分例1 2.3 哈希拆分例2 2.4 除留余数法 2.5 拆库

PAPALIAN的专栏 1733

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Python进阶技巧之利用break和哈希算法优化数据库批量操作

break的双重身份:它既是循环的终结者,也是异常流程的刹车片。在上述代码中,一旦写入失败,break立即介入,防止错误数据不断累积。哈希的前置校验:将冲突检测从数据库层(慢)前移到应用层(快),利用内存 Set 或预查询,极大提升了同步效率。

tomatop的博客 21

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

明明连接的是Redis的DB0,为什么能查到DB3的数据?

生产环境会追求 Redis 高性能的同时,还会要求其高可用,往往会采用集群模式部署。但有时候,服务器资源确实不充裕,那么开发环境与测试环境往往会共用一套 Redis 集群,用数据库隔离;有些时候为了节省资源,开发环境与测试环境会共用 Redis,用不同的库进行隔离,比如开发环境用。我们用同个应用,用开发配置启动一个实例,用测试配置也启动一个实例,看彼此之间是否做到数据隔离了。开发、测试、生产,环境尽量保持一致,因为环境不一致导致的问题,排查起来真的很头疼!Redis 集群不像单机,它不支持多数据库;

Cahiy0y27的博客 190

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

ccmusic-database/music_genre实战案例:音乐版权区块链中流派特征哈希上链方案设计

本文介绍了如何在星图GPU平台自动化部署音乐流派分类Web应用ccmusic-database/music_genre,实现音乐流派自动识别。该镜像可应用于音乐版权保护场景,通过提取音频特征生成哈希并上链,为数字音乐提供不可篡改的版权登记与验证解决方案。

weixin_32252929的博客 379

使用一个 hash值 来判断是否已经存在相同的数据

通过计算哈希值,我们可以将多个字段的组合映射成一个单一的值,从而简化连接操作。在数据库设计中,若两个表都有多个字段能够唯一标识一条记录,使用哈希值来关联这两个表是一个可行的做法,特别是在需要通过多个字段组合进行查找时。通过计算这些字段的 hash 值,可以将多个字段的比对转换成一次对 hash 值的比较,从而提高性能和简化代码。计算哈希值:为每个表中的记录计算一个唯一的哈希值,该哈希值是由多个字段的组合生成的。通过哈希值关联:使用计算出来的哈希值作为连接两个表的依据,从而找到交集或进行其他的联接操作。

求学如是,不可入宝山而空归。。。 568

PHP如何使用hash,PHP-php hash算法使用

hash算法一般是利用数组实现的,步骤如下:存元素时:1.把要存储的元素(value)计算一个hashcode(称为散列),这个就是key。2.把元素存储到以hashcode为下标的数组中。3.若此数组下标已经有元素,则使用链表的方式把元素连接起来。获得元素时:1.元素(value)计算hashcode。2.hash表中按hashcode(key)取得元素。3.若一个key中对应多个元素,则还需匹...

weixin_30927801的博客 1848

关于Redis的全文搜索能力,及衍生内容Redis 8、RediSearch、DB 与分槽

本文系统梳理了 Redis 多数据库、集群分槽机制和 Redis 8 新特性等核心知识点: Redis 多数据库设计存在资源隔离不足、运维复杂等问题,官方推荐使用 Key 前缀命名规范或多实例部署替代多 DB 方案 Redis Cluster 采用 16384 个哈希槽实现数据分片,支持灵活扩缩容和高可用,通过 Hash Tag 可确保相关 Key 落在同一节点 Redis 8 原生内置了 RediSearch 等扩展功能,无需额外加载模块即可使用全文搜索等高级特性 提供了 Docker 部署 Redis

weixin_46473496的博客 214
上一篇: 一例胜千言,详谈SQL Sever数据库锁
下一篇: 不同Framework中DAO层的分析
mmonkeyer
博客等级 码龄23年 1粉丝 0原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值