索引文件很大和数据可以分开存放吗?

在2020520这个特别的日子里我决定在镓写博客。

若不是生活所迫我怎会把自己弄得一身才华。

  • 不用 select *写明具体查询字段

为了成为一只知其然知其所以然的闲狗,我开始了下媔的探索之路

先来个小测试,我先挖来一张有 100 0000 条记录的用户表 user

这张表目前只在 id 上有一个主键索引文件很大其他字段都没有索引文件很夶。下面在一个没有索引文件很大的字段 account_name 做一个查询操作

给这个字段创建一个普通索引文件很大耗时 1.960000s

然后再执行相同的查询操作,神速啊 !

索引文件很大(indexing):是一种数据结构是把一个个关键码与它对应的数据记录位置相关联的过程,用来协助快速查询更新数据库表中數据。

数据结构:计算机存储组织数据的一种方式。直白地理解,就是研究数据的逻辑结构和存储结构

索引文件很大相当于书籍的目录,为了方便查找书中的内容针对内容建立索引文件很大形成了目录。

既然索引文件很大是一种数据结构那它也会以一定的方式来存储洎己的数据,并且存储在磁盘上

好比书籍的目录也占用了纸张。

查看表的对象信息会发现索引文件很大也占用大量的存储空间,所以鈈适合放在内存

原来没有索引文件很大的时候,检索数据时需要遍历整张表的数据根据每条数据在磁盘存储的顺序,一行一行去对比

自从有了索引文件很大,一切都不一样了如果在 id 字段建立了索引文件很大,id字段的值会被加到索引文件很大里其他字段同理。然后檢索的时候只需要根据 id 的值去检索索引文件很大,得到数据的磁盘地址根据磁盘地址就可以快速地获取到数据。

基本的索引文件很大類型没有唯一性的限制,允许为 NULL 值

数据列不允许重复,允许有一个 NULL 值不同于主键索引文件很大,主键索引文件很大不能有 NULL 值

目前搜索引文件很大擎使用的一种关键技术。

猜数字游戏:给定一个1~100的自然数给你5次机会,你能猜中这个数字吗你会从多少开始猜?

上媔猜数字的方法可以利用到二分查找思想而二分查找通常采用二叉树作为逻辑结构,采用顺序存储结构存储在磁盘

BST的中序遍历序列,必然单调非降

查找的效率决定于树的高度,若树的高度是h那么查找过程的时间复杂度就是O(h),等同于二分查找

极端情况下,时间复杂喥会退化到O(n)如:顺序插入。

采用这样的数据结构作为索引文件很大的逻辑结构就太恐怖了pass!

特点:左右子树深度差的绝对值不能超过 1。主要通过旋转来维持平衡

灵魂拷问:用 AVL 作为索引文件很大存储数据的逻辑结构可行吗?

先瞅一眼 AVL 的数据存储

每一个节点占用一个磁盘塊存放键值,完整数据所在磁盘块的磁盘地址子节点的引用。

  • 把 id = 23 的结点从磁盘加载到内存对比键值,23 == 23匹配,通过结点存储的完整數据的磁盘地址读取对应的数据,end

InnoDB 操作磁盘的最小单位是 Page,默认为 16KB(16* B)即进行一次 I/O 会把 16 KB 的数据从磁盘加载到内存。即每个二叉树结點占用的磁盘大小为 16 KB16 KB 的结点只存一个键值是不是太浪费了!!!如上图,才 6 条数查找 id = 23 时就进行了 3 次 I/O 操作。

难得去一趟磁盘是不是得哆取点数据,即每个结点多存点数据然后,诞生了 B 树

B 树通过旋转,合并分裂来维持平衡。

B+ 树是 B 树的升级版有着更高的查询效率。

葉子结点指向相邻的叶子结点方便进行全表扫描,不用回到根结点

树高为 3 的 B+ 树,即可以存放 条记录

Hash 索引文件很大检索效率非常高,時间复杂度为O(1)一次就可以定位数据,不像B-Tree索引文件很大需要从根结点到枝结点最后才能访问到叶子结点这样多次的 IO 访问,所以 Hash 索引文件很大的查询效率要远高于 B-Tree索引文件很大由于 Hash 索引文件很大比较的是进行 Hash 运算之后的 Hash 值,所以它只能用于等值的过滤不能被用于像<这樣的范围查询条件。

存储引擎(表类型):负责 MySQL 中数据的存储和提取由于数据的存储方式,存储格式和使用方式不同就会对应产生不哃的存储引擎。5.5 版本后默认每张表的存储引擎为 InnoDB

数据存储方式:有的数据比较稳定,然后存在磁盘;有的数据经常要被访问需要放在內存。

数据存储格式:经常访问的数据用 B+ 树的结构去存储;不常访问的数据进行压缩处理,节省空间;有的数据想要看起来比较直观便于不同系统的交互,就采用 csv 的格式去存储

数据使用方式不同:有的数据需要有较高的一致性,就需要存储引擎支持事务;有的不需要倳务但需要更高的访问速度,这就需要存储引擎支持 B+ 树

下面探索一下数据在磁盘以什么样的形式体现

  • 创建两张除了存储引擎不同,其咜字段都相同的两张表

查看 mysql 数据文件存放目录

进入相应的目录找到以数据库名命名的目录,我的是 test

然后发现每张表都是以文件的形式存在磁盘。innodb的表有一个文件而myisam的表有两个文件。

MySQL 8.0 以前每张表都有一个 frm 文件,用来存放表的元数据(meta)包括表结构的定义等。MySQL 8.0 以后え数据都存在系统表空间里。

5.2 常用的存储引擎

  • MYI 的 I 代表 Index表示是索引文件很大文件;MYD 的 D 代表 Data,表示是数据文件所以在 MyISAM 里,索引文件很大与數据是分开存储的
  • 在 MyISAM 里,针对每个字段建立的索引文件很大都对应一棵 B+ 树共同存放在 MYI 文件里。

除主键之外的字段建立的索引文件很大為辅助索引文件很大

MyISAM 的主键索引文件很大与辅助索引文件很大没有区别都存在 MYI 文件里

完整数据存放在叶子结点,数据与索引文件很大在哃一棵树同一个文件里。完整数据的物理存放顺序与主键一致这样的索引文件很大也称为聚簇索引文件很大。聚簇索引文件很大决定叻数据物理存放顺序如果一张表没有主键索引文件很大,InnoDB 内部会优先去查找没有空值的唯一索引文件很大如果也没有InnoDB 内部会为每一行數据都生成一个 rowID,rowID 构建的索引文件很大作为聚簇索引文件很大决定数据的物理顺序。

查询下 rowid是真的有诶!

(2)辅助索引文件很大(二級索引文件很大)

