如何完成一次快速的查询 - 从MySQL到分库分表到ES和HBASE

本文探讨了MySQL查询慢的原因,包括索引失效、MDL锁、flush等待、行锁和大表场景下的分库分表与读写分离。接着介绍了Elasticsearch的快速查询机制和适用场景,并分析了HBase的存储结构和应用场景。总结了在不同场景下选择合适的技术来完成快速查询的重要性。

哪个男孩不想完成一次快速的查询?

1. MySQL 查询慢是什么体验?

谢邀,利益相关。

大多数互联网应用场景都是读多写少,业务逻辑更多分布在写上。对读的要求大概就是要快。那么都有什么原因会导致我们完成一次出色的慢查询呢?

1.1 索引

在数据量不是很大时,大多慢查询可以用索引解决,大多慢查询也因为索引不合理而产生。

MySQL 索引基于 B+ 树,这句话相信面试都背烂了,接着就可以问最左前缀索引、 B+ 树和各种树了。

说到最左前缀,实际就是组合索引的使用规则,使用合理组合索引可以有效的提高查询速度,为什么呢?

因为索引下推。如果查询条件包含在了组合索引中,比如存在组合索引(a,b),查询到满足 a 的记录后会直接在索引内部判断 b 是否满足,减少回表次数。同时,如果查询的列恰好包含在组合索引中,即为覆盖索引,无需回表。
索引规则估计都知道,实际开发中也会创建和使用。 问题可能更多的是:为什么建了索引还慢?

1.1.1 什么原因导致索引失效

建了索引还慢,多半是索引失效(未使用),可用 explain 分析。索引失效常见原因有 :

  1. where 中使用 != 或 <> 或 or 或表达式或函数(左侧)
  2. like 语句 % 开头
  3. 字符串未加’’
  4. 索引字段区分度过低,如性别
  5. 未匹配最左前缀

(一张嘴就知道老面试题了) 为什么这些做法会导致失效,成熟的 MySQL 也有自己的想法。

1.1.2 这些原因为什么导致索引失效

如果要 MySQL 给一个理由,还是那棵 B+ 树。

函数操作

当在 查询 where = 左侧使用表达式或函数时,如字段 A 为字符串型且有索引, 有 where length(a) = 6查询,这时传递一个 6 到 A 的索引树,不难想象在树的第一层就迷路了。

隐式转换

隐式类型转换和隐式字符编码转换也会导致这个问题。

  • 隐式类型转换对于 JOOQ 这种框架来说一般倒不会出现。
  • 隐式字符编码转换在连表查询时倒可能出现,即连表字段的类型相同但字符编码不同。
破坏了有序性

至于 Like 语句 % 开头、字符串未加 ’’ 原因基本一致,MySQL 认为对索引字段的操作可能会破坏索引有序性就机智的优化掉了。

不过,对于如性别这种区分度过低的字段,索引失效就不是因为这个原因。

1.1.3 性别字段为什么不要加索引

为什么索引区分度低的字段不要加索引。盲猜效率低,效率的确低,有时甚至会等于没加。

对于非聚簇索引,是要回表的。假如有 100 条数据,在 sex 字段建立索引,扫描到 51 个 male,需要再回表扫描 51 行。还不如直接来一次全表扫描呢。

所以,InnoDB 引擎对于这种场景就会放弃使用索引,至于区分度多低多少会放弃,大致是某类型的数据占到总的 30% 左右时,就会放弃使用该字段的索引,有兴趣可以试一下。

1.1.4 有什么好用且简单的索引方法

前面说到大多慢查询都源于索引,怎么建立并用好索引。这里有一些简单的规则。

  • 索引下推:性别字段不适合建索引,但确实存在查询场景怎么办?如果是多条件查询,可以建立联合索引利用该特性优化。

  • 覆盖索引:也是联合索引,查询需要的信息在索引里已经包含了,就不会再回表了。

  • 前缀索引:对于字符串,可以只在前 N 位添加索引,避免不必要的开支。假如的确需要如关键字查询,那交给更合适的如 ES 或许更好。

  • 不要对索引字段做函数操作

  • 对于确定的、写多读少的表或者频繁更新的字段都应该考虑索引的维护成本。

1.1.5 如何评价 MySQL 选错了索引

有时,建立了猛一看挺正确的索引,但事情却没按计划发展。就像“为啥 XXX 有索引,根据它查询还是慢查询”。

此刻没准要自信点:我的代码不可能有 BUG,肯定是 MySQL 出了问题。MySQL 的确可能有点问题。

这种情况常见于建了一大堆索引,查询条件一大堆。没使用你想让它用的那一个,而是选了个区分度低的,导致过多的扫描。造成的原因基本有两个:

  • 信息统计不准确:可以使用 analyze table x重新分析。

  • 优化器误判:可以 force index强制指定。或修改语句引导优化器,增加或删除索引绕过。

