今日推荐 在 IDEA 中的各种调试技巧,轻松定位 Bug(超级全面)
来源:llc687.top/post/ 如何完成快速查询
哪个女孩不想快速查询一下?
1. MySQL 查询速度慢的经历是什么?
感谢您的关注。
大多数互联网应用场景都是读多写少,业务逻辑更加分布在写上。 对阅读的要求就是要快。 那么是什么原因导致我们完成了一个出色的慢查询呢?
1.1 指标
当数据量不大时,大多数慢查询都可以用索引来解决,而大多数慢查询也是由索引不合理造成的。
MySQL索引是基于B+树的。 我相信我笔试的时候这句话已经记不好了,接下来就可以问最左边的前缀索引、B+树以及各种树了。
说到最左边的前缀,其实就是复合索引的使用规则。 使用合理的组合索引可以有效提升查询率。 为什么?
因为指数被压低了。 如果查询条件包含在复合索引中,比如有一个复合索引(a,b),查询满足a的记录后,会直接判断索引内部是否满足b,减少次数返回表。
同时,如果查询的列恰好包含在复合索引中,则属于覆盖索引,无需回表。 索引规则恐怕大家都知道,在实际开发中也会创建和使用。 问题可能更多:为什么建索引慢?
1.1.1 索引失败的原因是什么
建索引还是慢,很有可能是索引无效(未使用),可以通过explain来分析。 索引失败的常见原因包括:
where 使用 != or or or or 表达式或函数(左侧)
以like语句开头%
不带 '' 的字符串
索引数组差异化太大,比如性别
与最左边的前缀不匹配
(面试老问题,一口就知道)为什么这种做法会导致失败? 成熟的MySQL也有自己的看法。
1.1.2 为什么这些激励措施会导致指数失败
如果要MySQL给出一个理由,那还是B+树。
功能操作
当查询中where=左边使用表达式或函数时,比如数组A是字符串类型,有索引,并且有一个查询where length(a) = 6,则传递一个索引树从6到A,不难想象在树的第一层就丢失了。
隐式转换
隐式类型转换和隐式字符编码转换也可能导致此问题。
秩序被打乱
至于Like语句开头的%,字符串中不加''的原因基本是一样的。 MySQL认为对索引数组的操作可能会破坏索引的顺序,因此进行了明智的优化。
然而,对于性别等差异化过大的数组,索引失败并不是因为这个原因。
1.1.3 为什么性别数组不建立索引
为什么不向索引区分度较低的数组添加索引。 盲目猜测是低效的,确实是低效的,有时甚至等于无加法。
对于非聚集索引,需要返回表。 如果有100条数据,在性别字段建索引,扫描51个男性,然后需要回表扫描51行。 最好直接进行全表扫描。
因此,InnoDB引擎将放弃对这些场景使用索引。 至于区分度有多低,当某类数据占总数的30%左右时,就会放弃数组的索引。 如果你有兴趣的话,你可以尝试一下。
1.1.4 有哪些有用且简单的索引方法
前面提到,大部分查询慢都是由于索引造成的,如何改进和利用好索引。 这里有一些简单的规则。
1.1.5 如何评价MySQL选择了错误的索引
有时,建立了乍一看正确的索引,但事情并没有按计划进行。 就像“为什么XXX有索引,基于它的查询仍然是慢查询”。
此时此刻,也许我应该更加自信:我的代码不可能有BUG,一定是MySQL有问题。 MySQL确实可能有问题。
这种情况常见于建立大量索引、建立大量查询条件时。 它没有使用您希望它使用的选项,而是选择了一种区别较小的选项,从而导致扫描次数过多。 基本上有两个原因:
但根据我浅薄的经验,更有可能是因为你建了一些不必要的索引。 难道没有人真的认为 MySQL 没有它那么聪明吗?
除了前面的指标原因外,还有以下不常见或难以确定的原因。
1.2 等分MDL锁
MDL 是在 MySQL 5.5 中引入的。 对表进行CRUD操作时,会自动添加MDL读锁; 当表结构发生变化时,会添加MDL写锁。 读写锁和写锁是互斥的。
当一个句子获得MDL写锁时,它会阻塞MDL读锁。 可以使用show processlist命令查看处于等待表元数据锁状态的语句。
1.3 等待刷新
lush速度很快,主要是因为flush命令被另一个语句阻塞,而这又阻塞了select。 通过show processlist命令查看,会发现处于Waiting for tablelush状态。
1.4 等行锁
有些东西持有未提交的写锁。
1.5 当前读数
InnoDB默认级别是可重复读。 想象一个场景,事务A启动一个事务,事务B也开始执行大量更新。 B是最先提交的,A是当前读取的,需要依次执行undo log,直到找到事务B开始之前的值。
1.6 大表场景
在还没有重新开发的MYSQL中,上亿的表肯定算大表了。 虽然这种情况在索引和查询层面已经得到了很好的实现,但是面对频繁的聚合操作可能会出现IO或者CPU瓶颈,即使是简单的查询效率也会有所提升。
而Innodb中每个B+树节点的存储容量为16KB,理论上可以存储2kw左右的行,此时树高为3层。 我们知道innodb_buffer_pool是用来缓存表和索引的。 如果索引数据很大,缓存命中率是不可预测的。 同时innodb_buffer_pool使用LRU算法来消除页面。 如果数据量太大,对旧数据或非热点数据的查询仍然可能会失败,挤出热点数据。
因此,大表常见的优化就是库与表读写分离。
1.6.1 分库分表方案
是分库还是分表? 这个需要具体分析。
层面就是对数据进行拆分,将原始数据分散到更多的数据库表中。
纵向上,库按业务划分,表按数组划分。
工具包括sharding-sphere、TDDL和Mycat。 首先需要评估分库和表数量,制定分片规则和选择key,然后开发和迁移数据,并考虑扩容问题。
问题
实际操作中,书写问题不大。 主要问题是唯一ID生成、非分区键查询、扩容等。
当然,如果数据库进行了划分,就会面临事务一致性、跨库join等问题。
1.6.2 读写分离 为什么要读写分离
分表解决大表的CPU瓶颈,分库解决IO瓶颈。 两者解决存储压力。 但查询却不一定。
如果下降到DB的QPS还是很高,并且读远小于写,可以考虑读写分离,基于主从模式来分担读的压力,避免单机机器负载太低,也要保证高可用,实现负载均衡。
问题
主要问题是过期读取和分配机制。
1.7 总结
以上列出了常见MySQL慢查询的原因及处理方法,并介绍了处理大数据场景的常用技巧。
分库分表、读写分离是针对大数据或者并发场景,也是为了提高系统的稳定性和扩展性。 但并非所有问题都能以这种方式得到最佳解决。
2. 如何评价ElasticSearch
前面提到,ES可以用于关键字查询。 那我们就来说说ES吧。
2.1 可以做什么
ES是一个基于Lucene的近实时分布式搜索引擎。 使用场景包括全文检索、NoSQL Json文档数据库、监控日志、数据采集分析等。
对于非数据开发来说,常用的应该是全文检索和日志。 ES的使用中,经常与Logstash、Kibana结合,也成为ELK。 我们先来看看如何使用日志。
下面是我们日志系统的搜索操作: 打开Kibana,在Discover页面输入“xxx”等格式的查询。
该操作可以在开发工具的控制台中替换为:
GET yourIndex/_search
{
"from" : 0, "size" : 10,
"query" : {
"match_phrase" : {
"log" : "xxx"
}
}
}
什么意思? Discover中添加“”和console中的match_phrase都表示这是一个句子匹配,即只保留那些包含所有搜索词且与搜索词位置相同的文档。
2.2 ES的结构
ES 7.0之前,存储结构是Index -> Type -> Document。 根据MySQL比较,是数据库-表-id(实际上这些比较不太合理)。 7.0之后,Type已被弃用,所以现在我们将索引视为表。
在开发工具的控制台中,您可以使用以下命令查看一些基本信息。 也可以用crul命令代替。
GET /_cat/health?v&pretty:查看集群健康状态
GET /_cat/shards?v :查看分片状态
GETyourindex/_mapping:索引映射结构
GETyourindex/_settings:索引设置结构
GET/_cat/indices?v:查看当前节点所有索引信息
重点是绘图和设置。 Mapping可以理解为MySQL中表的结构定义,setting负责控制比如分片数、副本数等。
以下是截取的某日志索引下的映射结构的一部分。 ES默认将字符串类型定义为text,并为其定义了一个名为keyword的子数组。 两者的区别是:文本类型会携带动词,而关键字类型不会携带动词。
"******": {
"mappings": {
"doc": {
"properties": {
"appname": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
2.3 为什么ES查询快?
分词是什么意思? 看完ES的索引原理就明白了。
ES是基于倒排索引的。 你是什么意思? 传统索引通常使用文档ID作为索引,使用内容作为记录。 相反,倒排索引根据已有的属性值找到对应行的位置,即以短语或内容作为索引,以文档ID作为记录。
下图是ES倒排索引的示意图,它由Term索引、Team Dictionary和Posting List组成。
图片
图中的Ada和Sara称为术语,实际上是动词后面的词。 如果把图中的Term Index去掉,是不是有点像MySQL呢? 术语词典就像一个二级索引,但MySQL存储在C盘上,检索一个术语需要多次随机访问磁盘操作。
ES在Term Dictionary的基础上,额外增加了一层Term Index,以FST的形式存储在显存中,并保存term的前缀,这样可以快速定位term在Term字典中的偏移量位于。 而且,FST形式和术语词典的块存储方式都节省显存和磁盘空间。
至此,我知道为什么这么快了。 正是因为显存中的Term Index,为术语索引Term Dictionary又做了一层索引。
然而,这并不意味着ES查询比MySQL更快。 检索大致可以分为两类。
2.3.1 分词后检索
ES的索引存储的是动词排序的结果。比如图中的Ada,在MySQL中,%da%会扫描全表,但是对于ES来说,可以快速定位
2.3.2 精确搜索
虽然这种情况差别不大,因为Term Index的优势没有了,但是还是需要在term字典中找到位置。 也许因为MySQL覆盖索引不需要返回表会快一点。
2.4 何时使用ES
前面提到,业务中的查询场景什么时候适合使用ES? 我有两种感觉。
2.4.1 全文检索
基于关键字的字符串类型的模糊查询在MySQL中是一场灾难,但对于ES来说却是小菜一碟。 具体场景,比如消息表中消息内容的模糊查询,即聊天记录查询。
但需要注意的是,如果需要的是类似于大多数搜索引擎的关键词查询而不是日志句匹配查询,就需要对英语的动词进行处理,应用最广泛的是ik。 Ik tokenizer的安装这里不再详述。
这意味着什么?
分词
日志开头的查询,当你输入“I'm such a smart Ghost”时,只会得到精确匹配。
而去掉“”,则得到所有按照“我”、“可”、“真”分词匹配的信息。 这显然会返回很多信息elementui模糊查询,而且不符合英语语义。 实际预期的动词疗效大概是“我”、“可”、“真”、“聪明鬼”,然后根据这些动词的结果来匹配查询。
这是由于ES默认的动词策略对英语不友好造成的。 它遵循英语短语的字母,但德语短语之间有空格。 这也是很多美国软件英文搜索结果不好看的原因之一。
对于这个问题,可以在控制台使用以下命令来测试当前索引的动词的功效。
POST yourindex/_analyze
{
"field":"yourfield",
"text":"我可真是个机灵鬼"
}
2.4.2 组合查询
如果数据量足够大,就有足够的表数组。 将所有数组信息都扔到ES中创建索引是不合理的。 如果使用MySQL,就只能按照上面说的分库分表,读写分离。 为什么不结合起来呢。
1.ES+MySQL
将id添加到参与查询的数组信息中,放入ES中,做成动词。 将全量信息加载到MySQL中,通过id快速检索。
2.ES+HBASE
如果你想省分库分表,也许你可以放弃MySQL,选择分布式数据库,比如HBASE。 对于这些NOSQL来说,存储容量海量,扩展容易,根据rowkey查询也很快。
以上思路是经典的索引和数据存储隔离方案。
当然,摊子越大,就越容易出事故,面临的问题也就越多。 使用ES作为索引层,数据同步、时序、映射设计、高可用性等都需要考虑。
毕竟,与简单的日志系统相比,日志可以等待,但用户不能。
2.5 总结
本节简单介绍一下 ES 为什么快以及可以用在什么地方。 现在您可以打开 Kibana 的控制台并尝试一下。
如果想在Java项目中访问,在SpringBoot的支持下,在ES环境OK的前提下,完全开箱即用,只有一个依赖。 基本的CRUD支持完全OK。
3.HBASE
前面提到了HBASE,什么是HBASE,鉴于篇幅这里简单说一下。
3.1 存储结构
MySQL 等关系数据库是基于行的。
HBASE 是按列的(实际上是列族)。 上表的列式存储会导致:
下图是HBASE的一个实际的表模型结构。
图片
行键是一个字段,按字典顺序排序。 时间戳是版本号。 info 和area 都是列族,垂直切割表格。 name和age称为列,属于某个列族,可以动态添加。 Cell 是一个具体的 Value。
3.2 OLTP和OLAP
数据处理大致可以分为两类:OLTP(在线事务处理)和OLAP(在线分析处理)。
面向列的适合OLAPelementui模糊查询,面向行的适合在线事务处理(OLTP)。 但是,HBASE不是OLAP,它没有事务,而且它实际上是面向CF的。 一般来说,使用HBASE进行OLAP的人并不多。
3.3 行键
HBASE表设计的好坏取决于RowKey的设计。这是因为HBASE只支持三种查询方式
1.基于Rowkey的单行查询 2.基于Rowkey的范围扫描 3.全表扫描
可见HBASE不支持复杂查询。
3.4 使用场景
HBASE不适合实时快速查询。 更适合写入密集型场景。 它具有快速的写入能力,查询对于单个或者小范围的查询都是可以的,当然只能通过rowkey。 但其性能和可靠性极高,并且不存在单点故障。
4. 总结
我个人认为软件开发是一个循序渐进的过程,技术是为项目服务的。 适当性比新颖性和复杂性更重要。
如何完成快速查询? 最好的办法是先找到自己的bug,解决当前的问题,然后再创造新的问题。
本文列出的大部分解决方案都简单提及了具体实现。 其实无论是MySQL分表还是ES业务整合,都会有很多细节和难点。 工程工程师必须知道这个事情并自己去做。
参考
推荐文章
更多项目源码