RE:MySQL
Posted asleaf
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RE:MySQL相关的知识,希望对你有一定的参考价值。
MySQL常用引擎
一般来说,MyISAM适合读密集型的表,InnoDB适合写密集型的表。
字符集与校对规则
字符集(charset):二级制编码到指定字符的映射。
校对规则(collate):当前字符集下的排序规则。
UTF8是我们常用的字符集,它的常用校对规则如下:
-
utf8_general_ci:UTF8的默认校对规则,不支持扩展,仅在字符之间逐个比较,速度很快。
-
utf8_unicode_ci:依据Unicode的算法(UCA)执行,支持扩展,即可以将一个字母看作与其它字母组合相等,准确性比utf8_general_ci高。
-
utf8_bin:以二进制数据编译存储每个字符。
一般情况下选择utf8_general_ci即可。
UTF8MB4是MySQL在5.5.3之后增加的编码,MB4指Most Bytes 4,专门用来兼容4字节的Unicode。作为UTF8的超集,UTF8MB4有更好的兼容性。
索引
MySQL索引使用的数据结构主要有BTree索引和哈希索引:
-
哈希索引:底层是哈希表,作为单条记录查询时性能很快。
-
BTree索引:多数场景下的索引选择,底层是BTree中的B+Tree,两种引擎对其的实现方式不一样:
-
MyISAM:非聚簇索引,B+Tree的叶节点存放数据的地址,检索时如果指定key存在,先取出地址然后按地址读取数据记录。
-
InnoDB:聚簇索引,数据文件本身就是索引文件,B+Tree的叶节点存放了完整的数据记录,主索引的key也就是数据表的主键,而其他的辅助索引会存储相应记录主键的值而不是地址。检索时,如果依据主索引找到key即可直接读取数据,如果依据辅助索引查找,则需要先取出主键值再走一遍主索引。
-
应用索引时的一些注意事项:
-
最左前缀原则:查询条件精确匹配联合索引的左边一列或连续多列。
-
避免对WHERE字句中的字段附加函数,否则无法命中索引。
-
主键应该是与业务无关的自增字段,否则可能导致索引混乱。
-
利用索引覆盖:查询条件命中索引的同时查询字段也属于索引中的字段。
-
避免冗余索引:尽量扩展已有索引而不是创建新索引,否则可能导致索引的功能相同即索引冗余。
-
附加索引的列应该设置为NOT NULL,否则索引会被放弃而进行全表扫描。
-
删除长期未使用的索引,减少不必要的性能消耗。
-
联合查询时ON或USING中的列应附加索引,GROUP BY或ORDER BY中的列应只涉及一个表。
-
查询条件的字段应使用正确的数据类型,否则MySQL的自动类型转换会导致无法命中索引。
索引虽然加快了查询速度,但是也会消耗更多的存储空间与维护资源,同时影响INSERT、DELETE的速度。索引并不是越多越好,如果表数据较少或者重复数据较多时不建议建立索引。
事务机制
事务特性:
-
A(Atomic)原子性:一个事务只会全部成功或全部失败。
-
C(Consistency)一致性:事务的运行不会改变数据的一致性。
-
I(Isolation)独立性:并发的事务之间不会互相影响。
-
D(Durability)持久性:事务提交后,该事务对数据所作的修改永久地保存在数据库中。
事务隔离级别:
-
Read Uncommitted:可能出现脏读、幻读、不可重复读,实际情况下不会用到。
-
Read Committed:可能出现幻读、不可重复读,JDBC的默认事务级别,同时
-
Repeatable Read:可能出现幻读,MySQL的默认事务级别。
-
Serializable:不会出现任何并发问题,但事务并发性急剧下降。
MySQL5.0之前,因为主从复制不一致性的问题,MySQL选择了Repeatable Read作为默认的隔离级别,但是在互联网项目中,一般会将隔离级别设置为Read Committed,这样可以减少死锁的出现几率,提高并发性。
事务隔离的实现基于锁机制与并发调度:
-
表级锁:对当前操作的表加锁,简单快速,不会出现死锁,但是并发度低。
-
行级锁:对当前操作的行加锁,锁粒度小,并发度高,但是消耗资源更多,会出现死锁。
虽然行级锁的粒度小、并发度高,但在有些情况下,如表数据非常多或事务比较复杂,应用表级锁的效率会更高。
锁机制可进一步划分为共享锁和排他锁:
-
共享锁(S):又被称为读锁,其他用户可再加共享锁读取数据,但是获取共享锁的事务只可读取而不可修改数据。
-
排他锁(X):又被称为写锁,一个事务对数据加上排他锁后,其他事务不可再对此数据加任何形式的锁。获取排他锁的事务既可读取又可修改数据。
表数据过多时的优化方案
-
限制数据范围:限制查询语句的数据范围,例如查询订单记录时,只在最近一个月的数据中筛选。
-
读/写分离:经典的数据库拆分方案,主库写、从库读。
-
缓存:使用MySQL的查询缓存或者建立缓存层。
-
垂直分区、水平分区:按照一定规则拆分数据或者应用数据库中间件等。
高可用方案
-
MHA,Master High Availablity,一主多从模式,也是最常用的方案。管理节点会定时探测集群中的Master节点,如果出现故障,自动将存储最新数据的Slave提升为新的Master,然后将其他的Slave指向新的Master。
-
MMM,Multi-Master Replication Manager,双主模式。同一时刻只有一个主允许写,另一个主允许读,一个主挂掉,其下面的Slave同样挂掉。因为存在数据不一致问题,此方案适合数据一致性要求不高的业务。
主从模式中,经常遇到的一个问题是Slave数据滞后于Master,一个解决方案是MySQL5.5之后引入的半同步复制机制,MySQL客户端请求时阻塞,直到数据至少已同步到一个Slave或者超时,这样可在一定程度上提高数据的一致性。
DDL注意事项
-
禁用存储过程、函数、触发器、外键约束,让它们尽量依赖于业务层面的处理,这样可以具有良好的扩展性。
-
非(!=)查询不会匹配值为NULL的列,例如 where status != ‘FINISH‘ 不会挑选出status=NULL的记录。
-
尽量使用枚举或整数类型代替字符串类型。
-
ID使用BIGINT即可,对应Java的Long类型。
-
整数类型指定宽度并不影响存储大小,例如指定INT(11)后,INT依然有32位存储空间,宽度仅表示显示长度。
-
避免使用DECIMAL和浮点数,可以用BIGINT代替,即浮点数乘以一个倍数。
-
表中最好带有创建和更新时间戳,以及创建或修改的用户标识,这是为了更好地审计和追踪数据。
-
不要真的删除数据,可以给它们设置删除标记或者做版本化处理。
-
单表不要设置太多字段。
-
用整数类型而不是字符串存储IP。
DML注意事项
-
查询语句不要使用全属性选择器(*)。
-
尽量避免 "!"、"<>"、"or" 查询,而使用 "in" 代替,"in" 的查询效率是log(n)。
-
模糊匹配时,只有后缀模糊查询才可以命中索引,如like ‘a%‘。
-
查询记录数目时,count(1)与count(*)等价,都会在有索引时选择合适的索引,没有索引时进行全表扫描。count(column)只统计column不为NULL的记录条数。
-
避免多于两表的Join,尽量使用冗余策略解决联表问题。
-
避免列运算。
-
以批量插入代替循环插入,减少网络I/O开销。
-
避免过长的SQL语句,可以拆成多个小的语句,以减少锁的时间。
-
使用UNION时,MySQL会给临时表加上DISTINCT唯一性检查,如果不需要去重处理,应该使用UNION ALL。
-
在主键自增的表中插入一条记录后,可以用SELECT LAST_INSERT_ID() 获取这个自增值。
-
查询数据为bit类型时,需要加0才可以看到其值,如select bit_column+0 from test,显示为十进制数。
数据库连接池
数据库连接池避免了频繁创建、销毁连接带来的性能开销,常见的连接池有以下几个:
-
HikariCP:功能简单,性能优秀,起源于BoneCP。
-
Druid:alibaba开源的数据库连接池,有监控、统计功能,性能良好。
-
DBCP:commons-pool之上的封装。
-
C3P0:历史久远,逻辑复杂。
参考资料
以上是关于RE:MySQL的主要内容,如果未能解决你的问题,请参考以下文章