测试与质量的关系 测试有助于提高软件的质量,但是提高软件的质量不能依赖于测试。测试与质量的关系很象在考试中“检查”与“成绩”的关系。 学习好的学生,在考试时通过认真检查能减少因疏忽而造成的答题错误,从而“提高”了考试成绩(取得他本来就该得的好成绩)。 而学习差的学生,他原本就不会做题目,无论检查多么细心,也不能提高成绩。 所以说,软件的高质量是设计出来的,而不是靠测试修补出来的。 I love U software testing

MySQL的性能调优工具:比mysqlreport更方便的tuning-primer.sh

上一篇 / 下一篇  2009-01-19 15:10:46

年初的时候收藏过一篇关于mysqlreport的报表解读,和内置的show status,show variables相比mysqlreport输出一个可读性更好的报表;但Sundry MySQL提供的脚本相比mysqlreport更进一步:除了报表还进一步提供了修改建议。安装和使用非常简单:

wgethttp://www.day32.com/MySQL/tuning-primer.sh
chmod +x tuning-primer.sh
./tuning-primer.sh

和mysqlreport一样,tuning-primer.sh也支持.my.cnf
[client]
user = USERNAME
password = PASSWORD
socket = /tmp/mysql.sock

样例输出:在终端上按照问题重要程度分别用黄色/红色字符标记问题

-- MYSQL PERFORMANCE TUNING PRIMER --
- By: Matthew Montgomery -

MySQL Version 5.0.45 i686

Uptime = 19 days 8 hrs 32 min 54 sec
Avg. qps = 0
Total Questions = 264260
Threads Connected = 1

Server has been running for over 48hrs.
It should be safe to follow these recommendations

To find out more information on how each of these
runtime variables effects performance visit:
http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html
Visit http://www.mysql.com/products/enterprise/advisors.html
for info about MySQL's Enterprise Monitoring and Advisory Service

SLOW QUERIES
The slow query log is NOT enabled.
Current long_query_time = 10 sec.
You have 0 out of 264274 that take longer than 10 sec. to complete
Your long_query_time may be too high, I typically set this under 5 sec.

BINARY UPDATE LOG
The binary update log is NOT enabled.
You will not be able to do point in time recovery
See http://dev.mysql.com/doc/refman/5.0/en/point-in-time-recovery.html

WORKER THREADS
Current thread_cache_size = 0
Current threads_cached = 0
Current threads_per_sec = 1
Historic threads_per_sec = 0
Your thread_cache_size is fine

MAX CONNECTIONS
Current max_connections = 100
Current threads_connected = 1
Historic max_used_connections = 33
The number of used connections is 33% of the configured maximum.
Your max_connections variable seems to be fine.

MEMORY USAGE
Max Memory Ever Allocated : 96 M
Configured Max Per-thread Buffers : 268 M
Configured Max Global Buffers : 7 M
Configured Max Memory Limit : 276 M
Physical Memory : 1.97 G
Max memory limit seem to be within acceptable norms

KEY BUFFER
Current MyISAM index space = 8 M
Current key_buffer_size = 7 M
Key cache miss rate is 1 : 1817
Key buffer fill ratio = 6.00 %
Your key_buffer_size seems to be too high.
Perhaps you can use these resources elsewhere

QUERY CACHE
Query cache is supported but not enabled
Perhaps you should set the query_cache_size

SORT OPERATIONS
Current sort_buffer_size = 2 M
Current read_rnd_buffer_size = 256 K
Sort buffer seems to be fine

JOINS
Current join_buffer_size = 132.00 K
You have had 0 queries where a join could not use an index properly
Your joins seem to be using indexes properly

OPEN FILES LIMIT
Current open_files_limit = 1024 files
The open_files_limit should typically be set to at least 2x-3x
that of table_cache if you have heavy MyISAM usage.
Your open_files_limit value seems to be fine

TABLE CACHE
Current table_cache value = 64 tables
You have a total of 125 tables
You have 64 open tables.
Current table_cache hit rate is 9%, while 100% of your table cache is in use
You should probably increase your table_cache

TEMP TABLES
Current max_heap_table_size = 16 M
Current tmp_table_size = 32 M
Of 564 temp tables, 6% were created on disk
Effective in-memory tmp_table_size is limited to max_heap_table_size.
Created disk tmp tables ratio seems fine

TABLE SCANS
Current read_buffer_size = 128 K
Current table scan ratio = 1 : 1
read_buffer_size seems to be fine

TABLE LOCKING
Current Lock Wait ratio = 0 : 264392
Your table locking seems to be fine

