背景:
低配設備I/O優化,利用mmap實現日誌的管理。
測試代碼:
public class MmapWriter { private static final int BUF_SIZE = 4096; private File mFile; private FileChannel mChannel; private RandomAccessFile raf; private long GROVE_SPACE = 1 * 1024 * 1024L; private MappedByteBuffer buffer; private long writeSize = 0L; public MmapWriter(String path) { // avg = 42612619225 /100 ns mFile = new File(path); try { if(mFile.exists()){ mFile.delete(); } raf = new RandomAccessFile(mFile, "rws"); mChannel = raf.getChannel(); } catch (FileNotFoundException e) { e.printStackTrace(); } } public static void main(String[] args) { long millis = System.currentTimeMillis(); System.out.println("--開始寫入--" ); try { for (int i = 0; i < 100; i++) { fisTest(); //buf=8192 (33355+33123)/200 | buf=2048 41929 | buf=4096 // mmapTest(); //buf=8192 (26417 + 25735)/200 |buf=2048 41685 |buf = 4096 31874 } //mmap 相比普通的讀寫,快70ms左右 } catch (Exception e) { e.printStackTrace(); }finally { System.out.println("--寫入完成-- cost="+(System.currentTimeMillis() - millis)); } } private static void fisTest(){ File file = new File("/Users/wiseyang/Downloads/FFmpeg從入門到精通.pdf"); FileInputStream fis = null; try { fis = new FileInputStream(file); File outFile = new File("FFmpeg從入門到精通.pdf"); if(outFile.exists()){ outFile.delete(); } BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outFile)); byte[] buf = new byte[BUF_SIZE]; int len; while ((len = fis.read(buf,0,buf.length))!=-1){ bos.write(buf,0,len); } bos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private static void mmapTest() throws IOException { MmapWriter writer = new MmapWriter("FFmpeg從入門到精通.pdf"); File file = new File("/Users/wintertian/Downloads/FFmpeg從入門到精通.pdf"); FileInputStream fis = new FileInputStream(file); byte[] buf = new byte[BUF_SIZE]; int len; while ((len = fis.read(buf,0,buf.length))!=-1){ writer.writeBuffer(buf,0,len); } writer.stopRecord(); } public void writeBuffer(byte[] frame, int pos, int bufferSize) { RandomAccessFile raf = this.raf; if (mChannel == null || frame == null || bufferSize <= 0) { return; } try { int n = 1; while ((buffer == null) || (writeSize + bufferSize) >= mChannel.size()) { buffer = mChannel.map(FileChannel.MapMode.READ_WRITE, writeSize, GROVE_SPACE * n); //mmap 需要擴容,不然會寫入失敗 n++; } buffer.put(frame, pos, bufferSize); writeSize += bufferSize; } catch (IOException e) { e.printStackTrace(); }catch (BufferOverflowException e){ e.printStackTrace(); } } @Override protected void finalize() throws Throwable { super.finalize(); stopRecord(); } public void stopRecord() { RandomAccessFile raf = this.raf; if (raf != null) { try { if (mChannel != null) { mChannel.truncate(writeSize); } raf.close(); } catch (IOException e) { e.printStackTrace(); } } this.raf = null; } }
結論:
buffer越大,mmap寫入越明顯
// fisTest(); //buf=8192 (33355+33123)/200 | buf=2048 41929 | buf=4096 40110 // mmapTest(); //buf=8192 (26417 + 25735)/200 |buf=2048 41685 | buf=4096 31874
mmap進行日誌管理的優缺點:
優點:斷電保護,寫入速度相對較快
缺點:
需要經常擴容,如果寫入前crash,容易造成空文件,造成磁盤空間浪費(當然可以改善,就是每次寫入時記錄寫入的位置,下次從寫入位置開始查找空數據)