引子

《容易引起雪崩的两个处理》里,我提到一个慢查询的问题。本文先从整洁架构的角度讲讲慢查询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. P4320-道路相遇,P5058-[ZJOI2004]嗅探器【圆方树,LCA】

    两题差不多就一起写了 P4320-道路相遇 题目链接:https://www.luogu.com.cn/problem/P4320 题目大意 \(n\)个点\(m\)条边的一张图,\(q\)次询问两个 ...

  2. 模拟一个简单的tomcat

    目录 简单处理 每个请求一个线程 模拟tomcat 参考 简单处理 // 客户端和服务器的通信,说到底就是两个数据的传输, // 客户端发送inputStream给服务器,服务器回复 // outpu ...

  3. [洛谷日报#204] StackEdit——Markdown 编辑器的功能介绍

    本文同时发表于洛谷日报,您也可以通过洛谷博客进行查看. 1.介绍与开始使用 1.1 这是什么? StackEdit是基于PageDown.Stack Overflow和其他堆栈交换站点使用的Markd ...

  4. torch的下载及安装

    Pytorch官网:https://pytorch.org/ 安装的时候如果选择在官网上下载安装的话下载会很慢,试过梯子也是很慢,此处提供一种快速安装的方法. 1.由于我是window系统,我选择了国 ...

  5. 前段--->js

    一,java script的引入方式 1,直接在script里书写你的代码 <script> alert("hbflfb")</script> 2,引入额外 ...

  6. 洛谷4755 Beautiful Pair (分治)

    题目描述 小D有个数列 \(a\),当一个数对 \((i,j)(i\le j)\) 满足\(a_i\)和\(a_j\)的积 不大于 \(a_i \cdots a_j\) 中的最大值时,小D认为这个数对 ...

  7. 以太坊web3开发初步学习

    以太坊web3开发初步学习 此文是对https://learnblockchain.cn/2018/04/15/web3-html/的学习再理解. 以太坊智能合约通过使用web3.js前端和智能合约交 ...

  8. 无网环境安装docker之--rpm

    总体思路:找一台可以联网的linux,下载docker的RPM依赖包而不进行安装(yum localinstall),将所有依赖的rpm环境打包好,再在无网环境中解压逐一安装(rpm:  --forc ...

  9. MySQL复习(二)MySQL基本数据类型

    MySQL基本数据类型 常用的字段类型大致可以分为数值类型.字符串类型.日期时间类型三大类 1. 数值类型 数值类型可以分为整型.浮点型.定点型三小类. 1.1 整型 (tiny:极小的, small ...

  10. Coursera Deep Learning笔记 深度卷积网络

    参考 1. Why look at case studies 介绍几个典型的CNN案例: LeNet-5 AlexNet VGG Residual Network(ResNet): 特点是可以构建很深 ...