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

  1. RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
  2. byte[] bytes = new byte[];
  3. rngCsp.GetBytes(bytes);
  4. uint value = ReadBigEndian32(bytes[], bytes[], bytes[], bytes[]);

开发参考 (解析dat以及查询)

  1. public class IPSearch3Fast
  2. {
  3. private static readonly Lazy<IPSearch3Fast> lazy = new Lazy<IPSearch3Fast>(() => new IPSearch3Fast());
  4. public static IPSearch3Fast Instance { get { return lazy.Value; } }
  5. private IPSearch3Fast()
  6. {
  7. LoadDat();
  8. Watch();
  9. }
  10.  
  11. private string datPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"qqzeng-ip-ultimate.dat");
  12. private DateTime lastRead = DateTime.MinValue;
  13.  
  14. private long[,] prefmap = new long[, ];
  15. private uint[] endArr;
  16. private string[] addrArr;
  17. private byte[] data;
  18.  
  19. /// <summary>
  20. /// 初始化二进制 qqzeng-ip-ultimate.dat 数据
  21. /// </summary>
  22.  
  23. private void LoadDat()
  24. {
  25. data = File.ReadAllBytes(datPath);
  26.  
  27. for (int k = ; k < ; k++)
  28. {
  29. int i = k * + ;
  30. int prefix = k;
  31. long startIndex = ReadLittleEndian32(data[i], data[i + ], data[i + ], data[i + ]);
  32. long endIndex = ReadLittleEndian32(data[i + ], data[i + ], data[i + ], data[i + ]);
  33. prefmap[k, ] = startIndex; prefmap[k, ] = endIndex;
  34. }
  35.  
  36. uint RecordSize = ReadLittleEndian32(data[], data[], data[], data[]);
  37. endArr = new uint[RecordSize];
  38. addrArr = new string[RecordSize];
  39. for (int i = ; i < RecordSize; i++)
  40. {
  41. long p = + (i * );
  42. uint endipnum = ReadLittleEndian32(data[p], data[ + p], data[ + p], data[ + p]);
  43.  
  44. int offset = data[ + p] + ((data[ + p]) << ) + ((data[ + p]) << );
  45. int length = data[ + p];
  46.  
  47. endArr[i] = endipnum;
  48. addrArr[i] = Encoding.UTF8.GetString(data, offset, length);
  49. }
  50. }
  51. private void Watch()
  52. {
  53. FileInfo fi = new FileInfo(datPath);
  54. FileSystemWatcher watcher = new FileSystemWatcher(fi.DirectoryName)
  55. {
  56. IncludeSubdirectories = false,
  57. NotifyFilter = NotifyFilters.LastWrite,
  58. Filter = "qqzeng-ip-ultimate.dat",
  59. };
  60.  
  61. watcher.Changed += (s, e) =>
  62. {
  63.  
  64. var lastWriteTime = File.GetLastWriteTime(datPath);
  65.  
  66. if (lastWriteTime > lastRead)
  67. {
  68. //延时 解决 正由另一进程使用,因此该进程无法访问此文件
  69. Thread.Sleep();
  70.  
  71. LoadDat();
  72. lastRead = lastWriteTime;
  73. }
  74. };
  75. watcher.EnableRaisingEvents = true;
  76. }
  77.  
  78. /// <summary>
  79. /// ip快速查询方法
  80. /// </summary>
  81. /// <param name="ip">1.1.1.1</param>
  82. /// <returns></returns>
  83. public string Find(string ip)
  84. {
  85. long val = IpToInt(ip, out long pref);
  86. long low = prefmap[pref, ], high = prefmap[pref, ];
  87. long cur = low == high ? low : BinarySearch(low, high, val);
  88. return addrArr[cur];
  89. }
  90.  
  91. // 二分逼近 O(logN)
  92. private long BinarySearch(long low, long high, long k)
  93. {
  94. long M = , mid = ;
  95. while (low <= high)
  96. {
  97. mid = (low + high) / ;
  98. uint endipnum = endArr[mid];
  99. if (endipnum >= k)
  100. {
  101. M = mid;
  102. if (mid == )
  103. {
  104. break; //防止溢出
  105. }
  106. high = mid - ;
  107. }
  108. else
  109. low = mid + ;
  110. }
  111. return M;
  112. }
  113.  
  114. private long IpToInt(string ipString, out long prefix)
  115. {
  116. //高性能
  117. int end = ipString.Length;
  118. unsafe
  119. {
  120. fixed (char* name = ipString)
  121. {
  122.  
  123. int numberBase = ;
  124. char ch;
  125. long[] parts = new long[];
  126. long currentValue = ;
  127. int dotCount = ;
  128. int current = ;
  129. for (; current < end; current++)
  130. {
  131. ch = name[current];
  132. currentValue = ;
  133.  
  134. numberBase = ;
  135. if (ch == '')
  136. {
  137. numberBase = ;
  138. current++;
  139.  
  140. if (current < end)
  141. {
  142. ch = name[current];
  143. if (ch == 'x' || ch == 'X')
  144. {
  145. numberBase = ;
  146. current++;
  147. }
  148. }
  149. }
  150.  
  151. for (; current < end; current++)
  152. {
  153. ch = name[current];
  154. int digitValue;
  155.  
  156. if ((numberBase == || numberBase == ) && '' <= ch && ch <= '')
  157. {
  158. digitValue = ch - '';
  159. }
  160. else if (numberBase == && '' <= ch && ch <= '')
  161. {
  162. digitValue = ch - '';
  163. }
  164. else if (numberBase == && 'a' <= ch && ch <= 'f')
  165. {
  166. digitValue = ch + - 'a';
  167. }
  168. else if (numberBase == && 'A' <= ch && ch <= 'F')
  169. {
  170. digitValue = ch + - 'A';
  171. }
  172. else
  173. {
  174. break;
  175. }
  176.  
  177. currentValue = (currentValue * numberBase) + digitValue;
  178.  
  179. }
  180.  
  181. if (current < end && name[current] == '.')
  182. {
  183. parts[dotCount] = currentValue;
  184. dotCount++;
  185. continue;
  186. }
  187. break;
  188. }
  189. parts[dotCount] = currentValue;
  190. prefix = parts[];
  191. return (parts[] << ) | ((parts[] & 0xff) << ) | ((parts[] & 0xff) << ) | (parts[] & 0xff);
  192. }
  193. }
  194.  
  195. //简洁的 普通
  196.  
  197. //byte[] b = IPAddress.Parse(ip).GetAddressBytes();
  198. //prefix = b[0];
  199. // return ReadBigEndian32(b[0], b[1], b[2], b[3]);
  200. }
  201.  
  202. private uint ReadBigEndian32(byte a, byte b, byte c, byte d)
  203. {
  204. return (uint)((a << ) | (b << ) | (c << ) | d);
  205. }
  206.  
  207. private uint ReadLittleEndian32(byte a, byte b, byte c, byte d)
  208. {
  209. return (uint)(a | (b << ) | (c << ) | (d << ));
  210. }
  211. }
  212.  
  213. /*
  214. (调用例子):
  215. string result = IPSearch3Fast.Instance.Find("1.2.3.4");
  216. --> result="亚洲|中国|香港|九龙|油尖旺|新世界电讯|810200|Hong Kong|HK|114.17495|22.327115"
  217. */