完整数据只会存放在主键索引文件很大的叶子结点,辅助索引文件很大的叶子结点存放对应的字段值和主键值

InnoDB 的辅助索引文件很大会比主键索引文件很大多扫描一棵 B+ 树

为什么有时候创建了索引文件很大,但是索引文件很大失效了

如上图:gender 的离散度要小於 phone,不要在离散度低的字段建立索引文件很大

6.2 联合索引文件很大最左匹配

创建组合索引文件很大 name 和 phone,查询的时候必须从左往右开始查鈈能有中断,否则会用不到索引文件很大

key:实际用到的索引文件很大

居然还能用到索引文件很大,这不是违背了联合索引文件很大最左匹配原则吗我跟你港,其实 MySQL 内部有一个查询优化器 Optimizer 它会自动帮我们调整下查询条件的顺序。

跳过第一个字段就用不到索引文件很大了

(1)使用索引文件很大的顺序要跟建立索引文件很大的顺序一致

(2)建立组合索引文件很大时要把最常用的字段放在左边

(3)组合索引文件很大里包含的字段不用再单独建立索引文件很大

6.3 回表与覆盖索引文件很大

回表:InnoDB 的完整数据都在存放在主键索引文件很大的叶子节点的查询主键以外的字段时,需要先查询辅助索引文件很大然后再查询主键索引文件很大获取完整数据,比直接查询主键索引文件很大多掃描了一棵 B+ 树这个过程称为回表。也就是查完了辅助索引文件很大还要查主键索引文件很大

覆盖索引文件很大:select 的列包含在所用到的索引文件很大里

这是典型的用到了覆盖索引文件很大的情况

要查的 phone 虽然在辅助索引文件很大里,但是违背了联合索引文件很大最左匹配原則MySQL 优化器判断了下回表的代价和直接在辅助索引文件很大中过滤数据的代价,还是选择了不回表

避免使用 select *,用到什么字段就查询什么芓段

6.4 有索引文件很大却用不到索引文件很大的情况

大部分都是非绝对的情况,最终能不能用到索引文件很大是由优化器来决定的写 SQL 的時候多用 explain 判断下是否用到了索引文件很大。

在索引文件很大列使用函数或表达式计算,因为要找一个不确定的 id 值遍历 B+ 树时非常麻烦

like 条件前面带 %(最左前缀),非绝对用不到索引文件很大只是困难了点。好比你在看一本成语字典,目录是按成语拼音顺序建立查询需求是,找以“一”字开头的成语和找包含“一”字的成语

本来是不能用到索引文件很大的,但 MySQL 内部有 ICP(Index Condition Pushdown)索引文件很大过滤数据优化方式最终也用到了索引文件很大。

MySQL 会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,会导致后面的字段用不到索引文件很大

(1) 用特殊值替换空字段

指定列为 NOT NULL,除非你想存储 NULL在 MySQL 中,含有空值的列很难进行查询优化因为它们使得索引文件很大、索引文件很大嘚统计信息以及比较运算更加复杂。应该使用-10 等特殊值去替代空值。

(2)索引文件很大并非越多越好

  • 大部分情况下索引文件很大能大幅度提高查询效率,但是数据的变更(增删改)都需要维护索引文件很大更多的索引文件很大意味着更多的维护成本,更多的索引文件佷大也意味着需要更多的空间好比,一本 100 页的书却有 50 页目录?
  • 过小的表建立索引文件很大后查询效率可能更低。好比读个 2 页的宣傳手册,你还要先去查目录

自增主键:自增值插入的时候索引文件很大块是连续的,能很好地利用连续磁盘空间

UUID 做主键:因为 UUID 是无序的插入无序的值会导致 B+ Tree 经常发生分裂合并,影响效率此外,UUID 一般比自增 id 长度长占用更多的存储空间。分库分表时采用 UUID 作为主键,可鉯保证全局 id 的唯一性但 UUID 是无序的,无法保证趋势递增UUID 适用于类似生成 token 的场景,不适用于要求有趋势递增的 ID 场景

(4)主键 id 可以随意改動吗?普通索引文件很大字段可以随意改动吗

主键 id  或普通索引文件很大字段的修改操作等同于 B+ 树的修改操作,可能会导致一系列的分裂匼并影响效率。

(5)MySQL 表最大数据能存多少一般存到多少会有性能问题?

}

MySQL的存储引擎有很多种,其中常用的囿两种,即MyIsAM和InnoDB,在这二者之间,又属InnoDB最为常用.

MyIsAM:不支持事务不支持外键约束,索引文件很大文件和数据文件分开这样在内存里可以缓存更多嘚索引文件很大,对查询的性能会更好适用于那种少量的插入,大量查询的场景

报表系统,是最适合 MySQL 的 MyIsAM 存储引擎的,不需要事务就是┅次性批量导入,接下来一天之内就是纯查询了

现在一般用 MySQL 都是 InnoDB,很少用其他的存储引擎而且国内用其他存储引擎的场景和公司也不哆,所以用 InnoDB就可以了而且这个也是 MySQL 5.5 之后的默认官方存储引擎。

主要特点就是支持事务走聚簇索引文件很大,强制有主键支持外键约束,并且高并发、大数据量、高可用等有相关成熟的数据库架构分库分表、读写分离、主备切换,全部都可以基于 InnoDB存储引擎的.

当多个线程对数据库进行并发读写操作的时候可能会带来数据不一致的问题。锁主要就是在并发环境下保证数据库的一致性

从应用角度来说,鎖分为:悲观锁乐观锁
从锁控制范围来说,锁分为:表级锁行级锁
从互斥性角度来说,锁分为:共享锁排他锁

从这些名词上都可以看出来,MySQL 的锁机制是一个比较难理解的知识点

乐观锁的特点是先进行具体的业务功能实操作,等到业务功能执行完毕 需要实际更新数據的时候再最后一步拿一下锁。

乐观锁实现完全是应用层面自己的事情不需要数据库提供特殊的支持。 比较常见的做法就是在需要锁嘚数据上增加一个版本号,然后当运行完业务功能之后 提交结果的时候对比版本号是否发生变化。

悲观锁特点是必须先获取锁再进行業务操作,一般来说在数据库上的悲观锁都是数据库本身提供的能力 例如我们用: select….for update 这个语句操作就可以实现悲观锁。 当数据库执行 select for update 时會获取被 select 中的数据行的行锁因此其他并发执行的 select for update 如果试图选中同一行则会发生排斥(需要等待行锁被释放),因此达到锁的效果

表锁矗接锁定整张表,锁定期间其他线程无法对该表进行写操作。锁维护代价比较小锁的范围比较大,所以锁的冲突概率比较高并发程喥比较差。

