• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >Android > 高并发低基数多字段任意组合查询的优化

高并发低基数多字段任意组合查询的优化

作者:网友 字体:[增加 减小] 来源:互联网 时间:2017-05-26

网友通过本文主要向大家介绍了字段组合,sql 字段组合,数据透视表字段组合,crm 字段自定义,报表字段自定义显示等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

高并发低基数多字段任意组合查询的优化


1.问题

首先解释一下这个标题里出现的"低基数多字段任意组合查询"指什么东西。这里是指满足下面几个条件的查询:
1. 检索条件中涉及多个字段条件的组合
2. 这些字段的组合是不确定的
3. 每个单独字段的选择性都不好

这种类型的查询的使用场景很多,比如电商的商品展示页面。用户会输入各种不同查询条件组合:品类,供应商,品牌,促销,价格等等...,最后往往还要对结果进行排序和分页。

这类问题令人头疼的地方在于:
1. 记录数量众多,如果进行全表扫描性能低下无法满足高并发访问的要求。
2. 查询条件涉及的任何单个字段的选择性都很低,不能通过单字段索引解决查询效率问题。
3. 如果建立普通的Btree多字段索引,由于用户的输入条件组合太多,可能要建成百上千个索引,这不现实也很难维护。

2.方案

对这类问题我想到的解决方案有2种

2.1bitmap索引

bitmap的特点是存储key以及所有取值等于这个key的行集的bitmap,对于涉及多个key的组合查询,只需把这些key对应的bitmap做与或运算即可。由于bitmap的size很小,bit与或运算的效率也很高,所以bitmap非常适合做这类查询。
bitmap索引也有缺点,更新一条记录就会锁住整个表,不适合并发写比较多的场景。另外一个问题是,常见的关系数据库中支持bitmap索引的似乎只有Oracle一家,而我们很多时候我们想用开源数据库。

2.2 倒排索引

倒排索引和bitmap有相似之处,存储的是key和取值等于这个key的行集,行集可能是list也可能是tree或其它存储形式。对于多个key的组合查询,把这些key的结果做集合运算即可。
倒排索引一般用于全文检索,但很多系统也用它支持结构化数据的搜索,比如Elasticsearch。Elasticsearch支持JSON文档的快速搜索,支持复合查询,排序,聚合,分布式部署等很多不错的特性。但是考虑下面几个因素,我们更希望在关系数据库里找方案。
-不需要使用搜索引擎为模糊匹配提供的高级特性,实际上我们需要是精确匹配或者简单的模糊匹配。
-数据量还没有大到需要建一个分布式搜索集群。
-原始数据本来就在关系数据库里,不想烦心数据同步的问题。
-已经基于关系数据库的接口开发了应用,不想推倒重来。
-已经掌握了关系数据库的运维管理,对于全新的系统不知道还要踩多少坑。
-考虑到Java和C效能差异,关系数据库内建方案的性能未必输与专业的搜索引擎。

3.PostgreSQL的解法

如果把解决方案的范围限定在开源关系数据库,答案可能只有一个,就是PostgreSQL的gin索引。
PostgreSQL的gin索引就是倒排索引,它不仅被用于全文检索还可以用在常规的数据类型上,比如int,varchar。
对于多维查询我们可以这样建索引:
1. 对所有等值条件涉及的低基数字段,建立唯一一个多字段gin索引
2. 对选择性比较好的等值查询或范围查询涉及的字段,另外建btree索引

可能有同学会有疑问,同样是多字段索引,为什么gin的多字段索引只要建一个就可以了,而btree的多字段索引却要考虑各种查询组合建若干个。这是由于gin多字段索引中的每个字段是等价的,不存在前导字段的说法,所以只要建一个唯一的gin多字段索引就可以覆盖所有的查询组合;而btree多字段索引则不同,如果查询条件中不包含suoyi前导字段,是无法利用索引的。

多字段gin索引的内部存储的每个键是(column number,key datum)这样的形式,所以可以区分不同的字段而不致混淆。存储的值是匹配key的所有记录的ctid集合。这个集合在记录数比较多的情况下采用btree的形式存储,并且经过了压缩,所以gin索引占用的存储空间很小,大约只有等价的btree索引的二十分之一,这也从另一方面提升了性能。

对于多维查询涉及的多个字段,包含在多字段gin索引中的字段,由gin索引做ctid的集合归并(取并集或交集),然后得到的ctid集合和其它索引得到的ctid集合再做BitmapAnd或BitmapOr归并。gin索引内部的ctid集合归并效率远高于索引间的ctid集合归并,而且gin索引对低基数字段的优化更好,所以充分利用gin索引的特性比为每个字段单独建一个btree索引再通过BitmapAnd或BitmapOr归并结果集效率高的多。


4.一个真实的案例

4.1 原始查询

下面这个SQL是某系统中一个真实SQL的简化版。

  1. SELECT CASE WHEN gpppur.GB_BEGINDATE <= '2016-02-29 14:36:00' AND gpppur.GB_ENDDATE > '2016-02-29 14:36:00' THEN 1
  2. WHEN gpppur.PREVIEW_BEGINDT <= '2016-02-29 14:36:00' AND gpppur.PREVIEW_ENDDT > '2016-02-29 14:36:00' THEN 2
  3. ELSE 3 END AS flag,
  4. gpppur.*
  5. FROM T_MPS_INFO gpppur
  6. WHERE gpppur.ATTRACT_TP = 0
  7. AND gpppur.COLUMN_ID = 1
  8. AND gpppur.FIELD2 = 1
  9. AND gpppur.STATUS = 1
  10. ORDER BY flag ASC,gpppur.PC_SORT_NUM ASC,gpppur.GB_BEGINDATE DESC
  11. LIMIT 0,45