开发代码:https://github.com/zengzhan/qqzeng-ip

最新IP数据库 存储优化 查询性能优化 每秒解析上千万的更多相关文章

  1. Mysql数据库调优和性能优化的21条最佳实践

    Mysql数据库调优和性能优化的21条最佳实践 1. 简介 在Web应用程序体系架构中,数据持久层(通常是一个关系数据库)是关键的核心部分,它对系统的性能有非常重要的影响.MySQL是目前使用最多的开 ...

  2. 一次使用 Redis 优化查询性能的实践

    因为我的个人网站 restran.net 已经启用,博客园的内容已经不再更新.请访问我的个人网站获取这篇文章的最新内容,一次使用 Redis 优化查询性能的实践 应用背景 有一个应用需要上传一组ID到 ...

  3. SQL SERVER 查询性能优化——分析事务与锁(五)

    SQL SERVER 查询性能优化——分析事务与锁(一) SQL SERVER 查询性能优化——分析事务与锁(二) SQL SERVER 查询性能优化——分析事务与锁(三) 上接SQL SERVER ...

  4. mysql笔记03 查询性能优化

    查询性能优化 1. 为什么查询速度会慢? 1). 如果把查询看作是一个任务,那么它由一系列子任务组成,每个子任务都会消耗一定的时间.如果要优化查询,实际上要优化其子任务,要么消除其中一些子任务,要么减 ...

  5. SQL Server查询性能优化——堆表、碎片与索引(二)

    本文是对 SQL Server查询性能优化——堆表.碎片与索引(一)的一些总结.  第一:先对 SQL Server查询性能优化——堆表.碎片与索引(一)中的例一的SET STATISTICS IO之 ...

  6. SQL Server查询性能优化——覆盖索引(二)

    在SQL Server 查询性能优化——覆盖索引(一)中讲了覆盖索引的一些理论. 本文将具体讲一下使用不同索引对查询性能的影响. 下面通过实例,来查看不同的索引结构,如聚集索引.非聚集索引.组合索引等 ...

  7. Redis 优化查询性能

    一次使用 Redis 优化查询性能的实践   应用背景 有一个应用需要上传一组ID到服务器来查询这些ID所对应的数据,数据库中存储的数据量是7千万,每次上传的ID数量一般都是几百至上千数量级别. 以前 ...

  8. MySQL查询性能优化(精)

    MySQL查询性能优化 MySQL查询性能的优化涉及多个方面,其中包括库表结构.建立合理的索引.设计合理的查询.库表结构包括如何设计表之间的关联.表字段的数据类型等.这需要依据具体的场景进行设计.如下 ...

  9. 高性能mysql 第六章查询性能优化 总结(上)查询的执行过程

    6  查询性能优化 6.1为什么查询会变慢 这里说明了的查询执行周期,从客户端到服务器端,服务器端解析,优化器生成执行计划,执行(可以细分,大体过程可以通过show profile查看),从服务器端返 ...