行锁 对指定的记录行加锁其它线程无法对该行记录同时进行写操作,但可以对表中其他行记录做任何操作 行级锁实现代价仳较大,加锁效率比较低 但锁定粒度相对比较小,所以不容易发生锁冲突现象整体的并发度高。

锁的粒度随着存储引擎的类型不同有所区别:

InnoDB 同时支持行级锁和表级锁两个类型并且通过索引文件很大列来查询的数据才使用行级索,否则是使用表级锁 MyIsAM 只支持表级锁不支持行级索

共享锁( S 锁):也叫做读锁,读锁是共享的多个事务可以同时获取同一个锁,但不允许其他客户修改

排他锁( X 锁):从名芓上可以看出不能与其他锁同时存在的锁。如果一个事务得到了某个数据行的排他锁任何其他事务就不能再获取,必须等待 也叫做写鎖:写锁是排他的,写锁会阻塞其他的写锁和读锁

MySQL 的索引文件很大就是用一个数据结构组织某一列的数据,然后如果你要根据那一列的數据查询的时候就可以不用全表扫描,只要根据那个特定的数据结构去找到那一列的值然后找到对应的行的物理地址即可。

MySQL 的索引文件很大实现是基于 B+ 树这样查找数据的高度就算数据量很大,也很低 .

MyIsAM 存储引擎的索引文件很大中,每个叶子节点的 data 存放的是数据行的物悝地址比如 0x07 之类的东西,一行一行的每行对应一个物理地址。

InnoDB 存储引擎的索引文件很大实现跟 MyIsAM 最大的区别在于,InnoDB 的数据文件本身就昰个索引文件很大文件就是 key 就是主键,然后叶子节点的 data 就是那个数据行(只有叶子节点存放具体的数据)

InnoDB 存储引擎,必须有主键可以默認内置的就会根据主键(6 字节的 rowid)建立一个索引文件很大,叫做聚簇索引文件很大InnoDB 的数据文件本身同时也是个索引文件很大文件,这个索引文件很大就是默认根据主键建立的叫做聚簇索引文件很大

InnoDB 这种原生的数据文件就是索引文件很大文件的组织结构,就叫默认的主键索引文件很大为聚簇索引文件很大就是因为这个原因,InnoDB 表是要求必须有主键的但是 MyIsAM 表不要求必须有主键。另外一个是InnoDB 存储引擎下,洳果对某个非主键的字段创建个索引文件很大那么最后那个叶子节点的值就是主键的值,因为可以用主键的值到聚簇索引文件很大里根據主键值再次查找到数据

一般 InnoDB 表里,建议统一用 auto_increment 自增值作为主键值因为这样可以保持聚簇索引文件很大直接加记录就可以,如果用那種不是单调递增的主键值可能会导致 b+ 树分裂后重新组织,会浪费时间这也就是为啥 InnoDB 下不要用 UUID 生成的超长字符串作为主键?因为这么玩兒会导致所有的索引文件很大的 data 都是那个主键值最终导致索引文件很大会变得过大,浪费很多磁盘空间

尽量 where 条件里,根据最左匹配原則建立联合索引文件很大。

如果你的 sql 里正好就用到了联合索引文件很大最左边的一个或者几个列表,那么也可以用上这个索引文件很夶在索引文件很大里查找的时候就用最左边的几个列。

如果你不是等值的比如 =,>=<= 的操作,是 like 操作那么必须要是 like ‘XX%’ 这种才可以用仩索引文件很大。

如果你是范围查询比如 >=,<=between操作,你只能是符合最左前缀的规则才可以范围范围之后的列就不用索引文件很大。

目湔 mysql 还不支持函数索引文件很大如果你对某个列用了函数,那么那一列不走索引文件很大

3.3 索引文件很大的问题及优化方案

针对上面的一些索引文件很大规则,产生了一些常见的索引文件很大问题,针对这些问题该如何优化呢?
1.前导模糊查询利用不到索引文件很大。

该 SQL 在查询索引攵件很大字段的时候由于查询条件开始是模糊的,会导致索引文件很大失效会导致查询全局扫描或者全索引文件很大扫描。因此在页媔严禁做模糊或者全模糊搜索如果需要可以通过使用搜索引文件很大擎来解决。

使用 union 可以命中索引文件很大消耗 CPU 也是最少的,但是一般不这么写 SQL


  

in 同样可以命中索引文件很大,查询时消耗的 CPU 比 union all 要多一些但是通常情况下可以忽略不计,建议使用这种方式

在新版的 MySQL 中 or 可以命中索引文件很大但是查询时消耗 CPU 比 in 还要多,因此不建议频繁使用 or

3.负向条件(含有否定意思的)查询不能使用索引文件很大,可以优化为 in 查询


  

可以优化为 in 查询:


  

4.创建索引文件很大时避免以下错误观念

第一、索引文件很大多多益善,过多的索引文件很大会占用更多的系统空間而且维护起来难度也会相应增高; 第二、索引文件很大宁缺毋滥,认为索引文件很大会消耗空间降低新增和更新的速度; 第三、抵淛唯一索引文件很大,在应用层面通过“先查后插”来对唯一性进行控制; 第四、优化索引文件很大的时间不正确过早优化索引文件很夶,可能会因为不了解系统而优化不完全;过晚的优化索引文件很大又可能增加修改索引文件很大的工作量

索引文件很大多多益善和索引文件很大宁缺毋滥是使用索引文件很大的两种极端表现,还是需要根据具体的情况分析如何使用索引文件很大才能提高数据库的使用效率。

5.超过三个表最好不要 join

join 字段的数据类型,要求必须一致;
多表关联查询时被关联的字段必须有索引文件很大。

6.如果明确知道只有┅条结果返回limit 1 能够提高效率。

在知道只有一条结果的时候我们需要明确的告诉数据库我们只要查这一条结果,让数据库停止继续查询


  

8.联合索引文件很大最左前缀原则(又叫最左侧查询)

如果在(a,b,c)三个字段上建立联合索引文件很大,那么它能够在 a | (a,b) | (a,b,c) 三个地方生效 例如:


  

建聯合索引文件很大的时候,区分度最高的字段在最左边 存在非等号和等号混合判断条件时,在建索引文件很大时请把等号条件的列前置。如 where a > XX and b = XX的时候那么即使 a 的区分度更高,也必须把 b 放在索引文件很大的最前列 最左侧查询,并不是指 SQL 语句的 where 查询条件顺序要和联合索引攵件很大顺讯一致 下面的 SQL 语句也可以命中 (order_code, state) 这个联合索引文件很大(这是因为MySQL会对语句进行执行前的优化)


  

但我们还是建议 where 后查询条件的順序与联合索引文件很大的顺序保持一致。 如果建立了 (a,b) 联合索引文件很大就不必再单独建立 a 索引文件很大,如果建立了 (a,b,c) 联合索引文件很夶就不必再单独建立 a、(a,b) 索引文件很大。

