0

0

MySQL数据库优化的一些笔记_MySQL

php中文网

php中文网

发布时间:2016-06-01 13:49:02

|

1121人浏览过

|

来源于php中文网

原创

bitsCN.com

  0. 索引很重要

  之前列举记录用了下面的语句。state字段为索引。

  SELECT * FROM feed_urls WHERE state='ok' AND feed_url'' LIMIT N,10

  当记录数量很大时,有几万之后,这句SQL就很慢了。主要是因为feed_url没有建立索引。后来的解决方法是,把feed_url为空的,设为一个ok以外的state值,就行了。

  1. 索引不是万能的

  为了计算记录总数,下面的语句会很慢。

 

  mysql> SELECT COUNT(*) FROM feed_urls WHERE state='error';

  +----------+

  | COUNT(*) |

  +----------+

  | 30715 |

  +----------+

  1 row in set (0.14 sec)

  mysql> EXPLAIN SELECT COUNT(*) FROM feed_urls WHERE state='error'/G

  *************************** 1. row ***************************

  id: 1

  select_type: SIMPLE

  table: feed_urls

  type: ref

  possible_keys: state,page_index

  key: page_index

  key_len: 10

  ref: const

  rows: 25936

  Extra: Using where; Using index

  1 row in set (0.00 sec)

 

  state为索引,请求用时140ms。遍历了state='error'索引下的每一条记录。

 

  mysql> SELECT state,COUNT(*) FROM feed_urls GROUP BY state;

  +----------+----------+

  | state | COUNT(*) |

  +----------+----------+

  | error | 30717 |

  | fetching | 8 |

  | nofeed | 76461 |

  | ok | 74703 |

  | queued | 249681 |

  +----------+----------+

  5 rows in set (0.55 sec)

  mysql> EXPLAIN SELECT state,COUNT(*) FROM feed_urls GROUP BY state/G

  *************************** 1. row ***************************

  id: 1

  select_type: SIMPLE

  table: feed_urls

  type: index

  possible_keys: NULL

  key: state

  key_len: 10

  ref: NULL

  rows: 431618

  Extra: Using index

  1 row in set (0.00 sec)

 

  请求用时550ms。遍历了每个state下的每一条记录。

  改进方法:

  独立一个表用来计数,使用MySQL的Trigger同步计数:

 

  CREATE TRIGGER my_trigger AFTER UPDATE ON feed_urls

  FOR EACH ROW BEGIN

  IF OLD.state NEW.state THEN

  IF NEW.state='ok' THEN

  UPDATE feed_stat SET count_feed = count_feed + 1;

  END IF;

  IF NEW.state IN ('ok', 'error', 'nofeed') THEN

  UPDATE feed_stat SET count_access = count_access + 1;

  END IF;

  END IF;

  END

 

  2. 当分页很大时

 

  mysql> SELECT * FROM feed_urls LIMIT 230000, 1/G

  *************************** 1. row ***************************

  id: 736841f82abb0bc87ccfec7c0fdbd09c30b5a24d

  link: http://mappemunde.typepad.com/

  title: Tim Peterson

  feed_url: NULL

  update_time: 2012-05-12 11:01:56

  state: queued

  http_server: NULL

  abstract: NULL

  previous_id: ceea30e0ba609b69198c53ce71c44070d69038c5

  ref_count: 1

  error: NULL

  aid: 230001

  1 row in set (0.50 sec)

  mysql> EXPLAIN SELECT * FROM feed_urls LIMIT 230000, 1/G

  *************************** 1. row ***************************

  id: 1

  select_type: SIMPLE

  table: feed_urls

  type: ALL

  possible_keys: NULL

  key: NULL

  key_len: NULL

  ref: NULL

  rows: 431751

  Extra:

  1 row in set (0.00 sec)

 

  读取一条记录,耗时500ms,因为表记录是变长的,所以MySQL不能算出目标位置,只能每一条记录的数过去。

  改进方法:

  通过索引定位,数索引比数记录要快,因为索引占用的空间比整条记录小很多。

 

  mysql> SELECT * FROM (SELECT aid FROM feed_urls ORDER BY aid LIMIT 215000, 1) d JOIN feed_urls u ON d.aid=u.aid/G

  *************************** 1. row ***************************

  aid: 215001

  id: 2e4b1a385c8aae40b3ec2af9153805ca446f2029

  link: http://ncse.com/

  title: NCSE

  feed_url: NULL

  update_time: 2012-05-12 10:47:15

  state: queued

  http_server: NULL

  abstract: NULL

  previous_id: 819a6e3c5edc1624a9b8f171d8d3ae269843785f

  ref_count: 3

  error: NULL

  aid: 215001

  1 row in set (0.06 sec)

  mysql> EXPLAIN SELECT * FROM (SELECT aid FROM feed_urls ORDER BY aid LIMIT 215000, 1) d JOIN feed_urls u ON d.aid=u.aid/G

  *************************** 1. row ***************************

  id: 1

  select_type: PRIMARY

