引子

《容易引起雪崩的两个处理》里,我提到一个慢查询的问题。本文先从整洁架构的角度讲讲慢查询sql完成的功能以及设计,再介绍对sql进行的实施测试现象以及思考。

设计讲解

一见杨过误终身,有多少萌妹子败给了一个痴字。金庸笔下的痴儿怨女数量之多就需要单独申请服务器,用独立存储单元进行存储。

先说神雕侠侣的杨过,陆无双和程英两姐妹苦等他16年最终等来了他和小龙女撒了一波狗粮而去;郭芙含怨20多年;完颜萍一生为杨过梦绕魂牵;郭襄做了尼姑;最惨公孙绿萼,为救他而死,用生命换来的解药杨过转身扔进绝情谷底,公孙绿萼在天上看到此情此景也会气的骂娘吧。

再说雪山飞狐的胡斐,出家的袁紫衣;为救他惨死的程灵素……

从领域驱动设计的角度讲,上面都是值对象。今天不讲值对象,讲实体。所以先拉回来。

功能简单来说如上图,就是从页面发起查询,查询入口有多个。有个页面是查询角色的仇人,比如杨过的仇人有金轮法王、公孙止、赵志敬……;有个页面是查询角色撩过的妹子,比如杨过撩过的妹子有凌洪波、陆无双、程英……;有些人在几个查询页面都能查到,比如李莫愁既是杨过撩过的妹子,又是杨过的仇人。这里我们把一种页面叫一个请求方。

请求方发起更新查询请求,比如请求方要查询杨过的所有仇人。请求经过转接系统将请求落库后转发到MQ。MQ异步返回包含杨过、胡斐在内的所有仇人的结果。转接系统根据结果反查请求信息,将结果存储。请求方3s后再发起结果查询,这时就会根据请求条件直接返回杨过的所有仇人。这里值得注意的是一次请求返回的结果有的100多条,有的200多条。就是说杨过、胡斐等这些金庸笔下的人物目前仇人列表总数是100多条;杨过、胡斐等这些金庸笔下的人物目前撩过的妹子列表总数是200多条。编辑还在不断录入数据,过一段时间,数据会变化。

功能的设计整体采用整洁架构中的事件溯源的变通方法。对于发起请求,只有增加和查询操作;对于回执结果,因为查询只会查询最新回执。所以一个请求方结果返回后会将所有之前的老数据更新为is_new=N,新插入记录is_new=Y。

回执结果记录数据表设计如下:

问题描述

这个查询系统受到不少人的喜爱,不知不觉间,查询次数过多,事件溯源的设计导致回执结果记录数据表积累了358万条数据。之前没有索引,结果出现一次请求查询20多秒,更新处理直接把数据库拖挂了。

《容易引起雪崩的两个处理》里,我讲解过给is_new加上索引,虽然is_new只有N和Y两个值,总体数据区分度不高,但是对于这种N和Y的值占比是万比一比例,只会查其中少的那部分,却是比普通索引有更高的效率。

问题来了,请求方的查询条件是where is_new=Y and 角色名=杨过 and 查询来源=仇人。我是该建立三个字段联合索引还是两个字段联合索引还是单个索引?

反直觉!经过试验:
1>is_new、角色名、查询来源 三个字段加索引
2>is_new、查询来源两个字段加索引

3>is_new、角色名两个字段加索引
4>is_new单个字段加索引
四种情况sql执行速度在ms级别没有任何差别!几次测试都是6ms返回!而更新时间虽然增加了建索引的时间,反而耗时大大减少!

原理分析

查询时间分析

上面所列的索引添加方式都是索引全中,假设is_new的数据共400多条,某查询来源的数据是一二百条,某角色名大概十几条。B+树底层:
1> is_new、角色名、查询来源 三个字段加索引时索引命中十几条,然后通过主键查到数据返回

2> is_new、查询来源  两个字段加索引时索引命中一二百条,然后扫描这一二百条数据,查到需要的十几条数据返回

3>is_new、角色名  两个字段加索引时索引命中二十几条,然后扫描这二十几条数据,查到需要的十几条数据返回

4>is_new单个字段加索引时索引命中400多条,然后扫描这400多条数据,查到需要的十几条数据返回