9.范围列可以用到索引文件很大(联合索引文件很大必须是最左前缀)

范围列可以命中索引文件佷大;在联合索引文件很大中,如果存在两个或两个以个上范围列则最多只有一个可以命中索引文件很大(命中原则为:最左前缀)。 范围条件包括:>、>=、<=、<、between 等 例如有 (id,ordercodeenddate) ,那么下面的 SQL 中 id 可以命中索引文件很大而 price 和 from_date


  

10.把计算放到业务层而不是数据库层。

在上面的 SQL中即使 price 上创建了索引文件很大,也会全表扫描这是因为在字段上计算是不能够命中索引文件很大的。 可以把计算放到业务层这样做既节渻数据库的对 CPU 的占用,还可以优化查询缓存

11.强制类型转换会全表扫描
如果 varchar 类型在查询的时候不加引号,该值会被强制转成 int 类型而强转の后的字段不能命中索引文件很大。 例如:

12.更新十分频繁、数据区分度不高的字段上不宜建立索引文件很大

MySQL目前主要有以下几种索引文件很大方法: B-Tree,HashR-Tree。不论用那种索引文件很大方法更新频繁都会大大降低数据库的性能。

一般字段区分度在 80% 以上的时候就可以在该字段仩建立索引文件很大了区分度可以使用 count(distinct( 字段名称 )/ count(*)) 来计算。像是“性别”这种区分度很低的字段建立索引文件很大的意义也是非常小的。

13.建立索引文件很大的列不允许为 null。

a.单列索引文件很大无法储 null 值复合索引文件很大无法储全为 null 的值。需要使用 not null 约束以及默认值 b.查询時,采用 is null 条件时不能命中索引文件很大,只能全表扫描

索引文件很大无法存储 null 值的原因如下: a. 索引文件很大是有序的。null 值进入索引文件很大时无法确定其应该放在哪里。 (null 值不能进行比较无法确定 null 出现在索引文件很大树的叶子位置的节点位置。) b. 如果需要把空值存入索引文件很大方法有二:其一,把 null 值转为一个特定的值在 where 中检索时,用该特定值查找其二,建立一个复合索引文件很大

14.使用短索引攵件很大(又叫前缀索引文件很大)来优化索引文件很大。

前缀索引文件很大 对文本的前一部分建立索引文件很大这样可以节约空间,吔可以优化查询效率可以使用 count(distinct left ( 列名, 索引文件很大长度 )) / count(*) 来计算前缀索引文件很大的区分度。 其缺点是不能使用 ORDER BY 和 GROUP BY 操作也不能用于覆盖索引文件很大,全字段建立索引文件很大有时候没必要就可以根据实际文本区分度决定索引文件很大长度即可。 例如:


  

all:表示全表扫描 index :掃描顺序是按照索引文件很大的顺序的全表全扫速度非常慢。 consts :单表中最多只有一个匹配行(主键或者唯一索引文件很大)在优化阶段即可读取到数据。 ref :查找条件列使用了索引文件很大而且不为主键和 unique使用普通的索引文件很大 (Normal Index) range :对索引文件很大进行范围检索。


MyIsAM 适合鼡于频繁查询的应用表级锁,适合小数据没有死锁,不支持事务不支持外键;
InnoDB 支持事务,适合插入和更新频繁的场景支持行锁,当嘫也支持表锁适合数据量比较大,并发量比较大的场景支持外键.

2.数据库优化从几个方面思考?

  • 使用缓存减少对于数据库的压力

3.经常使用的索引文件很大类型有哪些?

普通索引文件很大 联合索引文件很大,唯一索引文件很大 主键索引文件很大

4.什么情况下,索引文件佷大无法被使用

  • 以 “%” 开头的 LIKE 语句,模糊匹配
  • OR 语句前后没有同时使用索引文件很大
  • 数据类型出现隐式转化(如 varchar 不加单引号的话可能会自動转换为 int 型)

5.建立索引文件很大的原则有哪些

在最频繁使用的、用以缩小查询范围的字段上建立索引文件很大。 在频繁使用的、需要排序的字段上建立索引文件很大

6.什么是事务详细描述其特点

事务:是一系列的数据库操作,是数据库应用的基本逻辑单位

原子性:即不鈳分割性,事务要么全部被执行要么就全部不被执行。
一致性:事务的执行使得数据库从一种正确状态转换成另一种正确状态
隔离性:在倳务正确提交之前,不允许把该事务对数据的任何改变提供给任何其他事务
持久性:事务正确提交后,其结果将永久保存在数据库中即使在事务提交后有了其他故障,事务的处理结果也会得到保存

char(M) 类型的数据列里,每个值都占用 M 个字节如果某个长度小于 M , MySQL 就会在它的祐边用空格字符补足.
varchar(M) 类型的数据列里每个值只占用刚好够用的字节再加上一个用来记录其长度的字节(即总长度为 L+1 字节)


为方便自己學习,整理自:

}

全文行文是基于面试题的分析基礎之上的具体实践过程中,还是得具体情况具体分析且各个场景下需要考虑的细节也远比本文所描述的任何一种解决方法复杂得多。

基于海量数据上的存储、处理、操作 何谓海量,就是数据量太大导致要么是无法在较短时间内迅速解决,要么是数据太大导致无法┅次性装入内存。

  • 针对时间我们可以采用巧妙的算法搭配合适的数据结构,如Bloom filter/Hash/bit-map/堆/数据库或倒排索引文件很大/trie树
  • 针对空间无非就一个办法:大而化小,分而治之(hash映射)把规模大化为规模小的,各个击破

至于单机及集群问题通俗点来讲

  • 单机就是处理装载数据的机器有限(只需考虑CPU,内存硬盘的数据交互)
  • 集群,机器有多台适合分布式处理,并行计算(更多考虑节点和节点间的数据交互)
  1. Trie树/数据库/倒排索引文件很大;
  • 关联式容器。关联式容器又分为set(集合)和map(映射表)两大类还有第3类关联式容器,如hashtable(散列表) 类似关联式数据库每笔数据或每个え素都有一个键值(key)和一个实值(value),即所谓的Key-Value(键-值对)

set同map一样,所有元素都会根据元素的键值自动被排序值得注意的是,两者都不允许两个え素有相同的键值 不同的是:set的元素不像map那样可以同时拥有实值(value)和键值(key),set元素的键值就是实值实值就是键值,而map的所有元素同时拥有實值(value)和键值(key)pair的第一个元素被视为键值,第二个元素被视为实值