更有用是作者总结的处理MySQL性能问题处理的优先级:尤其是头3条,基本上可以解决大部分瓶颈问题的原因。
# Slow Query Log 慢查询 尤其是like操作,性能杀手,轻易不要使用,让全文索引交给Lucene或者利用Tag机制减少like操作;
# Max Connections 并发连接数:一个MySQL deamon缺省最大连接数是100,调到更高只是为了出现问题是给我们更多的缓冲时间而不是任其一直处于那么高的状态,并发连接数类似于等候大厅:当等候人数过多的时候,一味扩大等候厅不是根本解决问题的办法,提高业务的处理速度,多开几个窗口才是更好的解决方法;我的经验就是超过100: 数据就要想办法(镜像或者分片)分布到更多Deamon上
# Worker Threads:Jeremy Zawondy 曾在部落格上說到:Thread caching並不是我們最需要關心的問題,但當你解決了所有其他更嚴重的問題之後,它就會是最嚴重的問題。(thread caching really wasn't the worst of our problems. But it became the worst after we had fixed all the bigger ones.)
# Key Buffer
# Query Cache
# Sort Buffer
# Joins
# Temp Tables 临时表
# Table (Open & Definition) Cache 表缓存;
# Table Locking 表锁定
# Table Scans (read_buffer)
# Innodb Status

其他一些工具:
1mytop: 一个top like的show processlist;
2 使用cacti做MySQL的监控:推荐配置模板
3 把binlog导出成文本和slowquery的格式几乎是一样的,调用mysqlslowquery脚本分析,有时候也会有意外收获;

谢谢 oldplantegg 补充:
mysqlsla(hackmysql.com推出的一款日志分析工具该网站还维护了,mysqlreport,mysqlidxchk等比较实用的mysql工具)能够分析slow query 和binlog等,这样就不用将binlog导出来了

