zoukankan      html  css  js  c++  java
  • 最新IP数据库 存储优化 查询性能优化 每秒解析上千万

    高性能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

  • 相关阅读:
    协成
    进程与线程-多线程
    进程与线程2
    进程与线程
    socket编程
    常用模块二(hashlib、configparser、logging)
    异常处理
    python之路——面向对象进阶
    封装
    初识——面向对象
  • 原文地址:https://www.cnblogs.com/zengxiangzhan/p/8816243.html
Copyright © 2011-2022 走看看