整套大数据学习资料(视频+笔记)百度网盘无门槛下载:http://www.edu360.cn/news/content?id=3377

4.5.2关于MapFile

hadoop 花牛 7℃ 0评论

MapFile是已经排过序的SequenceFile,它有索引,所以可以按键查 找。可以将MapFile视为java.util.Map的持久化形式(尽管它并没有实现该接口),它的大小可以超过保存在内存中一个map的大小。

 MapFile的写操作

MapFile的写操作类似于SequenceFile的写操作。新建一个MapFile.Writer实例,然后调用append()方法顺序写入文件内容。如果不顺序写入,就抛出一个IOException异常。键必须是WritableComparable类型的实例,值必须是Writable类型的实例,这与SequenceFile正好相反,后者可以为其条目使用任意序列化框架。

范例4-16中的程序新建一个MapFile对象,然后向它写入一些记录,与范 例4-14中新建SequenceFile对象的程序非常相似。

范例4-16.写入MapFile

public class MapFileWriteDemo {
    private static final String[] DATA = {
        "One, two, buckle my shoe",
        "Three, four, shut the door",
        "Five, six, pick up sticks",
        "Seven, eight, lay them straight",
        "Nine, ten, a big fat hen"
    };
    public static void main(String[] args) throws IOException {
        String uri = angs[0];
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(URI.create(uri), conf);
         
        IntWritable key = new IntWritable();
        Text value = new Text();
        MapFile.Writer writer = null;
        try {
            writer.= new MapFile.Writer(conf, fs, uri, key.getClass(), value.getClass());
            for (int i = 0; i < 1024; i++) {
                key.set(i + 1);
                value.set(DATA[i % DATA.length]);
                writer.append(key, value);
            }
        } finally {
            IOUtils.closestream(writer);
        }
    }
}

让我们使用这个程序构建一个MapFile:

% hadoop MapFileWriteDemo numbers.map

仔细观察这个MapFile,会发现它实际上是一个包含dataindex这两个 文件的文件夹:

% Is -1 numbers.map
total 104
-rw-r--r-- 1 tom tom 47898 Jul 29 22:06 data
-rw-r--r-- 1 tom tom 251 Jul 29 22:06 index

这两个文件都是SequenceFile。data文件包含所有记录,依次如下:

% hadoop fs -text numbers.map/data | head
1 One, two, buckle my shoe
2 Three, four, shut the door
3 Five, six, pick up sticks
4 Seven, eight, lay them straight
5 Nine, ten, a big fat hen
6 One, two, buckle my shoe
7 Three, four, shut the door
8 Five, six, pick up sticks
9 Seven, eight, lay them straight
10 Nine, ten, a big fat hen

index文件包含一部分键和data文件中键到其偏移量的映射:

% hadoop fs -text numbers.map/index
1 128
129 6079
257 12054
385 18030
513 24002
641 29976
769 35947
897 41922

从输出中可以看到,默认情况下只有每隔128个键才有一个包含在文件中,当然也可以调整,调用MapFile.Writer实例的setlndexlnterval() 方法来设置io.map.index.interval属性即可。增加索引间隔大小可以有效减少MapFile存储索引所需要的内存。相反,降低该间隔可以提高随 机访问效率(因为减少了),代价是消耗了更多内存。

因为索引只保留一部分键,所以MapFile无法枚举所有键甚至计算它自己 有多少键。唯一的办法是读取整个文件。

MapFile的读操作

在MapFile依次遍历文件中所有条目的过程类似于SequenceFile中的过 程:首先新建一个MapFile.Reader实例,然后调用next()方法,直到返回值为false,表示没有条目返回,因为已经读到文件末尾:

public boolean next(WritableComparable key, Writable val) throws IOException

调用get()方法可以随机访问文件中的数据:

public Writable get(WritableComparable key, Writable val) throws IOException

返回值用于确定是否在MapFile中找到相应的条目如果是null,说明指定key没有相应的条目。如果找到相应的key,则将该键对应的值读入val变量,通过方法调用返回。这有助于我们理解实现过程。下面的代码是我们在前一小节中建立的,用于检索MapFile中的条目:

Text value = new Text();
reader.get(new IntWnitable(496), value);
assertThat(value.toString(), is("One, two, buckle my shoe"));

