高效能IP資料庫格式詳解
每秒解析1000多萬ip qqzeng-ip-ultimate.dat 3.0版
編碼:UTF8 位元組序:Little-Endian
返回規範欄位(如:亞洲|中國|香港|九龍|油尖旺|新世界電訊|810200|Hong Kong|HK|114.17495|22.327115)
------------------------ 檔案結構 -------------------------
// 檔案頭 4位元組
[IP段數量]
// 字首區 8位元組(4-4) 256*8
[索引區start第幾個][索引區end第幾個]
// 索引區 8位元組(4-3-1) ip段行數x8
[結束IP數字][地區流位置][流長度]
// 內容區 長度無限制
[地區資訊][地區資訊]……唯一不重複
------------------------ 檔案結構 ---------------------------
優勢:壓縮形式將資料儲存在記憶體中,通過減少將相同資料讀取到記憶體的次數來減少I/O.
較高的壓縮率通過使用更小的記憶體中空間提高查詢效能。
字首區為作為縮小查詢範圍,索引區和內容區長度一樣,
解析出來一次性載入到陣列中,查詢效能提高3-5倍!
壓縮:原版txt為38.5M,生成dat結構為3.68M 。
和上一版本2.0不同的是索引區去掉了[開始IP數字]4位元組,節省多1-2M。
3.0版本只適用[全球版],條件為ip段區間連續且覆蓋所有IPV4。
2.0版本適用[全球版][國內版][國外版]
效能:每秒解析1000多萬ip
建立:qqzeng-ip 於 2018-04-08
效能測試 ( CPU i7-7700K + DDR2400 16G + win10 X64 )
查詢【3.0】記憶體優化版 3414萬ip->3.318秒 每秒1028.93309222423萬次
查詢【3.0】記憶體優化版 4439萬ip->4.199秒 每秒1057.1564658252萬次
查詢【3.0】記憶體優化版 4056萬ip->3.821秒 每秒1061.50222454855萬次
查詢【3.0】記憶體優化版 1781萬ip->1.68秒 每秒1060.11904761905萬次
查詢【3.0】記憶體優化版 3862萬ip->3.66秒 每秒1055.1912568306萬次
查詢【3.0】記憶體優化版 3479萬ip->3.31秒 每秒1051.05740181269萬次
查詢【3.0】記憶體優化版 2892萬ip->2.713秒 每秒1065.97862145227萬次
查詢【3.0】記憶體優化版 3484萬ip->3.263秒 每秒1067.72908366534萬次
查詢【3.0】記憶體優化版 2699萬ip->2.548秒 每秒1059.26216640502萬次
查詢【3.0】記憶體優化版 88萬ip->0.087秒 每秒1011.49425287356萬次
查詢【3.0】記憶體優化版 161萬ip->0.153秒 每秒1052.28758169935萬次
查詢【3.0】記憶體優化版 91萬ip->0.088秒 每秒1034.09090909091萬次
查詢【3.0】記憶體優化版 42萬ip->0.041秒 每秒1024.39024390244萬次
查詢【3.0】記憶體優化版 159萬ip->0.152秒 每秒1046.05263157895萬次
查詢【3.0】記憶體優化版 88萬ip->0.084秒 每秒1047.61904761905萬次
查詢【3.0】記憶體優化版 123萬ip->0.118秒 每秒1042.37288135593萬次
查詢【3.0】記憶體優化版 106萬ip->0.101秒 每秒1049.50495049505萬次
查詢【3.0】記憶體優化版 61萬ip->0.059秒 每秒1033.89830508475萬次
查詢【3.0】記憶體優化版 177萬ip->0.169秒 每秒1047.33727810651萬次
查詢【3.0】記憶體優化版 106萬ip->0.101秒 每秒1049.50495049505萬次
查詢【3.0】普通優化版 1464萬ip->3.408秒 每秒429.577464788732萬次
查詢【3.0】普通優化版 352萬ip->0.803秒 每秒438.356164383562萬次
查詢【3.0】普通優化版 1357萬ip->3.042秒 每秒446.088099934254萬次
查詢【3.0】普通優化版 184萬ip->0.43秒 每秒427.906976744186萬次
查詢【3.0】普通優化版 752萬ip->1.697秒 每秒443.134944018857萬次
查詢【3.0】普通優化版 1795萬ip->4.032秒 每秒445.188492063492萬次
查詢【3.0】普通優化版 1823萬ip->4.076秒 每秒447.252208047105萬次
查詢【3.0】普通優化版 723萬ip->1.622秒 每秒445.745992601726萬次
查詢【3.0】普通優化版 136萬ip->0.319秒 每秒426.332288401254萬次
查詢【3.0】普通優化版 334萬ip->0.756秒 每秒441.798941798942萬次
查詢【3.0】普通優化版 636萬ip->1.435秒 每秒443.205574912892萬次
查詢【3.0】普通優化版 701萬ip->1.578秒 每秒444.233206590621萬次
查詢【3.0】普通優化版 1807萬ip->4.07秒 每秒443.980343980344萬次
查詢【3.0】普通優化版 489萬ip->1.105秒 每秒442.533936651584萬次
隨機生成 IP
RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider(); byte[] bytes = new byte[4]; rngCsp.GetBytes(bytes); uint value = ReadBigEndian32(bytes[0], bytes[1], bytes[2], bytes[3]);
開發參考 (解析dat以及查詢)
public class IPSearch3Fast { private static readonly Lazy<IPSearch3Fast> lazy = new Lazy<IPSearch3Fast>(() => new IPSearch3Fast()); public static IPSearch3Fast Instance { get { return lazy.Value; } } private IPSearch3Fast() { LoadDat(); Watch(); } private string datPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"qqzeng-ip-ultimate.dat"); private DateTime lastRead = DateTime.MinValue; private long[,] prefmap = new long[256, 2]; private uint[] endArr; private string[] addrArr; private byte[] data; /// <summary> /// 初始化二進位制 qqzeng-ip-ultimate.dat 資料 /// </summary> private void LoadDat() { data = File.ReadAllBytes(datPath); for (int k = 0; k < 256; k++) { int i = k * 8 + 4; int prefix = k; long startIndex = ReadLittleEndian32(data[i], data[i + 1], data[i + 2], data[i + 3]); long endIndex = ReadLittleEndian32(data[i + 4], data[i + 5], data[i + 6], data[i + 7]); prefmap[k, 0] = startIndex; prefmap[k, 1] = endIndex; } uint RecordSize = ReadLittleEndian32(data[0], data[1], data[2], data[3]); endArr = new uint[RecordSize]; addrArr = new string[RecordSize]; for (int i = 0; i < RecordSize; i++) { long p = 2052 + (i * 8); uint endipnum = ReadLittleEndian32(data[p], data[1 + p], data[2 + p], data[3 + p]); int offset = data[4 + p] + ((data[5 + p]) << 8) + ((data[6 + p]) << 16); int length = data[7 + p]; endArr[i] = endipnum; addrArr[i] = Encoding.UTF8.GetString(data, offset, length); } } private void Watch() { FileInfo fi = new FileInfo(datPath); FileSystemWatcher watcher = new FileSystemWatcher(fi.DirectoryName) { IncludeSubdirectories = false, NotifyFilter = NotifyFilters.LastWrite, Filter = "qqzeng-ip-ultimate.dat", }; watcher.Changed += (s, e) => { var lastWriteTime = File.GetLastWriteTime(datPath); if (lastWriteTime > lastRead) { //延時 解決 正由另一程式使用,因此該程式無法訪問此檔案 Thread.Sleep(1000); LoadDat(); lastRead = lastWriteTime; } }; watcher.EnableRaisingEvents = true; } /// <summary> /// ip快速查詢方法 /// </summary> /// <param name="ip">1.1.1.1</param> /// <returns></returns> public string Find(string ip) { long val = IpToInt(ip, out long pref); long low = prefmap[pref, 0], high = prefmap[pref, 1]; long cur = low == high ? low : BinarySearch(low, high, val); return addrArr[cur]; } // 二分逼近 O(logN) private long BinarySearch(long low, long high, long k) { long M = 0, mid = 0; while (low <= high) { mid = (low + high) / 2; uint endipnum = endArr[mid]; if (endipnum >= k) { M = mid; if (mid == 0) { break; //防止溢位 } high = mid - 1; } else low = mid + 1; } return M; } private long IpToInt(string ipString, out long prefix) { //高效能 int end = ipString.Length; unsafe { fixed (char* name = ipString) { int numberBase = 10; char ch; long[] parts = new long[4]; long currentValue = 0; int dotCount = 0; int current = 0; for (; current < end; current++) { ch = name[current]; currentValue = 0; numberBase = 10; if (ch == '0') { numberBase = 8; current++; if (current < end) { ch = name[current]; if (ch == 'x' || ch == 'X') { numberBase = 16; current++; } } } for (; current < end; current++) { ch = name[current]; int digitValue; if ((numberBase == 10 || numberBase == 16) && '0' <= ch && ch <= '9') { digitValue = ch - '0'; } else if (numberBase == 8 && '0' <= ch && ch <= '7') { digitValue = ch - '0'; } else if (numberBase == 16 && 'a' <= ch && ch <= 'f') { digitValue = ch + 10 - 'a'; } else if (numberBase == 16 && 'A' <= ch && ch <= 'F') { digitValue = ch + 10 - 'A'; } else { break; } currentValue = (currentValue * numberBase) + digitValue; } if (current < end && name[current] == '.') { parts[dotCount] = currentValue; dotCount++; continue; } break; } parts[dotCount] = currentValue; prefix = parts[0]; return (parts[0] << 24) | ((parts[1] & 0xff) << 16) | ((parts[2] & 0xff) << 8) | (parts[3] & 0xff); } } //簡潔的 普通 //byte[] b = IPAddress.Parse(ip).GetAddressBytes(); //prefix = b[0]; // return ReadBigEndian32(b[0], b[1], b[2], b[3]); } private uint ReadBigEndian32(byte a, byte b, byte c, byte d) { return (uint)((a << 24) | (b << 16) | (c << 8) | d); } private uint ReadLittleEndian32(byte a, byte b, byte c, byte d) { return (uint)(a | (b << 8) | (c << 16) | (d << 24)); } } /* (呼叫例子): string result = IPSearch3Fast.Instance.Find("1.2.3.4"); --> result="亞洲|中國|香港|九龍|油尖旺|新世界電訊|810200|Hong Kong|HK|114.17495|22.327115" */
開發程式碼:https://github.com/zengzhan/qqzeng-ip