云网OA
云网OA

采用JSP开发的办公自动化产品、基于B/S结构,运行环境:JDK v1.5、Tomcat v5.5、MySQL v4.1,三者均为以上版本其他相关内容:可视化流程设计: 流程支持串签、会签和分支流程,可以设置流程节点的修改、删除权限,并可指定流程中各个用户在表单中可以填写的域。智能表单所见即所得设计: 智能设计,自动在数据库中生成表格,方便优化程序 公共交流: 集论坛、博客、聊天室于一体文件柜:C

下载

  table:

  type: system

  possible_keys: NULL

  key: NULL

  key_len: NULL

  ref: NULL

  rows: 1

  Extra:

  *************************** 2. row ***************************

  id: 1

  select_type: PRIMARY

  table: u

  type: const

  possible_keys: aid

  key: aid

  key_len: 4

  ref: const

  rows: 1

  Extra:

  *************************** 3. row ***************************

  id: 2

  select_type: DERIVED

  table: feed_urls

  type: index

  possible_keys: NULL

  key: aid

  key_len: 4

  ref: NULL

  rows: 211001

  Extra: Using index

  3 rows in set (0.15 sec)

 

  耗时60ms,比之前的方法快了将近10倍。如果LIMIT语句里还有WHERE a=1,应该建立一个(a,aid)的索引。

  话说,MySQL好像还是不能直接算出第21500条索引的位置呀,这种方法还是数了索引了,能算出来就直接0ms了。不过这样的效率,对于百万级的,还能应付吧。如果是千万级的或者像我之前在KS创建的一张上亿条记录的表(120G),这种方法就肯定不行了。

  经过上述优化,打开最后一页的速度已经很快了(之前需要800ms,现在则为300ms左右)。

/

  膜拜下这Burst.NET最低档次的VPS (30RMB/month)。

  root@xiaoxia-pc:~/# ping feed.readself.com -n

  PING app.readself.com (184.82.185.32) 56(84) bytes of data.

  64 bytes from 184.82.185.32: icmp_req=1 ttl=45 time=161 ms

  64 bytes from 184.82.185.32: icmp_req=2 ttl=45 time=161 ms

  64 bytes from 184.82.185.32: icmp_req=3 ttl=45 time=161 ms

  用同样的方法,优化了搜索引擎的排名算法。即排名过程中选取尽量少的值出来排序,排序后再JOIN一次获取结果的信息。

  排序过程如下:

 

  SELECT u.*, count_level(u.id) lv

  FROM(

  SELECT f.id, f.ref_count, MATCH(i.link,i.title) AGAINST (keywords) score

  FROM feed_index i

  JOIN feed_urls f ON f.id=i.id

  WHERE MATCH(i.link,i.title) AGAINST (keywords)

  ORDER BY score*0.5 + score*0.5*(ref_count/max_ref_count_in_result) DESC

  LIMIT offset,10

  ) d JOIN feed_urls u ON u.id = d.id

 

  目前处理10万记录的全文索引数据,MySQL还是可以满足的,就是不知道上百万之后,还能不能撑下去。撑不下去就依赖第三方的工具了,例如Sphinx

  3. SELECT里的函数

  给FeedDB增加了层次的显示。因为本人太懒,所以没有给数据库表增加一个记录深度的字段。所以,直接写了一个MySQL的自定义函数 count_level,用来统计通过parent_id一直找到顶层经过的路径长度(Level)。

 

  CREATE DEFINER=`feeddb_rw`@`%` FUNCTION `count_level`(fid char(40)) RETURNS int(11)

  BEGIN

  SET @levels = 0;

  SET @found = false;

  WHILE NOT @found DO

  SELECT previous_id INTO @prev_id FROM feed_urls WHERE id=fid;

  IF @prev_id is null OR @prev_id = '' THEN

  SET @found = true;

  ELSE

  SET @levels = @levels + 1;

  SET fid = @prev_id;

  END IF;

  END WHILE;

  IF @prev_id is null THEN

  RETURN null;

  END IF;

  RETURN @levels;

  END

 

  在网页显示的时候用了类似下面的SQL语句。

 

  mysql> SELECT u.*, count_level(u.id) FROM feed_urls u ORDER BY ref_count DESC LIMIT 12000,1/G

  *************************** 1. row ***************************

  id: e42f44b04dabbb9789ccb4709278e881c54c28a3

  link: http://tetellita.blogspot.com/

  title: le hamburger et le croissant

  feed_url: http://www.blogger.com/feeds/7360650/posts/default

  update_time: 2012-05-15 14:50:53

  state: ok

  http_server: GSE

  abstract: Lepekmezest un épais sirop bordeaux obtenu par réduction dumoût de raisin, une sorte de mélasse de raisin, en somme. Légèrement acidulé, il apporte du pep's aux yaourts et nappe avec bonheur les

  previous_id: 129cabd96e7099a53b78c7ddeff98658351082e9

  ref_count: 9

  error: NULL

  aid: 174262

  count_level(u.id): 8

  1 row in set (4.10 sec)

 

  好吧,悲剧了!4100ms。一定对12000个条目都算了一次count_level,然后再进行排序。所以才用上了4秒那么漫长的时间!!!

  改进方法:

  先SELECT LIMIT,再在派生的临时表里,计算count_level。

 

  mysql> SELECT u.*, count_level(u.id) FROM (

  SELECT id FROM feed_urls ORDER BY ref_count DESC LIMIT 27521,1

  ) d JOIN feed_urls u ON u.id=d.id/G

  *************************** 1. row ***************************

  id: 61df288dda131ffd6125452d20ad0648f38abafd

  link: http://mynokiamobile.org/

  title: My Nokia Mobile

  feed_url: http://mynokiamobile.org/feed/

  update_time: 2012-05-14 14:06:57

  state: ok

  http_server: Apache/2.2.19 (Unix) mod_ssl/2.2.19 OpenSSL/1.0.0-fips mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635

  abstract: ArchivesSelect MonthMay 2012April 2012March 2012February 2012January 2012December 2011November 2011October 2011September 2011August 2011July 2011June 2011May 2011April 2011March 2011February 2011Janua

  previous_id: f37af92bb89c08f6d4b69e72eab05d8ab1e2aca4

  ref_count: 5

  error: NULL

  aid: 154996

  count_level(u.id): 8

  1 row in set (0.09 sec)

 

  如此,优化之后效果好很多了!但是还可以继续优化,例如建立一个字段存储Level的值应该是最好的办法了。

  初次了解MySQL一些工作机制,欢迎一起探讨!

  参考文献:

  http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/

  http://www.mysqlperformanceblog.com/2006/09/01/order-by-limit-performance-optimization/