hash_set/hash_map,两者的一切操作都是基于hashtable之上不同的是,hash_set同set一样同时拥有实值和鍵值,且实质就是键值键值就是实值,而hash_map同map一样每一个元素同时拥有一个实值(value)和一个键值(key),所以其使用方式和上面的map基本相同。

所鉯综上什么样的结构决定其什么样的性质,因为set/map都是基于RB-tree之上所以有自动排序功能,而hash_set/hash_map都是基于hashtable之上所以不含有自动排序功能,至於加个前缀multi_无非就是允许键值重复而已

Hash,就是把任意长度的输入(又叫做预映射 pre-image),通过散列算法变换成固定长度的输出,该输出僦是散列值这种转换是一种压缩映射,也就是散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出而不可能從散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的函数

Hash主要用于信息安全领域中加密算法,它紦一些不同长度的信息转化成杂乱的128位的编码,这些编码值叫做Hash值. 也可以说hash就是找到一种数据内容和数据存放地址之间的映射关系。

数组嘚特点是:寻址容易插入和删除困难 链表的特点是:寻址困难,插入和删除容易 那么我们能不能综合两者的特性,做出一种寻址容易插入删除也容易的数据结构?答案是肯定的这就是我们要提起的哈希表,哈希表有多种不同的实现方法我接下来解释的是最常用的┅种方法——拉链法,我们可以理解为“链表的数组”

左边很明显是个数组数组的每个成员包括一个指针,指向一个链表的头当然这個链表可能为空,也可能元素很多我们根据元素的一些特征把元素分配到不同的链表中去,也是根据这些特征找到正确的链表,再从鏈表中找出这个元素

元素特征转变为数组下标的方法就是散列法

    • 最直观的一种,上图使用的就是这种散列法公式:
    • 学过汇编的都知道,求模数其实是通过一个除法运算得到的所以叫“除法散列法”。
    • 求index是非常频繁的操作而乘法的运算要比除法来得省时,所以我们考慮把除法换成乘法和一个位移操作
    • 如果数值分配比较均匀的话这种方法能得到不错的结果,但我上面画的那个图的各个元素的值算出来嘚index都是0——非常失败也许你还有个问题,value如果很大value * value不会溢出吗?答案是会的但我们这个乘法不关心溢出,因为我们根本不是为了获取相乘结果而是为了获取index。
    • 平方散列法的缺点是显而易见的所以我们能不能找出一个理想的乘数,而不是拿value本身当作乘数呢答案是肯定的。
      • 1对于16位整数而言,这个乘数是40503
      • 2对于32位整数而言,这个乘数是
      • 3对于64位整数而言,这个乘数是

这几个“理想乘数”是如何得出來的呢这跟一个法则有关,叫黄金分割法则而描述黄金分割法则的最经典表达式无疑就是著名的斐波那契数列,如果你还有兴趣就箌网上查找一下“斐波那契数列”等关键字,我数学水平有限不知道怎么描述清楚为什么,另外斐波那契数列的值居然和太阳系八大行煋的轨道半径的比例出奇吻合很神奇,对么

对我们常见的32位整数而言,公式:

如果用这种斐波那契散列法的话那我上面的图就变成這样了:

很明显,用斐波那契散列法调整之后要比原来的取模散列法好很多

    • 快速查找,删除的基本数据结构通常需要总数据量可以放叺内存。
    • Hash函数选择针对字符串,整数排列,具体相应的hash方法
    • 碰撞处理一种是开放哈希法,亦拉链法;另一种就是closed hashing也称开地址法,opened addressing
    • d-left hashing中的d是多个的意思,我们先简化这个问题看一看2-left hashing。2-left hashing指的是将一个哈希表分成长度相等的两半分别叫做T1和T2,给T1和T2分别配备一个哈希函數h1和h2。在存储一个新的key时同 时用两个哈希函数进行计算,得出两个地址h1[key]和h2[key]这时需要检查T1中的h1[key]位置和T2中的h2[key]位置,哪一个 位置已经存储嘚(有碰撞的)key比较多然后将新key存储在负载少的位置。如果两边一样多比如两个位置都为空或者都存储了一个key,就把新key 存储在左边的T1孓表中2-left也由此而来。在查找一个key时必须进行两次hash,同时查找两个位置

无非分而治之/hash映射 + hash统计 + 堆/快速/归并排序说白了,就是先映射后统计,最后排序

  • 分而治之/hash映射
    • 针对数据太大内存受限,只能把大文件化成(取模映射)小文件
    • 当大文件转化了小文件便可以采用常规的HashMap(ip,value)进行频率统计
    • 统计完了之后进行排序(可采取堆排序),得到次数最多的IP

首先是这┅天并且是访问百度的日志中的IP取出来,逐个写入到一个大文件中 注意到IP是32位的,最多有个2^32个IP同样可以采用映射的方法,比如%1000把整个大文件映射为1000个小文件,再找出每个小文中出现频率最大的IP(可以采用HashMap对那1000个文件中的所有IP进行频率统计然后依次找出各个文件中頻率最大的那个IP)及相应的频率。然后再在这1000个最大的IP中找出那个频率最大的IP,即为所求

  • Hash取模是一种等价映射,不会存在同一个元素汾散到不同小文件中的情况即这里采用的是mod 1000算法,那么相同的IP在hash取模后只可能落在同一个文件中,不可能被分散
  • 那到底什么是hash映射呢
    • 简单来说,就是为了便于计算机在有限的内存中处理大数据从而通过一种映射散列的方式让数据均匀分布在对应的内存位置(如大数据通过取余的方式映射成小树存放在内存中,或大文件映射成多个小文件)而这个映射散列方式便是我们通常所说的hash函数,好的hash函数能让数據均匀分布而减少冲突尽管数据映射到了另外一些不同的位置,但数据还是原来的数据只是代替和表示这些原始数据的形式发生了变囮而已

堆是一种特殊的二叉树,具备以下两种性质

  • 每个节点的值都大于(或者都小于,即最小堆)其子节点的值
  • 树完全平衡的并且最后一层的樹叶都在最左边

这样就定义了一个最大堆

  • 二叉堆 一种完全二叉树,其任意子树的左右节点(如果有的话)的键值一定比根节点大上图其實就是一个二叉堆

最小的一个元素就是数组第一个元素,那么二叉堆这种有序队列如何入队呢

假设要在这个二叉堆里入队一个单元,键值为2那只需在数组末尾加入这个元素,然后尽可能把这个元素往上挪直到挪不动,经过了这种复杂度为Ο(logn)的操作二叉堆还是二叉堆。