之所以单个索引和联合索引查询结果区别不大呢,是因为扫描的数据共400多条,按照数据库的处理能力来说不算什么,这6ms时间主要花在了组装数据和传输数据上。如果查询条件不命中(返回数据条数为0),查询时间几乎为0!

更新时间分析

上面提到每次查询都会把上次结果的is_new更新为N,新数据插入时is_new=Y。所以读写比例为1:1。更新性能就是不能不考虑的问题。当然最重要的是更新操作是数据库被拖挂的罪魁祸首。

update 回执结果记录数据表 set is_new=N where is_new=Y and 查询来源=仇人。

我测试了一下:

1> is_new、角色名、查询来源 三个字段加索引时更新时间90ms。

2> is_new、查询来源  两个字段加索引时更新时间60ms。

3>is_new、角色名 两个字段加索引时更新时间60ms。

4>is_new单个字段加索引时更新时间30ms。

这个更新操作,涉及索引重建。层数越多越慢不难理解。但是为什么时间是几十毫秒级别呢?没建索引之前查询都要20s。原因是B+树是树形结构。示意图如下所示,声明:下面的解释只是针对这个问题一个脑补过程,实际上有很多不严谨的地方。比如B+树还有最底层的叶子节点来存放数据。叶子节点之间有双向链表,与主题无关,没画那么细。

更新操作会首先进行一个查询,is_new=Y,然后会在is_new=Y这个范围内将is_new=Y涉及的一二百条数据,再到is_new=N下面自己的位置进行插入。如果是三层,每层的数据都需要先找自己的位置,最慢。这里面没有画的叶子节点是从左到右按id顺序排序的。如果只有一层is_new=Y的整个直接放到is_new=N下面的最后就可以了,最快。这就解释了四种更新方式的更新时间差异。

但是为什么加了索引和完全不加索引之间有有多于千倍的性能差异呢?更新操作的时间也主要是花在查询上。如果完全不加索引,一二百条数据每条插入前先进行查找,查找要全表扫描,358万条数据,16K为一个内存换页。我就不具体算了,但是要进行很多次内存换页才能查出来。还要乘以数据条数。而加了索引,因为有is_new=Y条件,进行一次内存换页就可以了。因为is_new=Y数据总共就400多条,1个内存页是可以存下的。所以一二百条数据中下一条就不需要内存换页了,查询总共就需要1次内存换页,基本不花什么时间。剩下的就是一条条插入具体位置了。

最终结论

通过上面比较,自然是只加单索引is_new最高效。

往期推荐

「前任的50种死法」开发踩坑案例--慢就是错

学会用数据说话-分布式锁究竟可以多少并发?

MySQL常见6个考题在实际工作中的运用

mybatis的本质和原理