bitsCN.com

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

0

2026.03.03

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

3

2026.03.03

Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

66

2026.02.28

Golang 工程化架构设计:可维护与可演进系统构建
Golang 工程化架构设计:可维护与可演进系统构建

Go语言工程化架构设计专注于构建高可维护性、可演进的企业级系统。本专题深入探讨Go项目的目录结构设计、模块划分、依赖管理等核心架构原则,涵盖微服务架构、领域驱动设计(DDD)在Go中的实践应用。通过实战案例解析接口抽象、错误处理、配置管理、日志监控等关键工程化技术,帮助开发者掌握构建稳定、可扩展Go应用的最佳实践方法。

57

2026.02.28

Golang 性能分析与运行时机制:构建高性能程序
Golang 性能分析与运行时机制:构建高性能程序

Go语言以其高效的并发模型和优异的性能表现广泛应用于高并发、高性能场景。其运行时机制包括 Goroutine 调度、内存管理、垃圾回收等方面,深入理解这些机制有助于编写更高效稳定的程序。本专题将系统讲解 Golang 的性能分析工具使用、常见性能瓶颈定位及优化策略,并结合实际案例剖析 Go 程序的运行时行为,帮助开发者掌握构建高性能应用的关键技能。

45

2026.02.28

Golang 并发编程模型与工程实践:从语言特性到系统性能
Golang 并发编程模型与工程实践:从语言特性到系统性能

本专题系统讲解 Golang 并发编程模型,从语言级特性出发,深入理解 goroutine、channel 与调度机制。结合工程实践,分析并发设计模式、性能瓶颈与资源控制策略,帮助将并发能力有效转化为稳定、可扩展的系统性能优势。

23

2026.02.27

Golang 高级特性与最佳实践:提升代码艺术
Golang 高级特性与最佳实践:提升代码艺术

本专题深入剖析 Golang 的高级特性与工程级最佳实践,涵盖并发模型、内存管理、接口设计与错误处理策略。通过真实场景与代码对比,引导从“可运行”走向“高质量”,帮助构建高性能、可扩展、易维护的优雅 Go 代码体系。

20

2026.02.27

Golang 测试与调试专题:确保代码可靠性
Golang 测试与调试专题:确保代码可靠性

本专题聚焦 Golang 的测试与调试体系,系统讲解单元测试、表驱动测试、基准测试与覆盖率分析方法,并深入剖析调试工具与常见问题定位思路。通过实践示例,引导建立可验证、可回归的工程习惯,从而持续提升代码可靠性与可维护性。

4

2026.02.27

漫蛙app官网链接入口
漫蛙app官网链接入口

漫蛙App官网提供多条稳定入口,包括 https://manwa.me、https

336

2026.02.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Django 教程
Django 教程

共28课时 | 4.7万人学习

MongoDB 教程
MongoDB 教程

共17课时 | 3.1万人学习

XML教程
XML教程

共142课时 | 7.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号