用的是MySQL数据库,总数据量是60w,其中建有FIELD2+STATUS的多字段索引。
查询条件涉及的4个字段的值分布情况如下:

  1. postgres=# select ATTRACT_TP,count(*) from T_MPS_INFO group by ATTRACT_TP;
  2. attract_tp | count
  3. ------------+--------
  4. | 16196
  5. 6 | 251
  6. 2 | 50
  7. 1 | 3692
  8. 3 | 143
  9. 10 | 314
  10. 4 | 214
  11. 5 | 194333
  12. 9 | 326485
  13. 7 | 1029
  14. 0 | 6458
  15. (11 rows)

  16. postgres=# select COLUMN_ID,count(*) from T_MPS_INFO group by COLUMN_ID;
  17. column_id | count
  18. ------------+--------
  19. | 2557
  20. 285 | 20
  21. 120 | 194
  22. 351 | 2
  23. 337 | 79
  24. 227 | 26
  25. 311 | 9
  26. 347 | 2
  27. 228 | 21
  28. 318 | 1
  29. 314 | 9
  30. 54 | 10
  31. 133 | 27
  32. 2147483647 | 1
  33. 336 | 1056
  34. 364 | 1
  35. 131 | 10
  36. 243 | 5
  37. 115 | 393
  38. 61 | 73
  39. 226 | 40
  40. 196 | 16
  41. 350 | 5
  42. 373 | 72
  43. 377 | 2
  44. 260 | 4
  45. 184 | 181
  46. 363 | 1
  47. 341 | 392
  48. 64 | 1
  49. 344 | 199271
  50. 235 | 17
  51. 294 | 755
  52. 352 | 3
  53. 368 | 1
  54. 225 | 1
  55. 199 | 8
  56. 374 | 2
  57. 248 | 8
  58. 84 | 1
  59. 362 | 1
  60. 361 | 331979
  61. 319 | 7
  62. 244 | 65
  63. 125 | 2
  64. 130 | 1
  65. 272 | 65
  66. 66 | 2
  67. 240 | 2
  68. 775 | 1
  69. 253 | 49
  70. 60 | 45
  71. 121 | 5
  72. 257 | 3
  73. 365 | 1
  74. 0 | 1
  75. 217 | 5
  76. 270 | 1
  77. 122 | 39
  78. 56 | 49
  79. 355 | 5
  80. 161 | 1
  81. 329 | 1
  82. 222 | 9
  83. 261 | 275
  84. 2 | 3816
  85. 57 | 19
  86. 307 | 4
  87. 310 | 8
  88. 97 | 37
  89. 202 | 20
  90. 203 | 3
  91. 85 | 1
  92. 375 | 641
  93. 58 | 98
  94. 1 | 6479
  95. 59 | 114
  96. 185 | 7
  97. 338 | 10
  98. 379 | 17
  99. (80 rows)

  100. postgres=# select FIELD2,count(*) from T_MPS_INFO group by FIELD2;
  101. field2 | count
  102. --------+--------
  103. | 2297
  104. 6 | 469
  105. 2 | 320
  106. 1 | 11452
  107. 3 | 286
  108. 10 | 394
  109. 4 | 291
  110. 5 | 200497
  111. 9 | 331979
  112. 0 | 2
  113. 7 | 1178
  114. (11 rows)

  115. postgres=# select STATUS,count(*) from T_MPS_INFO group by STATUS;
  116. status | count
  117. --------+--------
  118. | 2297
  119. 0 | 15002
  120. 3 | 5
  121. 4 | 1
  122. 1 | 531829
  123. 2 | 31
  124. (6 rows)

由于这几个字段的值分布极其不均的,我们构造下面这个lua脚本产生不同的select语句来模拟负载。
qx.lua:

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • 高并发低基数多字段任意组合查询的优化

相关文章

  • 2017-05-26再见NullPointerException。在Kotlin里null的处理(KAD 19),kotlinnull
  • 2017-05-26MySQL参数table_open_cache的设置
  • 2017-05-26解决 在Android开发上使用KSOAP2上传大图片到服务器经常报错的问题,
  • 2017-05-26android support的作用及其常见错误的解决,androidsupport
  • 2017-05-228.2.2 Bitmap引起的OOM问题
  • 2017-05-26android.invalidate(),android.invalidate
  • 2017-05-26一个帖子学会Android开发四大组件,android四大
  • 2017-05-26浅谈Kotlin(三):类,浅谈kotlin
  • 2017-05-26URL转换成二维码,url
  • 2017-05-26Android手机输入法按键监听-dispatchKeyEvent

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • 2.6.3 ViewPager的简单使用
    • 将Android系统源码导入Android studio的方法,androidstudio
    • android 水准仪的实现(方向传感器的使用)
    • 使用WakeLock使Android应用程序保持后台唤醒,wakelockandroid
    • 安卓开发—简单的登陆界面,安卓登陆界面
    • ELK部署参考文档
    • android TCP自动重连
    • android自定义activity,androidactivity
    • Android几种Tab的实现方法
    • win7系统连接WiFi上网信号很弱怎么办

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有