mysql由服务器端与存储引擎两部分组成,存储引擎部分的锁机制对服务器端是透明的。服务器端内置缓存机制,有解析器和优化器机制。不同的存储引擎对事务、并发等都用不同的处理。
ACID代表的特性:原子性、一致性、隔离性、持久性
共享锁与排他锁,也叫读锁和写锁。锁中根据不同的锁定级别又分为表锁与行锁。
mysql中处理死锁的方式一般都设有死锁检测与死锁超时。如InnoDB中是将 持有最少行级排他锁的事务进行回滚
行级锁的变种:多版本控制(MVVC)比如InnoDB。InnoDB的处理方式是在每行后都加两列隐藏列,一个用以保存行的创建时间,一个用以保存行的删除时间,当然具体存储的是版本号的值。有此版本号的存储虽消耗了存储空间,但是却因为不用加锁而提高了性能
特别注意:MVVC仅会在提交读和可重复读这两种隔离级别中有作用
隔离级别:
未提交读:脏读、不可重复读、幻读。出现问题几率很大,实际很少使用。
提交读:不可重复读、幻读
可重复读:幻读 mysql默认使用级别 InnoDB使用间隙锁的方式来避免出现幻读的可能
串行:强制串行化,效率最低,很少使用
脏读:读取到了其他事务还未提交的操作;不可重复读:前后相同条件的语句读取到的结果前后不一致;幻读: 前后相同条件的语句读取到的结果数量前后不一致
事务日志:
数据库操作表时是在内存中操作的,并不是每次都进行随机IO移动磁头,而是会以追加的方式记录在事务日志中后,慢慢的统一向磁盘中写进行顺序IO,这样可以大幅提高效率。称为 预写式日志。也就是修改数据需要操作磁盘两次。
存储引擎:
** 暂时一般情况下推荐使用最安全的InnoDB引擎,它支持并发,使用行级锁,有良好的事务支持可以进行崩溃恢复,并且性能做了很多优化; 如果是日志型应用,不考虑事务与崩溃情况,但要求插入效率可以考虑MyISAM引擎,MyISAM引擎支持压缩表技术,对于只读数据提供了良好的读取效率和占用空间小的优点。MyISAM使用的是表锁,InnoDB为行级锁,MyISAM的性能瓶颈是表锁。infobright是Mysql一个开源数据仓库,可以支持大数据量应用,例如10TB以上数据。
切换引擎的三种方式:1、直接使用ALTER TABLE mytable ENGINE = InnoDB 来切换,优点是简单高效 缺点是占用大量的IO,并且原表会加表锁 特别值得注意的是例如在将InnoDB引擎切换为MyISAM引擎后,再切换回InnoDB引擎后,原表的外键将全部丢失; 2、使用导出与导入功能,在导出后手动修改SQL语句中的ENGINE,但是注意表名是不能相同的;3、创建与查询 使用结合了第一种的高效与第二种的安全。
基准测试-- sysbench 用于大概估量系统的承载力
测试指标:(吞吐量、响应时间或延迟、并发性、可扩展性)-- 数据量尽量贴近真实数据量大小。并且经过一段时间波动后稳定后看实际指标。 CPU利用率硬件资源就是用来消耗比进行服务的,吞吐量就是每秒可以返回更多的数据 是一个衡量指标但更重要的是响应时间
绘图的重要性。
性能剖析 -- 性能 即响应时间
可能导致的两种不合适的测量:
1、在错误的时间启动或停止测量 如果查询到了慢查询 就应该去解决慢查询的问题而不是从系统整体行为看
2、测量的是聚合后的信息,而不是目标活动本身 完成一项任务时间一般分为两部分 执行时间和等待时间
优化原则议题
选择可以处理存储数据的最小的数据类型,这样可以意味着使用更少的存储空间,更低的硬件资源。后期数据量大后再进行字段类型的更改是非常耗资源耗时的事情
使用简单数据类型,可以减少CPU的周期,比如使用整型相对于字符型存储,可以减少校验规则和字符集的不同,更快。日期及ip地址都可以使用整型进行存储
尽量避免NULL列,尤其是需要使用到索引的列。因为创建包含NULL列的索引记录会占用更多的空间
TIMESTAMP和DATATIME都可以存储时间并且都支持到秒,但TIMESTAMP仅需要一半的存储空间,并且还会自动的根据时区进行变化,但是它的缺点允许的时间范围要小得多
查看表创建的语句 这时 用如使用别名的数据类型 都会变为基本数据类型
SHOW CREATE TABLE 表名
整数类型:**整数型中的数字长度对数据库保存无影响,只是用来在客户端工具中显示的数字,即INT(1)和INT(20)是相同的
实数类型:FLOAT和DOUBLE是浮点数,得到的是近似值,但由于CPU可以直接计算浮点数,故速度快;DECIMAL是存储精确的小数,但是资源消耗大,MySQL内部有自己实现DECIMAL数据类型的一套逻辑。仅在需要特别精确数字时才应该使用DECIMAL类型,例如财务数据。可以处理的一种方式是将需要存储为DECIMAL类型的小数乘以倍数,然后存储为BIGINT类型,这样可以解决浮点类型计算不精确和DECIMAL类型精算资源消耗大的问题
字符串类型:VARCHAR和CHAR由于存储引擎的不同,由于校验规则与字符集的不同,对数据的存储与性能有很大的区别。VARCHAR可变长字符串存储,它会使用1个或者2个额外的字节来存储字符串的长度。使用情况最大长度比平均长度大很多,这样碎片就不是问题(如果长度不够了,MyISAM与InnoDB会分别用自己的方式处理数据,MyISAM将行分拆成片段存储,InnoDB用分裂页将行放进页内);或者使用了UTF8这样字符集,每个字符使用不同的字节数进行存储。在使用字符串类型时需要使用需要的空间进行存储。
字符类型:定长的类型。存储建议比如如果要存储的数据都是差不多等长的数据则使用CHAR进行存储更好,或者存储的数据很小,效率更高占用空间更小。经常变更的数据使用CHAR进行存储可以减少碎片的产生
与字符串及字符类型类似,BINARY和VARBINARY用以存储二进制类型,MySQL以字节来处理而不是字符,速度更快
BOLB与TEXT:如果存储的值非常大InnoDB引擎会使用外部存储并在每行中使用1-4字节存储指针。BOLB存储二进制没有排序规则与字符集,TEXT有排序规则与字符集。大字段MySQL进行排序时并不是按照所有内容进行排序的,而是根据max_sort_length进行排序的。建议能避免使用大字段就不用大字段,必须使用的情况下使用SUBSTRING(COLUMN,LENGTH)来转换为字符串进行操作,但要确保长度不能超过max_head_table_size或tmp_table_size,这样使MySQL使用内存临时表而不是MyISAM的磁盘临时表进行操作,如果EXPLAIN执行计划中显示了USING TEMPORARY则表示使用了隐式表。
在某些情况下使用枚举类型来替代字符串的存储,优点让表的空间占用更小,转换后的主键也变小了,其他键上建立索引也会让索引变的小了。但需要注意的是,使用枚举类型进行排序的时候不是根据字符串的值进行排序的,而是根据底层存储的数字的顺序进行排序的,所以要么是在建立枚举类型时候就按照字母规则进行插入,要么就使用FIELD函数,但使用函数就会使索引失效,还有一点需要注意的是枚举类型最适合之后不会有修改的数据,如果使用ALTER TABLE重新修改枚举,则将会导致MySQL重建整个表
DATETIME与TIMESTAMP:范围不同TIMESTAMP使用UNIX时间戳,仅支持到2038年原因是32位超出极限然后覆盖值,而DATETIME支持到9999年;保存方式不一样,TIMESTAMP会根据时区的不同动态转换时间而DATETIME存储的只是文本;TIMESTAMP效率更高些;TIMESTAMP默认是NOT NULL的,一般建议存储为TIMESTAMP类型。
关于标识列:强烈建议使用整型系列来进行存储,不建议使用字符串来进行存储。使用类似MD5()或者UUID()来生成的随机值,会存储于分布在不同的空间内,会导致INSERT或SELECT语句很慢。那么如何处理这种形式的存储:将UUID()去掉'-'或者使用UNHEX()转换UUID为16字节,然后存储为BINARY(16)中,查询时使用HEX()格式化十六进制的格式。
** 关于关联列一定要使用相同的数据类型进行存储,不能出现不同数据类型的列进行关联的问题
** 应该使用无符号整数来存储IP地址
陷阱:1、太多的列,在存储层与服务器层使用MySQL使用行缓冲格式转列的代价非常大 2、太多的关联,建议最多仅在12张表之内进行关联查询可以保证一定的性能 3、变相的枚举 4、在不能避免的需要使用NULL的情况下,可以使用NULL进行存储而不会将数据变得更加的复杂
范式与反范式 (信息数据是否存在冗余)
范式与反范式的优点与缺点:范式的缺点就是一般都需要最少的一次关联查询,代价昂贵,并且有可能会是索引失效。反之为反范式的优点,可以不进行关联查询仅在单一表中进行操作,并且可以更好的使用索引策略。实际情况一般是混用范式与反范式的情况,适当的冗余某些字段可以减少关联查询的消耗(需要排序的字段、缓存衍生值如人员访问的计数),提高索引策略提高查询速度,当然冗余数据在UPDATE等操作时会增加新的消耗,这需要平衡考虑。
缓存表与汇总表
定期重建与实时维护,根据实际的可接受的程度定期重建可以减少碎片的产生,有完全顺序的组织索引并且会减少资源的占用。在进行重建过程中要保证目前表依然可以使用,使用"影子表"来处理此问题。 缓存表可以使用不同的引擎,如使用MyISAM可以做缓存表可以减少索引的大小,还有可能会导出到其他搜索工具中进行检索的操作
计数器表设计:为了提高查询效率,可以单独设计一个表用来存储计数字段。为了提高并发插入的性能,可以创建几行数据分别进行存储计数条数,在进行插入操作时随机进行行数的插入,最后统计SUM的值,这样避免了串行阻塞的操作。分表进行存储,例如一天一个记录的频率。
ALTER TABLE陷阱:MySQL在做ALTER TABLE语句时是通常创建一个新表,将所有现有数据进行拷贝,然后再删除旧表,这是一个非常糟糕的执行,大部分情况下会引起MySQL服务的阻塞中断。常见的两种处理方式是:
1、在一台不对外提供数据库服务上进行ALTER TABLE操作,然后和提供服务的主库进行切换
2、使用影子拷贝。表结构创建一张与源表无关的新表,然后通过重命名和删除操作交换两张表
对于仅修改.frm文件的处理方式,ALTER TABLE语句是非常快的。例如:
所有的MODIFY COLUMN会导致重建表,而ALTER COLUMN会直接操作.frm文件,速度非常快。
值得注意的是ALTER TABLE允许使用ALTER COLUMN、MODIFY COLUMN和CHANGE COLUMN 这三种操作都是不一样的
** 不被官方支持的修改.frm方法暂略
索引 -- (本博客中已有一篇总结索引基本使用方法文章可以参考)
BTree与哈希索引:
BTree对索引列是顺序组织存储的,所以很适合查找范围内的数据。每一节点页左右两边都有存储下一节点页的指针,叶子结点存储值,搜索结果要么找到值要么找不到值,特别说明的是BTree的深度是与表的大小有关的。其中BTree中特别注意的几个问题有:1、全值匹配 2、最佳左前缀 3、不要跨列使用索引 这样会导致后面字段的索引失效。由于BTree是顺序存储索引的,因此是支持ORDER BY的排序的,特别指出的是在创建索引的时候要注意字段的顺序和SQL的使用顺序,也要注意MySQL解析器的执行顺序会索引的影响。(其他语句方面要注意的问题避免索引可能失效的情况在另一篇文章中有较详细的记载)
哈希索引在Memory引擎中是显示支持的索引类型,哈希必须精确的匹配所有列才可以使用到索引,通过键值计算的哈希码在哈希表中寻找数据的指针寻找数据。特别指出的是Memory引擎支持多列非唯一哈希索引,也就是说可能存在相同哈希值的多行值,这时候Memory引擎内部是通过链表的形式存储的。哈希索引简单优缺点说明:速度相当快,并且索引仅存储哈希码而不是具体的值因此空间小,但它有特定的使用场景,它不能部分使用索引就是说复合索引(A,B)不能单独仅用A,仅能支持等值匹配也不支持范围查询,并且无法用于排序,如果遇到哈希冲突的问题引擎需要都需要遍历一遍来确定符合条件的行。
InnoDB的自适应哈希索引,当InnoDB引擎检测到某些索引值使用非常频繁时会在内存中基于BTree在其之上建立一个哈希索引,此哈希索引非上文提到的哈希索引,仅是InnoDB可以使用哈希值而不是键本身进行索引查找。下面主要介绍如何应用以提高性能:
去掉原url使用字符串方式创建的索引,使用url_crc进行索引的应用,具体来说如下:创建表和触发器
这样在每插入一条url记录的同时会计算一个crc32的值插入到记录中,然后根据此字段进行哈希值的整数快速比较进行索引,要比字符串比较快很多。当然这里要指出的是crc32会在大数据量中存在哈希冲突(可以使用自己生成无冲突的64位哈希函数返回整数或者截取MD5()的方法实现)以及这样的方式会增加一个维护索引列的缺点。那么如何处理哈希冲突的问题?
如上图SQL语句中带上原字段的常量值。使用FNV64()函数会比CRC32()函数冲突少,可以以插件的形式在MySQL中使用。
索引的3大优点:1、大大减少的服务器需要扫描的数据量 2、帮助服务器避免排序和临时表 3、可以将随机IO变为顺序IO
索引的3星理论:1、索引将相关的记录放到一起 2、索引中的数据顺序和查找中的排列顺序一致 3、索引中的列包含了查询中需要的全部列
关于可能会导致索引失效的SQL一些注意项,在另一篇文章中已经有较详细的说明。这里记录下关于前缀索引,什么是前缀索引就是对于很长的数据字段,在建立索引时仅截取前面一段长度的数据作为索引,这样可以减少数据大小,提高索引速度,当然对于前缀索引的截取长度要进行测试,使它尽可能的准确值接近真实数据长度的数量值。有两种方法:
1、首先查询实际字段长度查询数据量,然后截取需要创建索引的字段使长度慢慢增加直到查询的结果接近于实际查询出的数量级则结束
2、使用 select count(DISTINCT LEFT(city,3))/count(*) from table 来计算完整列的选择性,当选择性接近实际 select count(DISTINCT city)/count(*) from table 则合适
前缀索引的缺点是 无法进行ORDER BY和GROUP BY,以及不能进行索引覆盖
创建前缀索引:
聚簇索引与非聚簇索引
覆盖索引
using index
使用索引做排序
只有当索引的列顺序和order by字句的顺序完全一致,并且所有列的排序方向都一样时,MySQL才能使用索引来对结果做排序。如果是多表关联查询时,order by必须是第一张表的字段。上述条件也必须符合最佳左前缀,当然如果索引左边为常量,则可以使用后面的索引列,但仍不允许跨列访问。
压缩索引-- 前缀压缩 例: (第一个索引块perform 第二个索引块performance 则第二个实际存储为 7,ance)
在使用更少的存储空间的同时带来的可能是更慢的速度,因为前缀被压缩了,故不能使用二分查找,正序排序还可以,但order by desc就不是很好了。对于cpu密集型应用,大多都是随机查找,对于压缩索引来说是很慢的,如果是倒序排序则更慢。如果是IO密集型应用,则查询效果好处比成本好的多。(cpu密集型指的是注重计算型的应用,cpu经常100%的操作,io并不是系统资源消耗的主要问题;io密集型指的是注重io数据交换,比如web应用,cpu利用率常常为很低的利用状态)
冗余索引与重复索引-- 要避免重复索引,适当情况下可以存在冗余索引
重复索引意义为在相同字段上存在多个完全相同的索引,应该避免出现此种情况;冗余索引意义为存在A,B索引的同时又存在A索引,A,B索引符合最佳左前缀。比如当既需要单列索引又需要多列索引时,则需要冗余索引,但带来的缺点是维护成本高,尤其是进行insert、update、delete等操作,新增索引后导致内存瓶颈后。
索引设计技巧 -- 不是针对每一条SQL都单独设计索引,而是需要全局考虑让索引在符合最佳左前缀的前提下,符合更多的SQL语句
在设计复合索引时,尽量让更多的SQL语句可以匹配上,如果中间某个值不在SQL中可以使用IN来列举所有范围值,使索引符合最佳左前缀,将范围值字段放在最后。