热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

内存缓存与硬盘缓存访问速度的比较

   这两天在为一个应用做solr搜索方案定制的过程中,需要用到solr的fieldcache,在估算fieldcache需要的内存容量,缓存中key是int,value是两个64

      这两天在为一个应用做solr搜索方案定制的过程中,需要用到solr的fieldcache,在估算fieldcache需要的内存容量,缓存中key是int,value是两个64bit大小的long类型数组,数据量大约是8100w,64×8100w/1024/1024,大致需要10G的容量,

 然而服务器总共也只有8G内存,实在无法支持这么大容量的缓存数据。

         

    于是开始想是不是可以有其他的替换的方案,可以不需要使用这么大的缓存容量,有能满足缓存的需要。考虑是不是可以将fieldcache中的数据内存存放到硬盘中去,在调用的时候可以通过key值快速计算出文档中的偏移量从而量数据取出,因为直观感觉只要知道一个文件偏移量而取内存应该是很快的。

          

    光有感觉是不行的,还需要实际测试一下,测试硬盘访问速度到底和内存访问速度相差多大。

 

  初始化测试数据       

    分别写一个向内存中和向硬盘中写数据的代码,内容如下:

  1. 向内存中写

    Map data = new HashMap();
    for (int i = 0; i <2000000; i++) {
    data.put(i, new Long[] { (long) (i + 1), (long) (i + 2) });
    }

     

  2. 向硬盘中写

    import java.io.File;
    import java.io.RandomAccessFile;
    import org.apache.lucene.analysis.Analyzer;
    import org.apache.lucene.analysis.payloads.PayloadHelper;
    import org.apache.lucene.analysis.standard.StandardAnalyzer;
    import org.apache.lucene.document.Document;
    import org.apache.lucene.document.Field;
    import org.apache.lucene.index.IndexWriter;
    import org.apache.lucene.index.IndexWriterConfig;
    import org.apache.lucene.index.IndexWriterConfig.OpenMode;
    import org.apache.lucene.store.Directory;
    import org.apache.lucene.store.SimpleFSDirectory;
    import org.apache.lucene.util.Version;
    public class DocReplication {
    public static Analyzer analyzer;
    static {
    analyzer = new StandardAnalyzer(Version.LUCENE_34);
    }
    public static void main(String[] arg) throws Exception {
    RandomAccessFile randomFile = new RandomAccessFile(new File(
    "DocReplication.text"), "rw");
    Directory dir = new SimpleFSDirectory(new File("indexdir"));
    IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_34,
    analyzer);
    iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
    IndexWriter writer = new IndexWriter(dir, iwc);
    for (int i = 0; i <2000000; i++) {
    // 向一个随机访问文件中写
    randomFile.write(PayloadHelper.encodeInt(i));
    randomFile.write(long2Array(i + 1));
    randomFile.write(long2Array(i + 2));
    // 向lucene中document中写
    Document doc = new Document();
    doc.add(new Field("id", String.valueOf(i), Field.Store.YES,
    Field.Index.NOT_ANALYZED_NO_NORMS));
    doc.add(new Field("id2", String.valueOf(i), Field.Store.YES,
    Field.Index.NOT_ANALYZED_NO_NORMS));
    writer.addDocument(doc);
    System.out.println("point:" + randomFile.getFilePointer());
    }
    writer.commit();
    writer.close();
    randomFile.close();
    }
    static byte[] long2Array(long val) {
    int off = 0;
    byte[] b = new byte[8];
    b[off + 7] = (byte) (val >>> 0);
    b[off + 6] = (byte) (val >>> 8);
    b[off + 5] = (byte) (val >>> 16);
    b[off + 4] = (byte) (val >>> 24);
    b[off + 3] = (byte) (val >>> 32);
    b[off + 2] = (byte) (val >>> 40);
    b[off + 1] = (byte) (val >>> 48);
    b[off + 0] = (byte) (val >>> 56);
    return b;
    }
    }

     以上向内存中和向硬盘中写都是写200w条数据,在执行向硬盘中写的过程中分别是向lucene的索引文件和向RandomAccessFile随机文件中写,下面介绍一下用RandomAccessFile写的文件结构,文件中一条记录的数据结构,如图:
    《内存缓存与硬盘缓存访问速度的比较》
     一条记录的长度为20字节,只要拿到docid也就是key值就能计算出
    RandomAccessFile的文件偏移量=docid × 20。

     至于为什么要向lucene索引文件中写的原因是,想比较一下通过lucene的 indexread.get(docid) 方法取得到document的fieldvalue 的访问速度,和用RandomAccessFile访问缓存值的速度到底谁更快。

 

编写读数据测试案例

  1. 从自定义随机文件中读取

    public static void main(String[] args) throws Exception {
    RandomAccessFile randomFile = new RandomAccessFile(new File(
    "DocReplication.text"), "rw");
    long current = System.currentTimeMillis();
    for (int i = 0; i <100000; i++) {
    int docid = (int) (Math.random() * 2000000);
    randomFile.seek(docid * 20 + 4);
    randomFile.readLong();
    randomFile.readLong();
    }
    System.out.println((System.currentTimeMillis() - current) / 1000);
    randomFile.close();
    }

     

  2. 从内存中读取

    public static void main(String[] args) {
    Map data = new HashMap();
    for (int i = 0; i <2000000; i++) {
    data.put(i, new Long[] { (long) (i + 1), (long) (i + 2) });
    }
    long start = System.currentTimeMillis();
    Long[] row = null;
    long tmp = 0;
    for (int i = 0; i <100000; i++) {
    int doc = (int) (Math.random() * 2000000);
    row = data.get(doc);
    tmp = row[0];
    tmp = row[1];
    }
    System.out.println((System.currentTimeMillis() - start) );
    }

     

  3. 从lucene索引文件中随机访问

    public static void main(String[] args) throws Exception {
    Directory dir = new SimpleFSDirectory(new File("indexdir"));
    long start = System.currentTimeMillis();
    IndexReader reader = IndexReader.open(dir);
    Document doc = null;
    for (int i = 0; i <100000; i++) {
    int docid = (int) (Math.random() * 2000000);
    doc = reader.document(docid);
    doc.get("id");
    doc.get("id2");
    }
    System.out.println("consume:" + (System.currentTimeMillis() - start)/ 1000);
    }

     三个测试案例,都是从目标存储中从有200w数据量的cache中随机取出一个key,通过key取到value,这样的过程重复10w次,看看需要花费多少时间。

