我 Java 开发三年多经验的时候,不知咋地,有次被猎头安排到一家大数据公司去面试了。


面试官问了我一道题:MySQL 单表上亿,怎么优化分页查询?


当时回答这题,我仅仅说了分表分库,其他方面说的都是支支吾吾。现在感觉,至今也无法答好这题,但多少有了点想法。希望我说完答案,你可以帮我指出错误,谈谈你的想法。



1、表容量的问题

首先,MySQL 不管怎么优化也是很难支持单表一亿数据量带查询条件的分页查询,需要提前考虑分表分库。单表设计以 200-500 万为宜;优化的好,单表数据到一两千万,性能也还行。



2、总页数的问题

页面不需要显示总页数,仅显示附近的页码,这样可以避免单表总行数的查询。


需要显示总页数,这种情况就比较难处理一些。首先 MySQL 的 MyISAM 引擎把一个表的总行数记录在磁盘中,查询 count(*) 可以直接返回;InnoDB 引擎是一行行读出来累加计数,大数据量时性能堪忧,大几秒甚至几十秒都有可能(我相信你一定遇到过)。所以 MyISAM 的总行数查询速度是比 InnoDB 快的,但这个快也仅限于不带 where 条件的。MyISAM 还有一个硬伤,不支持事务。


如何既支持事务又快速的查出总数呢?

使用 InnoDB 引擎新建一张表记录业务表的总数,新增、删除各自在同一事务中增减总行数然后查询,保证事务的一致性和隔离性。当然,这里更新总行数要借助分布式锁或 CAS 方式更新记录总数的表。



3、具体的 SQL 优化

新增表记录业务表的总数,也是无法彻底解决带查询条件的总行数查询慢的问题。这里只能借助具体的 SQL 优化。


不带条件 + 自增 id 字段连续

这种理想情况就不讨论了,通过 pageNo 和 pageSize 算出 id 的起始与结束值

where id >= ? and id < ?where id betweenwhere id >= ? limit 10

就可以直接搞定了。


主键 id + 带查询条件

这种就是我们最需要解决的情况。使用 limit 分页,有个询耗时起始记录的位置成正比的问题,所以不能直接使用。


可以这样根据主键进行关联查询

select * from table t1join (select id from table where condition limit 10) t2on t1.id = t2.id order by t1.id asc

其中 condition 是包含索引的查询条件,使用 id 字段进行具体信息的关联回查。当然查询条件 condition 中索引是否生效对性能影响也很大。


索引没有生效的一些情况:

  • 组合索引的「最左前缀」原则

  • or 的使用可能导致索引未生效,可使用 union all 替代

  • like 查询以 % 开头

  • 对 null 值判断

  • 使用 != 或 <> 操作符

  • 索引列上使用计算、函数



4、其他解法

  • 继续优化数据库配置

  • 提升数据库服务器硬件性能

  • 引入大数据组件

  • 引入大型商业数据库或者非关系型数据库解决大表问题


更多相关文章

  1. 阿里面试官:什么是MySQL索引,为什么要有索引?
  2. 为什么 MongoDB 索引选择B-树,而 Mysql 索引选择B+树(精干总结)
  3. 服务端开发指南与最佳实战 | 数据存储技术 | MySQL(05) 索引的排序
  4. 服务端开发指南与最佳实战 | 数据存储技术 | MySQL(04) 索引使用的
  5. 服务端开发指南与最佳实战 | 数据存储技术 | MySQL(03) 如何设计索
  6. 全文检索引擎 Solr 系列(1)— 入门篇
  7. 全文检索引擎 Solr 系列(3)— Solr 核心概念、配置文件

随机推荐

  1. Android(安卓)Log 日志系统
  2. Android HttpURLConnection应用技巧分享
  3. Android属性动画之ObjectAnimator
  4. Android(安卓)左右布局
  5. ANT编译Android Eclipse工程
  6. android 跑马灯效果及相关
  7. Android CTS 测试总结
  8. Android中构建数据业务应用
  9. Android 源码编译 文件系统制作
  10. Java培训出身,今获阿里Android岗offer,大专