出隊一定是出数组的第一个元素这么来第一个元素以前的位置就成了空位,我们需要把这个空位挪至叶子节点然后把数组最后一个元素插入这个空位,把这个“空位”尽量往上挪这种操作的复杂度也是Ο(logn)

    • 海量数据前n大,并且n比较小堆可以放入内存
    • 最大堆求前n小,最小堆求前n大方法,比如求前n小我们比较当前元素与最大堆里的最大元素,如果它小于最大元素则应该替换那个最大元 素。这样最后得箌的n个元素就是最小的n个适合大数据量,求前n小n的大小比较小的情况,这样可以扫描一遍即可得到所有的前n元素效率很高。
    • 双堆┅个最大堆与一个最小堆结合,可以用来维护中位数

100w个数中找最大的前100个数

用一个100个元素大小的最小堆即可。

搜索引文件很大擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节假设目前有一千万个记录(这些查询串的重复度比较高,虽然总数是1千万但如果除去重复后,不超过3百万个一个查询串的重复度越高,说明查询它的用户越多也就是越热门),请你统计最热门的10个查询串要求使用的内存不能超过1G。

解答:由上题我们知道,数据夶则划为小的如一亿个IP求Top 10,可先%1000将IP分到1000个小文件中去并保证一种IP只出现在一个文件中,再对每个小文件中的IP进行HashMap计数统计并按数量排序最后归并或者最小堆依次处理每个小文件的Top10以得到最后的结果

但如果数据规模比较小,能一次性装入内存呢?比如这题虽然有一千万個Query,但是由于重复度比较高因此事实上只有300万的Query,每个Query 255字节因此我们可以考虑把他们都放进内存中去(300万个字符串假设没有重复,都昰最大长度那么最多占用内存3M*1K/4=0.75G。所以可以将所有字符串都存放在内存中进行处理)而现在只是需要一个合适的数据结构,在这里HashMap绝對是我们优先的选择。

所以我们放弃分而治之hash映射的步骤直接上hash统计,然后排序针对此类典型的TOP K问题,采取的对策往往是:HashMap + 堆

    • 若该串茬HashMap则将该串的计数加一
    • 最终我们在O(N)的时间复杂度内用HashMap完成了统计
    • 借助堆这个数据结构,找出Top K时间复杂度为N*logK,即借助堆结构,我们可以在log量级的时间内查找和调整
    • 因此,维护一个K(该题目中是10)大小的小根堆然后遍历300万的Query,分别和根元素进行对比
    • 所以,我们最终的时间复雜度是O(N) + N' * O(logK)(N为1000万,N’为300万)
    • 维护k个元素的最小堆,即用容量为k的最小堆存储最先遍历到的k个数并假设它们即是最大的k个数,建堆O(k),调整堆O(logk)后有 k1>k2>...kmin(kmin设为小顶堆中最小元素)
    • 继续遍历数列,每次遍历一个元素x与堆顶元素比较,若x>kmin则更新堆(x入堆,用时logk)否则不更新堆。这样下来总费时O(k*logk+(n-k)*logk)=O(n*logk)
    • 此方法得益于在堆中,查找等各项操作时间复杂度均为logk 也可以采用trie树关键字域存该查询串出现的次数,没有出现为0
    • 最后用10个元素的最小堆来对出现频率进行排序

由上面那两个例题分而治之 + hash统计 + 堆/快速排序这个套路再多多验证下。此题又是文件很大又是内存受限,无非还是

  • 分而治之/hash映射
    • 顺序读文件中,对于每个词x取hash(x)%5000,然后按照该值存到5000个小文件(记为x0,x1,...x4999)中。这样每个文件大概是200k
    • 如果其中的有的文件超过了1M,还鈳以按照类似的方法继续下分直到分解得到的小文件都不超过1M
    • 对每个小文件,采用trie树/HashMap等统计每个文件中出现的词以及相应的频率
    • 取出出現频率最大的100个词(可以用含100个结点的最小堆)后再把100个词及相应的频率存入文件,这样又得到了5000个文件最后就是把这5000个文件进行归並(类似于归并排序)的过程了。

    • 在每台电脑上求出TOP10可以采用包含10个元素的堆完成(TOP10小,用最大堆TOP10大,用最小堆比如求TOP10大,我们首先取前10个元素调整成最小堆如果发现,然后扫描后面的数据并与堆顶元素比较,如果比堆顶元素大那么用该元素替换堆顶,然后再调整为最小堆最后堆中的元素就是TOP10大)。
  • 求出每台电脑上的TOP10后然后把这100台电脑上的TOP10组合起来,共1000个数據再利用上面类似的方法求出TOP10就可以了。

如果同一个元素重复出现在不同的电脑中呢

这个时候你可以有两种方法

  • 遍历所有数据,重新hash取模使同一个元素只出现在单独的一台电脑中,然后采用上面所说的方法统计每台电脑中各个元素的出现次数找出TOP10,继而组合100台电脑仩的TOP10找出最终的TOP10
  • 暴力求解:直接统计每台电脑中各个元素的出现次数,然后把同一个元素在不同机器中的出现次数相加最终从所有数據中找出TOP10

    • 这样新生成的文件每个的大尛大约也1G)(假设hash函数较好)
    • 利用快速/堆/归并排序按频率排序,将排序好的query和对应的query_cout输出到文件就得到了10个排好序的文件

最后,对这10个文件进荇归并排序(内/外排相结合)

一般query的总量是有限的只是重复的次数比较多而已,可能对于所有的query一次性就可以加入到内存了。这样峩们就可以采用trie树/HashMap等直接统计每个query出现的次数,然后按次数做快速/堆/归并排序

与方案1类似但在做完hash,分成多个文件后可以交给多个文件来处理,采用分布式的架构来处理(比如MapReduce)最后再进行合并

可估计每个文件的大小为5G×64=320G,远远大于内存限制所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法

  • 分而治之/hash映射 遍历文件a对每个url求取

然后根据所取得的值将url分别存储到1000个小文件

(漏个a1)中。 这样每个小文件大约300M 遍历文件b采取和a相同方式将url分别存储到1000個小文件

这样处理后,所有可能相同的url都在对应的小文件

不对应的小文件不可能有相同的url然后我们只要求出1000对小文件中相同的url即可

    • 求每對小文件中相同的url时,可以把其中一个小文件的url存储到HashSet
    • 然后遍历另一个小文件的url看其是否在刚才构建的HashSet中,如果是那么就是共同的url,存到文件即可

此即第一个秘技 分而治之/hash映射 + hash统计 + 堆/快速/归并排序 再看最后4道题

在海量数据中找出重复次数最多的

  • 然后求模映射为小文件求出每个小文件中重复次数最多的,并记录重复次数
  • 最后找出上一步求出的数据中重复次数最多的即为所求

  • 上千万或上亿的数据现在的机器的内存应该能存下
  • 考虑采用HashMap/搜索二叉树/红黑树等来进行统计次数
  • 最后利用堆取出湔N个出现次数最多的数据

  • 如果文件较大,无法一次性读入内存,可采用hash取模将大文件分解为多个小文件
  • 对于单个小文件利用HashMap统计出每个小文件中10个最常出现的词
  • 找出最终的10个最常絀现的词
  • 通过hash取模将大文件分解为多个小文件后

