要對系統的ES的寫入性能作優化,第一個想到的就是讀取ip的代碼。java
需求描述:系統會根據每條document的ip,找到一個表示行政區域的code,供地理圖的渲染使用。而實際IP有40多億,寫出來的json文件有7個G,太扯...python
目前是兩種方案,一種是ipip提供的二進制文件和sdk,1秒大概查10萬條,速度夠了,但並不能精確到縣;mysql
一種是找到到縣的數據,寫到mysql裏,由於在一個範圍內的ip地區相同,所以壓縮下來大概是12M條數據,查詢速度是1秒1萬左右。sql
結合兩種,把mysql裏的數據寫成二進制數據,ip爲4位unsigned int(在java裏是8位long),code爲4位int,簡單計算一下1200w條大體100MB的數據,加載到內存大體幾秒就能夠json
開始操做:數組
創建二進制文件(python):dom
sql = "select * from ip_code limit %d, 10000" % i cursor.execute(sql) area_data = cursor.fetchone() while area_data: file.write(struct.pack('I', area_data[0])) file.write(struct.pack('i', area_data[4])) area_data = cursor.fetchone()
struct爲python將類型轉化爲byte的庫,I表示無符號int,i表示int,area_data[0]和area_data[4]分別爲ip和區域codeeclipse
加載測試(java):jvm
import java.io.BufferedInputStream; import java.util.ArrayList; import java.util.Random; public class IpCode { public static ArrayList<Long> ips = new ArrayList<Long>(); public static ArrayList<Integer> codes = new ArrayList<Integer>(); public static int len = 0; public static int find(long ip){ int l=0,r=len-1; int mid; while(l <= r){ mid = (l+r)/2; if(ip == ips.get(mid)){ return codes.get(mid); } else if(ip > ips.get(mid)){ if(mid == len-1 || ip < ips.get(mid+1)){ return codes.get(mid); } else{ l = mid + 1; } } else{ if(mid == 0 || ip > ips.get(mid-1)){ return codes.get(mid); } else{ r = mid - 1; } } } return -1; } public static void main(String[] args){ // TODO Auto-generated method stub long startTime=System.currentTimeMillis(); //獲取開始時間 long totalMem = Runtime.getRuntime().totalMemory(); long maxMem = Runtime.getRuntime().maxMemory(); long freeMem = Runtime.getRuntime().freeMemory(); System.out.println("導入前內存檢測"); System.out.println(totalMem); System.out.println(maxMem); System.out.println(freeMem); byte b[] = new byte[1024]; int count; try { long x=0; int y=0; BufferedInputStream bin = (BufferedInputStream)(new IpCode().getClass().getResourceAsStream( "/ip_code_final.dat" )); while((count=bin.read(b))!=-1){ for(int i=0;i<count;i+=8){ x = (long)(b[i] & 0xFF) | (long)(b[i+1] & 0xFF) << 8 | (long)(b[i+2] & 0xFF) << 16 | (long)(b[i+3] & 0xFF) << 24; y = b[i+4] & 0xFF | (b[i+5] & 0xFF) << 8 | (b[i+6] & 0xFF) << 16 | (b[i+7]& 0xFF) << 24; ips.add(x); codes.add(y); len++; } } totalMem = Runtime.getRuntime().totalMemory(); maxMem = Runtime.getRuntime().maxMemory(); freeMem = Runtime.getRuntime().freeMemory(); System.out.println("導入後內存檢測"); System.out.println(totalMem); System.out.println(maxMem); System.out.println(freeMem); System.out.println(String.format("數組長度爲%d", len)); System.out.println(String.format("最後一條數據ip爲%d,區域代碼爲%d", x, y)); bin.close(); long endTime=System.currentTimeMillis(); //獲取結束時間 System.out.println("程序運行時間: "+(endTime-startTime)+"ms"); // 計算查詢時間 long max=3758095360L,min=16777472L; long all_time = 0; for(int i=0;i<100000;i++){ long ii = min + (((long) (new Random().nextDouble() * (max - min)))); startTime=System.currentTimeMillis(); //獲取結束時間 int code = find(ii); all_time += System.currentTimeMillis() - startTime; } System.out.println("搜索10萬條時間: "+all_time+"ms"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } catch(java.lang.OutOfMemoryError ee){ ee.printStackTrace(); } } }
運行結果:性能
java heap OOM........
因此要調大一下jvm的內存
eclipse => run => run configurations => Arguments => VM arguments
添加-Xms1024m -Xmx1024m
再運行:
導入前內存檢測
128974848
1908932608
126895344
導入後內存檢測
1103101952
1908932608
473296336
數組長度爲12945927
最後一條數據ip爲3758095360,區域代碼爲150100
程序運行時間: 6461ms
搜索10萬條時間: 61ms
這個速度就很是理想了,考慮以後把其它必要信息也加入,理論上不會有太多耗能