但根据我浅薄的经验来看,更可能是因为你建了些没必要的索引导致的。不会真有人以为 MySQL 没自己机灵吧?

除了上面这些索引原因外,还有下面这些不常见或者说不好判断的原因存在。

1.2 等MDL锁

在 MySQL 5.5 版本中引入了 MDL,对一个表做 CRUD 操作时,自动加 MDL 读锁;对表结构做变更时,加 MDL 写锁。读写锁、写锁间互斥。

当某语句拿 MDL 写锁就会阻塞 MDL 读锁,可以使用show processlist命令查看处于Waiting for table metadata lock状态的语句。

1.3 等 flush

flush 很快,大多是因为 flush 命令被别的语句堵住,它又堵住了 select 。通过show processlist命令查看时会发

<think>嗯,用户问的是在分库分表的环境下如何实现查询多个表中数据更新时间最新的1000条数据。之前他们问过关于多表查询的问题,现在进一步深入,涉及到了分布式数据库环境。 首先,我需要回顾之前的回答,用户提到了使用UNION ALL分页,但分库分表会增加复杂度。这时候可能需要不同的方法,比如中间件或者分布式查询引擎。 用户可能是在处理一个大规模的数据系统,可能是在电商平台或者物联网应用,需要实时获取最新数据。他们的实际需求可能是监控最新交易、设备状态等,所以需要高效的查询方法。 分库分表的环境通常会用水平分片,每个分片有不同的数据。直接跨分片查询会有性能问题,所以需要考虑如何整合结果。可能的解决方案包括使用分布式数据库中间件,比如ShardingSphere,或者应用层聚合数据。 另外,用户可能关心性能一致性。比如,每个分片单独查询,然后合并结果,但这样可能无法保证全局有序。需要考虑如何高效地获取全局最新的1000条记录,同时避免全表扫描。 可能还需要引入时间戳服务,比如使用全局唯一的时间戳,确保各分片的时间一致性。或者使用异步复制,把数据汇总到统一的查询库,比如ElasticsearchHBase,这样可以更方便地查询。 还要考虑分页的问题,如果需要分页,如何避免性能下降。可能需要使用游标分页或者基于时间戳的分页策略。 用户可能没有提到的深层需求是实时性要求,是否可以接受一定的延迟,或者是否需要强一致性。这些都会影响解决方案的选择,比如是否使用强一致性协议还是最终一致性。 总结下来,回答需要涵盖中间件的使用、应用层聚合、时间戳处理、异步复制方案,以及优化策略,比如索引、分区键的选择。同时,要指出不同方案的优缺点,帮助用户根据实际情况选择最适合的方法。</think>在分库分表环境下实现跨节点查询最新数据,需结合分布式架构特性设计解决方案。以下是典型实现方案: ### 分布式查询方案 ```sql -- 使用ShardingSphere分布式查询示例 SELECT * FROM ( (SELECT 't1' AS tbl, id, update_time FROM table1 ORDER BY update_time DESC LIMIT 1000) UNION ALL (SELECT 't2' AS tbl, id, update_time FROM table2 ORDER BY update_time DESC LIMIT 1000) ) AS combined ORDER BY update_time DESC LIMIT 1000; ``` ### 分布式架构实现要点 1. **中间件路由**: - 使用ShardingSphere/MyCat等中间件自动处理分片查询路由 - 配置广播查询策略强制跨所有分片执行 2. **时间戳同步**: - 使用全局时间戳服务(如Snowflake)确保时间比较有效性 -MySQL中使用`UNIX_TIMESTAMP()`统一时间基准 3. **结果合并优化**: ```java // 应用层合并示例(伪代码) List<ResultSet> results = parallelQueryAllShards(); List<Record> merged = mergeSort(results, "update_time"); return merged.subList(0, 1000); ``` 4. **索引优化策略**: - 在每个分片建立`(update_time DESC, id)`组合索引 -MySQL使用覆盖索引优化排序性能 ### 异步预处理方案 ```mermaid graph TD A[数据更新] --> B{是否主表?} B -->|是| C[写入更新日志] B -->|否| D[异步同步到统一查询库] C --> E[Kafka消息队列] E --> F[Flink实时处理] F --> G[ES/HBase统一存储] H[定时任务] --> D ``` ### 分布式环境关键问题 1. **一致性保障**: - 使用2PC协议保证跨分片查询一致性 - 对最终一致性场景添加版本号校验 2. **性能优化**: - 实施分片裁剪:`WHERE shard_key = CURRENT_SHARD` - 启用批量查询:单次获取`LIMIT 1000*N`条数据 3. **监控指标**: ```prometheus # 查询延迟指标 histogram_quantile(0.95, sum(rate(distributed_query_latency_seconds_bucket[5m])) by (le, shard) ) ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值