一个反直觉的sql的更多相关文章

  1. 旋转VR相机不头晕:一个反直觉的发现

    旋转VR相机不头晕:一个反直觉的发现 本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/deta ...

  2. 【应用】_有道词典客户端一个后缀名为sql的数据库。

    [缘起] 在清理电脑磁盘的时候,看一看各安装文件夹有占用了多大容量,发现有道词典居然达140MB了,于是进去看看. 发现个有趣的文件:XXX.sql. 首先我们看一看它的安装文件夹的结构: Dict ...

  3. 一个小面试题sql

    一.            问答题 1简要说明分页是如何实现的. A:sqlserver: Select top(pagesize)  * from  student where id not in( ...

  4. 一个小时成功安装SQL Server 2012图解教程

    在安装微软最新数据库SQL Server 2012之前,编者先确定一下安装环境:Windonws 7 SP1,32位操作系统.CPU是2.1GHz赛扬双核T3500,内存2.93GB. 安装SQL S ...

  5. 从一个小例子认识SQL游标

    1    什么是游标: 关系数据库中的操作会对整个行集起作用. 例如,由 SELECT 语句返回的行集包括满足该语句的 WHERE 子句中条件的所有行. 这种由语句返回的完整行集称为结果集. 应用程序 ...

  6. 通过一个正则表达式,让SQL Server数据库的带参sql也支持位置参数语法!

    .NET Framework 2.0 中,Microsoft 在 System.Data.Common 名称空间下定义了一组类用来让程序员编写适用于不同数据库的数据访问代码,而且还在 Enterpri ...

  7. mysql一个非常实用解决sql查询优化的函数explain

    前言: 项目中有个表有几万条数据,然后还要关联其他表,查询条件也有点多,再没有其他查询条件下,默认时间查询条件如果是查一年的数据的话,查询时间那是慢得可怕. 找总监去优化sql,看他用了一个sql函数 ...

  8. 再次提供一个纯粹通过pl/sql解析json的方法。

    在github上面有一个叫pljson的项目,该项目就是用pl/sql 来解析json的. 项目地址:pljson(需翻|强),如果翻不了强的同学,我在国内克隆了一个副本,不定期同步更新 pljson ...

  9. 截取字符串中最后一个中文词语(MS SQL)

    有朋友需求一个问题,就是处理一张表中某一字段,从这个字段中去截取内容中最后一个中文词语. ID SourceText Result 1 张达:U:1杨英苹:U:1,周忱:U:1,;苗桥:U:1,章玮: ...

随机推荐

  1. 鸿蒙内核源码分析(内存映射篇) | 虚拟内存虚在哪里 | 百篇博客分析OpenHarmony源码 | v15.03

    百篇博客系列篇.本篇为: v15.xx 鸿蒙内核源码分析(内存映射篇) | 虚拟内存虚在哪里 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有哪些分 ...

  2. Loj#3026-「ROIR 2018 Day1」管道监控【Trie,费用流】

    正题 题目链接:https://loj.ac/p/3026 题目大意 给出\(n\)个点的一棵外向树,然后\(m\)个字符串和费用表示你每次可以花费这个费用覆盖路径字符串和给出字符串相等的路径,求覆盖 ...

  3. 淘宝商品html--网页结构

    淘宝商品html--网页结构 本篇爬虫紧接上一篇关于 泸州老窖 的爬虫随笔: import re import json def get_space_end(level): return ' ' * ...

  4. mysql8.0.20安装配置教程

    mysql配置安装教程 1.下载mysql8.0.20安装包 下载地址: https://dev.mysql.com/downloads/mysql/.找到安装包后下载.(官网为英文,如果看不懂的小伙 ...

  5. Bert文本分类实践(一):实现一个简单的分类模型

    写在前面 文本分类是nlp中一个非常重要的任务,也是非常适合入坑nlp的第一个完整项目.虽然文本分类看似简单,但里面的门道好多好多,作者水平有限,只能将平时用到的方法和trick在此做个记录和分享,希 ...

  6. 手把手教你 Docker部署可视化工具Grafana

    一.Grafana的简单介绍 Grafana是开源的.炫酷的可视化监控.分析利器,无论您的数据在哪里,或者它所处的数据库是什么类型,您都可以将它与Grafana精美地结合在一起.它还有丰富的套件供您选 ...

  7. 使用CEF(三)— 从CEF官方Demo源码入手解析CEF架构与CefApp、CefClient对象

    在上文<使用CEF(2)- 基于VS2019编写一个简单CEF样例>中,我们介绍了如何编写一个CEF的样例,在文章中提供了一些代码清单,在这些代码清单中提到了一些CEF的定义的类,例如Ce ...

  8. Serverless 对研发效能的变革和创新

    作者 | 杨皓然(不瞋) 对企业而言,Serverless 架构有着巨大的应用潜力.随着云产品的完善,产品的集成和被集成能力的加强,软件交付流程自动化能力的提高,我们相信在 Serverless 架构 ...

  9. 简单几步零成本使用Vercel部署OneIndex 无需服务器搭建基于OneDrive的网盘

    前提 你需要一个OneDrive账号,必须管理员开放API 需要已安装Node.js 拥有Github账号,没有就注册一个 魔法上网环境(看情况) 注册应用 登录https://portal.azur ...

  10. 项目优化之v-if

    前言: 在vue项目中,由于功能比较多,需要各种条件控制某个功能.某个标签.表格中的某一行是否显示等,需要使用大量的v-if来判断条件. 例如: <div v-if="isShow(a ...