随机推荐

  1. python 对模块的应用你还得练点这些

    1.有如下字符串:n = "路飞学城"(编程题) - 将字符串转换成utf-8的字符编码的字节,再将转换的字节重新转换为utf-8的字符编码的字符串 - 将字符串转换成gbk的字符 ...

  2. django初探-创建简单的博客系统

    django第一步 1. django安装 pip install django print(django.get_version()) 查看django版本 2. 创建项目 打开cmd,进入指定目录 ...

  3. 常用Markdown公式整理 && 页内跳转注意 && Markdown preview

    目录: 常用Markdown公式及注意事项 标题 列表 链接 区块 代码块 / 引用  粗体和斜体 文字块 图片 表格 横线 页内跳转注意事项 其他重要需注意 Markdown preview 前提: ...

  4. 【Bootstrap】bootstrap-fileinput上传文件插件

    [bootstrap-fileinput] 这是个据传最好用的bootstrap相关联的文件上传控件,支持拖曳上传,多线程上传,上传文件预览等等功能. 首先还是说一下要引入的一些文件: <lin ...

  5. APP专业的开发公司都有这样一套开发流程,强烈建议收藏!

    下面让我们来剖析到底是如何开发App的呢? 1.App界面设计开发: 通过客户提出需求,需要头脑风暴得出合适的方案和设计理念; 确认页面风格,确定整个界面的布局.关键截面的设计.文字.及其他的设计 G ...

  6. 1077. Kuchiguse (20)

    The Japanese language is notorious for its sentence ending particles. Personal preference of such pa ...

  7. Tomcat服务器的常用配置

    1.如何修改端口号, tomcat启动后经常会报端口冲突, 怎么办 如果部署在Linux环境下面, 首先使用netstat -apn命令检查是否是真的端口已经被占用了 如果真的被占用,进入tomcat ...

  8. Beta No.2

    今天遇到的困难: 组员对github极度的不适应 Android Studio版本不一致项目难以打开运行 移植云端的时候,愚蠢的把所有项目开发环境全部搬上去.本身云的内存小,性能差,我们花费了太多时间 ...

  9. 201621123050 《Java程序设计》第14周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结与数据库相关内容. 2. 使用数据库技术改造你的系统 2.1 简述如何使用数据库技术改造你的系统.要建立什么表?截图你的表设计. 答 ...

  10. Bate测试报告

    1 测试中找出的bug Bug类型 总数 描述 修复的bug 10 1.注册成功并没有直接跳转到登录页面: 2.学校地区无限制,数字也可以: 3.虽然相同用户名不能注册,但是只是显示,注册失败,却没有 ...