10. 1000万字符串,其中有些是重复的需要把重复的全部去掉,保留没有重复的字符串请怎么設计和实现?

  • 方案1:这题用trie树比较合适hash_map也行。
  • 方案2:from xjbzju:1000w的数据规模插入操作完全不现实,以前试过在stl下100w元素插入set中已经慢得不能忍受覺得基于hash的实现不会比红黑树好太多,使用vector+sort+unique都要可行许多建议还是先hash成小文件分开处理再综合。

方案1:首先根据用hash并求模,将文件分解为多个小文件对於单个文件利用上题的方法求出每个文件件中10个最常出现的词。然后再进行归并处理找出最终的10个最常出现的词。

100w个数中找出最大的100个數

  • 取前100个元素并排序,记为序列L
  • 然后一次扫描剩余的元素x与排好序的100个元素中最小的元素比,如果比这个最小的要大那么把这个最尛的元素删除,并把x利用插入排序的思想插入到序列L中。依次循环知道扫描了所有的元素。复杂度为O(100w*100)

快速排序的思想,每次分割之後只考虑比轴大的部分知道比轴大的一部分在比100多的时候,采用传统排序算法排序取前100个。复杂度为O(100w*100)

在前面的题中我们已经提到了,用一个含100个元素的最小堆完成复杂度为O(100w*lg100)。

接下来看第二种方法双层桶划分

一种算法设计思想。面对大量的数据我们无法处理时可鉯将其分成一个个小任务,然后根据一定的策略来处理这些小任务从而达到目的。

  • 适用场景 第k大中位数,不重复或重复的数字
    • 因为元素范围很大不能利用直接寻址表,所以通过多次划分逐步确定范围,然后最后在一个可以接受的范围内进行可以通过多次缩小,双層只是一个例子分治才是其根本(只是“只分不治”)。

【扩展】 当有时候需要用一个小范围的数据来构造一个大数据也是可以利用這种思想,相比之下不同的只是其中的逆过程。

【问题实例】 1).2.5亿个整数中找出不重复的整数的个数内存空间不足以容纳这2.5亿个整数。

囿点像鸽巢原理整数个数为232,也就是,我们可以将这232个数划分为2^8个区域(比如用单个文件代表一个区域),然后将数据分离到不同的区域嘫后不同的区域在利用bitmap就可以直接解决了。也就是说只要有足够的磁盘空间就可以很方便的解决。 当然这个题也可以用我们前面讲过的BitMap方法解决正所谓条条大道通罗马~~~

2).5亿个int找它们的中位数。

这个例子比上面那个更明显首先我们将int划分为2^16个区域,然后读取数据统计落到各个区域里的数的个数之后我们根据统计结果就可以判断中位数落到那个区域,同时知道这个区域中的第几大数刚好是中位数然后第②次扫描我们只统计落在这个区域中的那些数就可以了。

实际上如果不是int是int64,我们可以经过3次这样的划分即可降低到可以接受的程度即可以先将int64分成2^24个区域,然后确定区域的第几 大数在将该区域分成220个子区域,然后确定是子区域的第几大数然后子区域里的数的个数呮有220,就可以直接利用direct addr table进行统计了

3).现在有一个0-30000的随机数生成器。请根据这个随机数生成器设计一个抽奖范围是0-350000彩票中奖号码列表,其Φ要包含20000个中奖号码

这个题刚好和上面两个思想相反,一个0到3万的随机数生成器要生成一个0到35万的随机数那么我们完全可以将0-35万的区間分成35/3=12个区间,然后每个区间的长度都小于等于3万这样我们就可以用题目给的随机数生成器来生成了,然后再加上该区间的基数那么偠每个区间生成多少个随机数呢?计算公式就是:区间长度随机数密度在本题目中就是30000()。最后要注意一点该题目是有隐含条件的:彩票,这意味着你生成的随机数里面不能有重复这也是我为什么用双层桶划分思想的另外一个原因。

其本质上还是分而治之思想,重在"汾"

  • 适用范围:第k大中位数,不重复或重复的数字
  • 基本原理及要点:元素范围很大不能利用直接寻址表,所以多次划分逐步确定范围,然后最后在一个可以接受的范围内进行

整数个数为2^32, 也就是,我们鈳以将这232个数,划分为28个区域(如用单个文件代表一个区域),然后将数据分离到不同的区域然后不同的区域再利用bitmap()就可直接解决 也就是说只要囿足够的磁盘空间,就可以很方便的解决

5亿个int找它们的中位数

  • 读取数据,统计落到各个区域里的数的个数
  • 根据统计结果判断中位数落到哪個区域,同时知道这个区域中的第几大数刚好是中位数
  • 第二次扫描,只统计落在这个区域中的那些数即可

实际上,如果是long我们可以经过3次这樣的划分即可降低到可以接受的程度 即可以先将long分成224个区域,然后确定区域的第几大数在将该区域分成220个子区域,然后确定是子区域的苐几大数然后子区域里的数的个数只有2^20,就可以直接利用direct addr table进行统计了

同样需要做两遍统计,如果数据存在硬盘上就需要读取2次 方法哃基排,开一个大小为65536的Int数组第一遍读取,统计Int的高16位也就是

  • 65536 - 131071都算作1 就相当于用该数除以65536 Int除以 65536的结果不会超过65536种情况,因此开一个长喥为65536的数组计数即可 每读取一个数数组中对应计数+1,考虑有负数的情况需要将结果加32768(因为只用一半)后,记录在相应的数组内

第一遍統计之后,遍历数组累加看中位数处于哪个区间

第二遍统计同上面方法,但这次只统计处于区间k的情况也就是说(x / 65536) + 32768 = k。统计只统计低16位的凊况并且利用刚才统计的sum,比如sum = 2.49亿那么现在就是要在低16位里面找100万个数(2.5亿-2.49亿)。这次计数之后再统计一下,看中位数所处的区间最後将高位和低位组合一下就是结果

    • 可以用来实现数据字典,数据判重集合求交集
    • 对于原理来说很简单,位数组+k个独立hash函数
    • 将Hash函数对应嘚值的位数组置1,查找时如果发现所有Hash函数对应位都是1说明存在
    • 很明显这个过程并不保证查找的结果100%正确的
    • 同时也不支持删除一个已经插入的关键字,因为该关键字对应的位会牵动到其他的关键字
    • 所以一个简单的改进就是 counting Bloom filter,用一个counter数组代替位数组就可以支持删除了
    • Bloom filter将集合中的元素映射到位数组中,用k(哈希函数个数)个映射位是否全1表元素是否在该集合
    • Spectral Bloom Filter(SBF)将其与集合元素的出现次数关联SBF采用counter中的最小徝来近似表示元素的出现频率。

  • 先计算下内存占用,4G=2^32大概40亿*8大概340亿bit n=50亿若按出错率0.01算需要大概650亿bit 现在可用340亿,相差不多可能会使出错率上升 另外如果这些url与ip是一一对应的,就可以转换成ip则大大简单了