posted @2008-12-11 11:23fand 阅读(19) |评论 (0)编辑
支撑1000万pv的数据库缓存解决方案
支撑1000万pv/数据库缓存系统

  系统主要是构建的高效数据库缓存系统,其中包含了分布式解决方案,该系统已经应用在舍得网上了,没有发现大问题,本人也相信该系统已经足够强大,应付数百万IP/天的应用都不是问题。

  代码看上去很简单,其实却是两年经验的总结,整过过程也遇到了很多难点,最后一一解决了。本系统非常简洁易用,主程序不到1000行代码,用“精悍”来形容绝对不为过,1000行代码却包含了数据库对象的缓存、列表和长度的缓存、按字段散列缓存、update延时更新、自动清除列表缓存等功能,用它来实现像论坛、博客、校友录、交友社区等绝大部分应用网站都足够了。

  现在进入正题。。。。。

  为什么要用缓存?如果问这个问题说明你还是新手,数据库吞吐量毕竟有限,每秒读写5000次了不起了,如果不用缓存,假设一个页面有100个数据库操作,50个用户并发数据库就歇菜,这样最多能支撑的pv也就50*3600*15=270万,而且数据库服务器累得半死,搞不好什么时候就累死了。我的这套缓存系统比单独用memcached做缓存还要强大,相当于在memcached上再做了两级缓存,大家都知道memcached很强了,但是吞吐量还是有限,每秒20000次get和put当遇到超大规模的应用时还是会歇菜,本地HashMap每秒可执行上百万次put和get,在这上面损耗的性能几乎可以忽略不记了。温馨提示:能不用分布式的时候就不要用分布式,非用分布式的时候再考虑用memcached,我的缓存系统在这方面都已经实现了,改个配置就可以了,有兴趣的可以仔细测试测试!

  一般数据库缓存在我看来包含四种。第一种:单个对象的缓存(一个对象就是数据库一行记录),对于单个对象的缓存,用HashMap就可以了,稍微复杂一点用LRU算法包装一个HashMap,再复杂一点的分布式用memcached即可,没什么太难的;第二种:列表缓存,就像论坛里帖子的列表;第三种:长度的缓存,比如一个论坛板块里有多少个帖子,这样才方便实现分页。第四种:复杂一点的group,sum,count查询,比如一个论坛里按点击数排名的最HOT的帖子列表。第一种比较好实现,后面三种比较困难,似乎没有通用的解决办法,我暂时以列表缓存(第二种)为例分析。

  mysql的底层在做通用的列表缓存时都是根据查询条件把列表结果缓存起来,但是只要该表的记录有任何变化(增加/删除/修改),列表缓存要全部清除,这样只要一个表的记录经常变化(通常情况都会这样),列表缓存几乎失效,命中率太低了。

  本人想了一个办法改善了列表缓存,当表的记录有改变时,遍历所有列表缓存,只有那些被影响到的列表缓存才会被删除,而不是直接清除所有列表缓存,比如在一个论坛版(id=1)里增加了一个帖子,那么只要清除id=1这个版对应的列表缓存就可以了,版id=2就不用清除了。这样处理有个好处,可以缓存各种查询条件(如等于、大于、不等于、小于)的列表缓存,但也有个潜在的性能问题,由于需要遍历,CPU符合比较大,如果列表缓存最大长度设置成10000,两个4核的CPU每秒也只能遍历完300多次,这样如果每秒有超过300个insert/update/delete,系统就吃不消了,此路不通。

  在前面两种解决办法都不完美的情况下,本人和同事经过几个星期的思索,总算得出了根据表的某几个字段做散列的缓存办法,这种办法无需大规模遍历,所以CPU符合非常小,由于这种列表缓存按照字段做了散列,所以命中率极高。思路如下:每个表有3个缓存Map(key=value键值对),第一个Map是对象缓存A,在A中,key是数据库的id,Value是数据库对象(也就是一行数据);第二个Map是通用列表缓存B,B的最大长度一般1000左右,在B中,key是查询条件拼出来的String(如start=0,length=15#active=0#state=0),Value是该条件查询下的所有id组成的List;第三个Map是散列缓存C,在C中,key是散列的字段(如根据userId散列的话,其中某个key就是userId=109这样的String)组成的String,value是一个和B类似的HashMap。其中只有B这个Map是需要遍历的,不知道说明白了没有,看完小面这个例子应该就明白了,就用论坛的回复表作说明,假设回复表T中假设有字段id,topicId,postUserId等字段(topicId就是帖子的id,postUserId是发布者id)。

  第一种情况,也是最常用的情况,就是获取一个帖子对应的回复,sql语句应该是象

  select id from T where topicId=2008 order by createTime desc limit 0,5

  select id from T where topicId=2008 order by createTime desc limit 5,5

  select id from T where topicId=2008 order by createTime desc limit 10,5

  的样子,那么这种列表很显然用topicId做散列是最好的,把上面三个列表缓存(可以是N个)都散列到key是topicId=2008这一个Map中,当id是2008的帖子有新的回复时,系统自动把key是topicId=2008的散列Map清除即可。由于这种散列不需要遍历,因此可以设置成很大,例如100000,这样10万个帖子对应的所有回复列表都可以缓存起来,当有一个帖子有新的回复时,其余99999个帖子对应的回复列表都不会动,缓存的命中率极高。

  第二种情况,就是后台需要显示最新的回复,sql语句应该是象

  select id from T order by createTime desc limit 0,50

  的样子,这种情况不需要散列,因为后台不可能有太多人访问,常用列表也不会太多,所以直接放到通用列表缓存B中即可。

  第三种情况,获取一个用户的回复,sql语句象

  select id from T where userId=2046 order by createTime desc limit 0,15

  select id from T where userId=2046 order by createTime desc limit 15,15

  select id from T where userId=2046 order by createTime desc limit 30,15

  的样子,那么这种列表和第一种情况类似,用userId做散列即可。

  第四种情况,获取一个用户对某个帖子的回复,sql语句象

  select id from T where topicId=2008 and userId=2046 order by createTime desc limit 0,15

  select id from T where topicId=2008 and userId=2046 order by createTime desc limit 15,15

  的样子,这种情况比较少见,一般以topicId=2008为准,也放到key是topicId=2008这个散列Map里即可。

  那么最后的缓存结构应该是下面这个样子:

  缓存A是:

  Key键(long型)Value值(类型T)

  11Id=11的T对象

  22Id=22的T对象

  133Id=133的T对象

  ……

  列表缓存B是:

  Key键(String型)Value值(ArrayList型)

  from T order by createTime desc limit 0,50ArrayList,对应取出来的所有id

  from T order by createTime desc limit 50,50ArrayList,对应取出来的所有id

  from T order by createTime desc limit 100,50ArrayList,对应取出来的所有id

  ……

  散列缓存C是:

  Key键(String型)Value值(HashMap)

  userId=2046Key键(String型)Value值(ArrayList)

  userId=2046#0,5id组成的List

  userId=2046#5,5id组成的List

  userId=2046#15,5id组成的List

  ……

  userId=2047Key键(String型)Value值(ArrayList)

  userId=2047#0,5id组成的List

  userId=2047#5,5id组成的List

  userId=2047#15,5id组成的List

  ……

  userId=2048Key键(String型)Value值(ArrayList)

  userId=2048#topicId=2008#0,5id组成的List

  userId=2048#5,5id组成的List

  userId=2048#15,5id组成的List

  ……

  ……

  总结:这种缓存思路可以存储大规模的列表,缓存命中率极高,因此可以承受超大规模的应用,但是需要技术人员根据自身业务逻辑来配置需要做散列的字段,一般用一个表的索引键做散列(注意顺序,最散的字段放前面),假设以userId为例,可以存储N个用户的M种列表,如果某个用户的相关数据发生变化,其余N-1个用户的列表缓存纹丝不动。以上说明的都是如何缓存列表,缓存长度和缓存列表思路完全一样,如缓存象select count(*) from T where topicId=2008这样的长度,也是放到topicId=2008这个散列Map中。如果再配合好使用mysql的拆表和memcached,加上F5设备做分布式负载均衡,该系统对付像1000万IP/天这种规模级的应用都足够了。

TAG:

 

评分:0

我来说两句

日历

« 2024-04-25  
 123456
78910111213
14151617181920
21222324252627
282930    

数据统计

  • 访问量: 95234
  • 日志数: 112
  • 图片数: 1
  • 文件数: 1
  • 书签数: 1
  • 建立时间: 2007-01-16
  • 更新时间: 2010-06-28

RSS订阅

Open Toolbar