接着上一篇 MySQL 数据库性能优化之缓存参数优化 ,这是 MySQL数据库性能优化专题 系列的第二篇文章:MySQL 数据库性能优化之表结构
很多人都将 数据库设计范式 作为数据库表结构设计“圣经”,认为只要按照这个范式需求设计,就能让设计出来的表结构足够优化,既能保证性能优异同时还能满足扩展性要求。殊不知,在N年前被奉为“圣经”的数据库设计3范式早就已经不完全适用了。这里我整理了一些比较常见的数据库表结构设计方面的优化技巧,希望对大家有用。
由于MySQL数据库是基于行(Row)存储的数据库,而数据库操作 IO 的时候是以 page(block)的方式,也就是说,如果我们每条记录所占用的空间量减小,就会使每个page中可存放的数据行数增大,那么每次 IO 可访问的行数也就增多了。反过来说,处理相同行数的数据,需要访问的 page 就会减少,也就是 IO 操作次数降低,直接提升性能。此外,由于我们的内存是有限的,增加每个page中存放的数据行数,就等于增加每个内存块的缓存数据量,同时还会提升内存换中数据命中的几率,也就是缓存命中率。
数据类型选择
数据库操作中最为耗时的操作就是 IO 处理,大部分数据库操作 90% 以上的时间都花在了 IO 读写上面。所以尽可能减少 IO 读写量,可以在很大程度上提高数据库操作的性能。
我们无法改变数据库中需要存储的数据,但是我们可以在这些数据的存储方式方面花一些心思。下面的这些关于字段类型的优化建议主要适用于记录条数较多,数据量较大的场景,因为精细化的数据类型设置可能带来维护成本的提高,过度优化也可能会带来其他的问题:
数字类型:非万不得已不要使用DOUBLE,不仅仅只是存储长度的问题,同时还会存在精确性的问题。同样,固定精度的小数,也不建议使用DECIMAL,建议乘以固定倍数转换成整数存储,可以大大节省存储空间,且不会带来任何附加维护成本。对于整数的存储,在数据量较大的情况下,建议区分开 TINYINT / INT / BIGINT 的选择,因为三者所占用的存储空间也有很大的差别,能确定不会使用负数的字段,建议添加unsigned定义。当然,如果数据量较小的数据库,也可以不用严格区分三个整数类型。
字符类型:非万不得已不要使用 TEXT 数据类型,其处理方式决定了他的性能要低于char或者是varchar类型的处理。定长字段,建议使用 CHAR 类型,不定长字段尽量使用 VARCHAR,且仅仅设定适当的最大长度,而不是非常随意的给一个很大的最大长度限定,因为不同的长度范围,MySQL也会有不一样的存储处理。
时间类型:尽量使用TIMESTAMP类型,因为其存储空间只需要 DATETIME 类型的一半。对于只需要精确到某一天的数据类型,建议使用DATE类型,因为他的存储空间只需要3个字节,比TIMESTAMP还少。不建议通过INT类型类存储一个unix timestamp 的值,因为这太不直观,会给维护带来不必要的麻烦,同时还不会带来任何好处。
ENUM & SET:对于状态字段,可以尝试使用 ENUM 来存放,因为可以极大的降低存储空间,而且即使需要增加新的类型,只要增加于末尾,修改结构也不需要重建表数据。如果是存放可预先定义的属性数据呢?可以尝试使用SET类型,即使存在多种属性,同样可以游刃有余,同时还可以节省不小的存储空间。
LOB类型:强烈反对在数据库中存放 LOB 类型数据,虽然数据库提供了这样的功能,但这不是他所擅长的,我们更应该让合适的工具做他擅长的事情,才能将其发挥到极致。在数据库中存储 LOB 数据就像让一个多年前在学校学过一点Java的营销专业人员来写 Java 代码一样。
字符编码
字符集直接决定了数据在MySQL中的存储编码方式,由于同样的内容使用不同字符集表示所占用的空间大小会有较大的差异,所以通过使用合适的字符集,可以帮助我们尽可能减少数据量,进而减少IO操作次数。
纯拉丁字符能表示的内容,没必要选择 latin1 之外的其他字符编码,因为这会节省大量的存储空间
如果我们可以确定不需要存放多种语言,就没必要非得使用UTF8或者其他UNICODE字符类型,这回造成大量的存储空间浪费
MySQL的数据类型可以精确到字段,所以当我们需要大型数据库中存放多字节数据的时候,可以通过对不同表不同字段使用不同的数据类型来较大程度减小数据存储量,进而降低 IO 操作次数并提高缓存命中率
适当拆分
有些时候,我们可能会希望将一个完整的对象对应于一张数据库表,这对于应用程序开发来说是很有好的,但是有些时候可能会在性能上带来较大的问题。
当我们的表中存在类似于 TEXT [...]
MySQL, MySQL数据库性能优化专题, performance tuning
在平时被问及最多的问题就是关于 MySQL 数据库性能优化方面的问题,所以最近打算写一个MySQL数据库性能优化方面的系列文章,希望对初中级 MySQL DBA 以及其他对 MySQL 性能优化感兴趣的朋友们有所帮助。
这是 MySQL数据库性能优化专题 系列的第一篇文章:MySQL 数据库性能优化之缓存参数优化
数据库属于 IO 密集型的应用程序,其主要职责就是数据的管理及存储工作。而我们知道,从内存中读取一个数据库的时间是微秒级别,而从一块普通硬盘上读取一个IO是在毫秒级别,二者相差3个数量级。所以,要优化数据库,首先第一步需要优化的就是 IO,尽可能将磁盘IO转化为内存IO。本文先从 MySQL 数据库IO相关参数(缓存参数)的角度来看看可以通过哪些参数进行IO优化:
query_cache_size/query_cache_type (global)
Query cache 作用于整个 MySQL Instance,主要用来缓存 MySQL 中的 ResultSet,也就是一条SQL语句执行的结果集,所以仅仅只能针对select语句。当我们打开了 Query Cache 功能,MySQL在接受到一条select语句的请求后,如果该语句满足Query Cache的要求(未显式说明不允许使用Query Cache,或者已经显式申明需要使用Query Cache),MySQL 会直接根据预先设定好的HASH算法将接受到的select语句以字符串方式进行hash,然后到Query Cache 中直接查找是否已经缓存。也就是说,如果已经在缓存中,该select请求就会直接将数据返回,从而省略了后面所有的步骤(如 SQL语句的解析,优化器优化以及向存储引擎请求数据等),极大的提高性能。
当然,Query Cache 也有一个致命的缺陷,那就是当某个表的数据有任何任何变化,都会导致所有引用了该表的select语句在Query Cache 中的缓存数据失效。所以,当我们的数据变化非常频繁的情况下,使用Query Cache 可能会得不偿失。
Query Cache的使用需要多个参数配合,其中最为关键的是 query_cache_size 和 query_cache_type ,前者设置用于缓存 ResultSet 的内存大小,后者设置在何场景下使用 Query Cache。在以往的经验来看,如果不是用来缓存基本不变的数据的MySQL数据库,query_cache_size 一般 256MB 是一个比较合适的大小。当然,这可以通过计算Query Cache的命中率(Qcache_hits/(Qcache_hits+Qcache_inserts)*100))来进行调整。query_cache_type可以设置为0(OFF),1(ON)或者2(DEMOND),分别表示完全不使用query cache,除显式要求不使用query cache(使用sql_no_cache)之外的所有的select都使用query cache,只有显示要求才使用query [...]
MySQL, MySQL数据库性能优化专题, performance tuning
最近很多朋友反馈之前开发的 myperf 工具自从增加了 snap 和 report 两个功能升级之后使用起来比以前复杂了,不仅参数选项增加了,还多了一个配置文件,而且还要创建数据库,不是很明白如何使用了。
为了让大家使用起来更方便容易,这里做一个简单的介绍。
文件组成
myperf
myperf工具主程序,可执行python文件
myperf.cnf
myperf工具的配置文件,用来存放各种配置项以减少命令行输入
myperf.sql
myperf工具用来存放性能数据的Schema结构创建文件,如果需要使用snap和report功能,就必须要通过此文件创建好Schema用来存放性能数据。当然,如果一直只是使用top模式,那就不需要使用了
功能组成
top
类似于Linux/Unix 下最常用最基本的性能查看工具 top 类似,实时刷新展示数据库中的各项核心性能指标信息
snap
收集数据库当前各项性能状态数据并存储在数据库中,用于性能分析调优,类似于 Oracle 的 Statspack 的 snapshot功能,目前收集的信息主要是 global status,如果是 MySQL5.5,还会手机Performance Schema 中的部分信息
report
和上面的snap是相辅相成的,主要是对snap存储的性能数据进行分析,获得性能报告,类似于 Oracle 的 Statspack 的 report 功能。当然,由于数据来源有限,和 Oracle Statspack 相比,还有很大的差距
由于配置文件对于程序运行比较重要,所以这里对配置文件再单独介绍一下。myperf 配置文件模仿 MySQL 的配置文件,也将配置项进行了分组,目前的3个分组分别如下:
main
用来配置与数据库连接无关的全局控制参数,如interval,mode,event,session等
source
用来配置被监控的源数据库的连接信息
target
用来配置存放监控数据的目标数据库连接信息,由于需要存储性能数据,所以这里的配置中还会有一个用来指定存放性能数据的database的参数项
最后,有个好消息宣布,那就是 myperf 迎来了第一位合作者 Leopku,@Leopku
myperf, MySQL, mysqltools, performance, performance tuning
myperf 继 0.1-beta 版发布,可以实时在线显示 MySQL 性能数据并定期刷新之后,最近又增加了计划中的另外两个小功能:
定期收集MySQL数据库的性能数据并存储(snapshot) 由于初衷主要是考虑到多台 MySQL 服务器,所以数据会集中存储到一台集中的 MySQL Server中,并没有保存到自身。当然,以后有可能增加这样的功能。
根据性能数据的snapshot进行分析并生成report记录到文本文件 目前生成的报表还只能生成纯文本方式,不支持保存在数据库中。且只支持生成相邻两个snapshot的报表,暂不支持人工选择某两个snapshot之间的报表。
除了增加这2个功能外,还在原来的基础上增加了一个可选配置文件。主要是为了减少在命令行输入的内容,毕竟很多时候都在重复输入一些参数。当然,不喜欢配置文件方式的同学,选择不使用配置文件,直接将参数全部在命令行中输入。 由于现在 MySQL 5.5 增加了 Performance Schema,所以新版本中也增加了针对 Performance Schema 数据的收集,比如Event wait,File IO 等,只是目前针对这写数据的分析还比较弱,希望后续能加强一些。 源代码、配置文件以及存储snapshot数据所使用的Schema结构文件都放在 Google Code 上了,可以通过SVN工具匿名方式co出来,也欢迎猛击下载试用。 至此,之前设想的基本功能已经完成的差不多了,我的代码写的比较糙,有兴趣进一步完善的同学,欢迎 Gmail 给 sky000,一起维护完善这个小工具。
myperf, MySQL, mysqltools, performance, performance tuning
一直苦恼于 MySQL 没有像 Oracle Statspack 这样的性能分析工具,调优手段太少。很久以前自己学习 C 的时候,写了个简单的收集分析 MySQL 性能状态数据的小工具,之所以选择用 C 去写,一来是为了熟悉 MySQL 的 C API,熟悉 MySQL 代码,另一方面想捡回早就丢掉的 C 语言知识(后被证明很难实现,哈哈)。
随着 MySQL 5.5的出现,MySQL 新增了 Performance Schema,用于展示一些系统等待事件和性能信息。虽然仍然没有 DBA 最需要的 SQL 执行的信息,但至少比以前仅仅只有一些简单的 status 有了很大的进步。
所以最近又想完善一下这个被我自己称为 MySQL Performance Tool 的工具 “myperf” 。这次选择用较轻量级的脚本来实现,简单快速嘛(其实对我也是刚开始学习的,哈哈)
暂时计划包含以下3个功能:
显示实时性能状态 (top mode)
对性能状态做snapshot (snap mode)
分析性能状态,输出报告 (report mode)
目前暂时先做了第一个功能点,连接到需要查看的数据库后以类似于linux 上 top 命令的方式实时刷新,包括active session 也会输出。
实现比较简陋,当前目标是基本能用就行。下面是输出截图:
欢迎有兴趣和我一起完善这个工具的同学加入和我一起完善他,有兴趣请 gmail 给 sky000。
myperf, MySQL, mysqltools, performance, performance tuning