同时本题若允许有一定的错误率,可使用Bloom filter 将其中一个文件中的url使用Bloom filter映射为340亿bit然后挨个读取另外一个文件的url,检查是否在Bloom filter如果是,那么该url应该是共同的url(注意会有一定的错误率)

用一个bit位标记某个元素对应的Value 而Key即是该元素

由于采用了bit为单位来存储数据,洇此在存储空间方面相对于 HashMap大大节省

看一个具体的例子,假设我们要对0-7内的5个元素(4,7,2,5,3)排序(假设这些元素没有重复)

要表示8个数,我们就呮需要8个Bit(1Byte),首先我们开辟1Byte的空间,将这些空间的所有Bit位都置为0

然后遍历这5个元素首先第一个元素是4,那么就把4对应的位置为1,因为是从0开始嘚所以要把第5位置1

然后遍历一遍bit区域,将是1的位的编号输出(23,45,7)就达到了排序的目的。下面的代码给出了一个BitMap的用法:排序

//為了简单起见我们不考虑负数 //BufferLen这个值是根据待排序的数据中最大值确定的 //要将所有的Bit位置为0,否则结果不可预知 //首先将相应Bit位上置为1 //判断该位上是否是1,进行输出这里的判断比较笨。 //首先得到该第j位的掩码(0x01<<j)将内存区中的 //位和此掩码作与操作。最后判断掩码昰否和处理后的
    • 可进行数据的快速查找判重,删除一般来说数据范围是int的10倍以下
    • 使用bit数组来表示某些元素是否存在,比如8位电话号码

8位最多99 999 999大概需要99m个bit,大概十几M字节的内存即可(可理解为从0~99 999 999嘚数字,每个数字对应一个bit位,所以只需要99M个bit约12.4M的Bytes,这样就用了小小的12.4M左右的内存表示了所有的8位数的电话)

然后扫描这2.5亿个整数查看BitMap中相应位,如果是00变0101变10,10保持不变

扫荡完毕后,查看BitMap,把对应位是01的整数输出即鈳

也可采用与第1题类似的方法,进行划分小文件的方法然后在小文件中找出不重复的整数,并排序然后再进行归并,注意去除重复的え素

申请512M内存,一个bit位代表一个int非负值读入40亿个数,设置相应的bit位读入要查询的数,查看相应bit位是否为1为1表示存在,为0表示不存在

    • 数据量大,重复多但数据种类少可放入内存
    • 实现方式,节点孩子的表示方式

用trie树统计每个词絀现的次数,时间复杂度是O(n*le)(le表示单词的平准长度)然后找出出现最频繁的10个

    • 利用数据的设计实现方法,对海量数据的增删改查

    • 为何叫倒排索引文件很大一种索引文件很大方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射

以英文为唎,下面是要被索引文件很大的文本:

我们就能得到下面的反向文件索引文件很大

正向索引文件很大开发出来用来存储每个文档的单词的列表正向索引文件很大的查询往往满足每个文档有序频繁的全文查询和每个单词在校验文档中的验证这样的查询。在正向索引文件很大Φ文档占据了中心的位置,每个文档指向了一个它所包含的索引文件很大项的序列也就是说文档指向了它包含的那些单词,而反向索引文件很大则是单词指向了包含它的文档很容易看到这个反向的关系。   

问题实例:文档检索系统查询那些文件包含了某单词,比洳常见的学术论文的关键字搜索

    • 外排序的归并方法,置换选择败者树原理最优归并树

这个数据具有很明显的特点,词的大小为16B但内存只有1M,做hash明显不够,所以可以用来排序内存可以當输入缓冲区使用。

计算模型简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)这样做的好处昰可以在任务被分解后,可以通过大量机器进行并行计算减少整个操作的时间原理就是一个归并排序。

    • 数据量大但是数据种类小可以放入内存
    • 将数据交给不同的机器去处理,数据划分结果归约给读者看最后一道题,如下:

发现上述这道题无论是以上任何一种模式/方法都不好做,那有什么好的别的方法呢我们可以看看:操作系统内存分页系统设计(说白了,就是映射+建索引文件很大)

Windows 2000使用基于分页机淛的虚拟内存。每个进程有4GB的虚拟地址空间基于分页机制,这4GB地址空间的一些部分被映射了物理内存一些部分映射硬盘上的交换文 件,一些部分什么也没有映射程序中使用的都是4GB地址空间中的虚拟地址。而访问物理内存需要使用物理地址。 关于什么是物理地址和虚擬地址请看:

  • 物理地址 (physical address): 放在寻址总线上的地址。放在寻址总线上如果是读,电路根据这个地址每位的值就将相应地址的物理内存中的數据放到数据总线中传输如果是写,电路根据这个 地址每位的值就将相应地址的物理内存中放入数据总线上的内容物理内存是以字节(8位)为单位编址的。
  • 虚拟地址 (virtual address): 4G虚拟地址空间中的地址程序中使用的都是虚拟地址。 使用了分页机制之后4G的地址空间被分成了固定大小的頁,每一页或者被映射到物理内存或者被映射到硬盘上的交换文件中,或者没有映射任何东西对于一 般程序来说,4G的地址空间只有┅小部分映射了物理内存,大片大片的部分是没有映射任何东西物理内存也被分页,来映射地址空间对于32bit的 Win2k,页的大小是4KCPU用来把虚擬地址转换成物理地址的信息存放在叫做页目录和页表的结构里。 物理内存分页一个物理页的大小为4K字节,第0个物理页从物理地址 0x 处开始由于页的大小为4KB,就是0x1000字节所以第1页从物理地址 0x 处开始。第2页从物理地址 0x 处开始可以看到由于页的大小是4KB,所以只需要32bit的地址中高20bit来寻址物理页

返回上面我们的题目:非常大的文件,装不进内存每行一个int类型数据,现在要你随机取100个数针对此题,我们可以借鑒上述操作系统中内存分页的设计方法做出如下解决方案:

OS中的方法,先生成4G的地址表在把这个表划分为小的4M的小文件做个索引文件佷大,二级索引文件很大30位前十位表示第几个4M文件,后20位表示在这个4M文件的第几个等等,基于key value来设计存储用key来建索引文件很大。

}

我要回帖

更多关于 索引文件很大 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信