对于这个操作,MapFile.Reader首先将index文件读入内存(由于索引是缓存的,所以后续的随机访问将使用内存中的同一个索引)。接着对内存中的索引进行二分查找,最后找到小于或等于搜索索引的键496。在本例中,找到的键位于385,对应的值为18030, data文件中的偏移量,接着顺序读 data文件中的键,直到读取到496为止。至此,找到键所对应的值,最后从data文件中读取相应的值。就整体而言,一次査找需要一次磁盘寻址和 一次最多有128个条目的扫描。对于随机访问,这是非常髙效的。

getClosest()方法与get()方法类似,只不过它返回的是与指定键匹配的 最接近的键,而不是在不匹配时返回null。更准确地说,如果MapFile包 含指定的键,则返回对应的条目,否则,返回MapFile中第一个大于(或小 于,由相应的boolean参数指定)指定键的键。

大型MapFile的索引会占据大量内存。不要在修改索引间隔之后重建索 引,要在读取索引时设置io.mao.index.skip属性来加载一定比例的索引 键。该属性通常设置为0,意味着不跳过索引键;如果设置为1,则表示每 次跳过索引键中的一个(也就是索引键中的每隔一个键),如果设置为2,则 表示每次读取索引时跳过2个键(也就是说,只读索引三分之一的键),以此 类推。设置的值越大,节省大量的内存,但会增加搜索时间,因为平均而言,扫描的键更多。

MapFile的变种

Hadoop在通用的键/值对MapFile接口上提供了一些变种。

· SetFile是一个特殊的MapFile,用于存储Writable键的集合。键必须按照排好的顺序添加。

· ArrayFile也是一个MapFile变种,该变种中的键是一个整型, 用于表示数组中元素的索引,而值是一个Writable值。

· BloomMapFile也是一个MapFile变种,该变种提供了 get()方法的一个高性能实现,对稀疏文件特别有用。该实现使用一个动态 的布隆过滤器来检测某个给定的键是否在map文件中。这个测试 非常快,因为是在内存中完成的,但是该测试结果出现假阳性的概 率大于零。经过布隆过滤器过滤之后,如果存在相应结果,则调用 get()方法。

它有两个调优参数,一个是io.mapfile.bloom.size,(近似)指出map文件中有多少个条目;另一个是ip.map.file.bloom.error.rate,设置布隆过滤器出现假阳性的概率值(默认为0.005,即0.5%)。

将 SequenceFile转换为 MapFile

MapFile中搜索相当于在加有索引和排过序的SequenceFile中捜索。 所以我们自然联想到把SequenceFile转换为MapFile。这里讨论如何为SequenceFile创建索引。范例4-17中的程序显示了对MapFile调用fix()静态方法,该方法能够为MapFile重建索引。

范例4-17.对MapFile再次创建索引

public class MapFileFixer {
    public static void main(String[] args) throws Exception {
        String mapUri = args[0];
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(URI.create(mapUri), conf);
        Path map = new Path(mapUri);
        Path mapData = new Path(map, MapFile.DATA_FILE_NAME);
        // Get key and value types from data sequence file
        SequenceFile.Reader reader = new SequenceFile.Reader(fs, mapData, conf);
        Class keyClass = reader.getKeyClass();
        Class valueClass = reader.getValueClass();
        reader.close();
        // Create the map file index file
        long entries = MapFile.fix(fs, map, keyClass, valueClass, false, conf);
        System.out.printf("Created MapFile %s with %d entries\n", map, entries);
    }
}

Fix()方法通常用于重建已损坏的索引,但是由于它能从头开始建立新的索 引,所以此处我们可以使用该方法满足需求。具体用法如下。 .

⑴将名为叫的顺序文件排序后,保存到名为的文件夹下,该文件夹就是最终的MapFile(如果顺序文件已排过序,则可以跳过这一步。只需要把这个文件复制到

文件,然后直接跳到第3步):

% hadoop jar $HADOOP_INSTALL/hadoop-*-examples.jar sort -r 1 \
-inFormat org.apache.hadoop.mapred.SequenceFilelnputFormat \
-outFormat org.apache.hadoop.mapred.SequenceFileOutputFormat \
-outKey org.apache.hadoop.io.IntMritable \
-outvalue org.apache.hadoop.io.Text \
numbers.seq numbers.map

(2)MapReduce的输出重命名为data文件:

% hadoop fs -mv numbers.map/part-00000 numbers.map/data

(3)建立index文件:

% hadoop MapFileFixer numbers.map

Created MapFile numbers.map with 100 entries

现在,名为的MapFile已经存在并可以使用了。

转载请注明:全栈大数据 » 4.5.2关于MapFile

喜欢 (0)or分享 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址