测试结果:

  从自定义随机文件中读取 从内存中读取 从lucene索引文件中随机访问
 总耗时 3717ms 75ms 1673ms

 

    从测试结果看,通过内存读cache是最快的,无疑和预想的结果是一致的,但是本来以为从自定义的随机文件中读取速度要比从lucene的indexreader来取要快些,但从测试结果看恰恰相反,从lucene的indexreader要比自定义随机文件快差不多一倍。

   比较之下,内存和磁盘的访问速度比是75比1673=1比22,速度还是相差挺大的,我很好奇,要是将磁盘改成SSD存储介质的话,磁盘访问速度会有多大提升,无奈现在测试环境中还没有SSD的服务器,改天找一台来测试一下,到时候再将测试结果公布一下。


推荐阅读
  • Lucene 全文检索技术入门
    一、搜索引擎的历史萌芽:Archie、Gopher起步:Robot(网络机器人)的出现与spider(网络爬虫)发展:excite、galax ... [详细]
  • PHP函数的工作原理与性能分析
    在编程语言中,函数是最基本的组成单元。本文将探讨PHP函数的特点、调用机制以及性能表现,并通过实际测试给出优化建议。 ... [详细]
  • 2023年最新指南:如何在PHP中屏蔽警告和错误
    本文详细介绍了如何在PHP中屏蔽警告和错误,包括多种方法和最佳实践,帮助开发者提升代码质量和安全性。 ... [详细]
  • 在一个整型数组中,除了两个数字只出现一次外,其他所有数字都出现了两次。编写一个程序来找出这两个只出现一次的数字。 ... [详细]
  • 本文探讨了 TypeScript 中泛型的重要性和应用场景,通过多个实例详细解析了泛型如何提升代码的复用性和类型安全性。 ... [详细]
  • Cookie学习小结
    Cookie学习小结 ... [详细]
  • 浅析python实现布隆过滤器及Redis中的缓存穿透原理_python
    本文带你了解了位图的实现,布隆过滤器的原理及Python中的使用,以及布隆过滤器如何应对Redis中的缓存穿透,相信你对布隆过滤 ... [详细]
  • 本文详细介绍了在MySQL中如何高效利用EXPLAIN命令进行查询优化。通过实例解析和步骤说明,文章旨在帮助读者深入理解EXPLAIN命令的工作原理及其在性能调优中的应用,内容通俗易懂且结构清晰,适合各水平的数据库管理员和技术人员参考学习。 ... [详细]
  • 本文深入解析了JDK 8中HashMap的源代码,重点探讨了put方法的工作机制及其内部参数的设定原理。HashMap允许键和值为null,但键为null的情况只能出现一次,因为null键在内部通过索引0进行存储。文章详细分析了capacity(容量)、size(大小)、loadFactor(加载因子)以及红黑树转换阈值的设定原则,帮助读者更好地理解HashMap的高效实现和性能优化策略。 ... [详细]
  • 本文深入探讨了NoSQL数据库的四大主要类型:键值对存储、文档存储、列式存储和图数据库。NoSQL(Not Only SQL)是指一系列非关系型数据库系统,它们不依赖于固定模式的数据存储方式,能够灵活处理大规模、高并发的数据需求。键值对存储适用于简单的数据结构;文档存储支持复杂的数据对象;列式存储优化了大数据量的读写性能;而图数据库则擅长处理复杂的关系网络。每种类型的NoSQL数据库都有其独特的优势和应用场景,本文将详细分析它们的特点及应用实例。 ... [详细]
  • 一:什么是solrSolr是apache下的一个开源项目,使用Java基于lucene开发的全文搜索服务器;Lucene是一个开放源代 ... [详细]
  • Redis 是一个高性能的开源键值存储系统,支持多种数据结构。本文将详细介绍 Redis 中的六种底层数据结构及其在对象系统中的应用,包括字符串对象、列表对象、哈希对象、集合对象和有序集合对象。通过12张图解,帮助读者全面理解 Redis 的数据结构和对象系统。 ... [详细]
  • 本文介绍了如何在Spring框架中使用AspectJ实现AOP编程,重点讲解了通过注解配置切面的方法,包括方法执行前和方法执行后的增强处理。阅读本文前,请确保已安装并配置好AspectJ。 ... [详细]
  • 深入解析Java中的空指针异常及其预防策略
    空指针异常(NullPointerException,简称NPE)是Java编程中最常见的异常之一。尽管其成因显而易见,但开发人员往往容易忽视或未能及时采取措施。本文将详细介绍如何有效避免空指针异常,帮助开发者提升代码质量。 ... [详细]
  • MySQL的查询执行流程涉及多个关键组件,包括连接器、查询缓存、分析器和优化器。在服务层,连接器负责建立与客户端的连接,查询缓存用于存储和检索常用查询结果,以提高性能。分析器则解析SQL语句,生成语法树,而优化器负责选择最优的查询执行计划。这一流程确保了MySQL能够高效地处理各种复杂的查询请求。 ... [详细]
author-avatar
凌彩霞_685
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有