0
点赞
收藏
分享

微信扫一扫

MySQL重要知识点(总结)

最近一段时间都学习mysql,将重要的知识点总结如下:

  • 一、字段、表、索引设计规范相关
  • 二、事务相关
  • 三、锁相关
  • 四、存储引擎相关
  • 五、大表优化相关
  • 六、索引优化相关
  • 七、语句优化相关

一、字段、表、索引设计规范

1、字段设计规范
① 字段类型优先选择符合存储需要的最小类型

字段类型优先级:整型>date;time >enum>char;varchar>blob

② 够用就行(如smallint,varchar(N))
③ 尽量避免使用允许为null()

④ 避免使用ENUM类型
⑤ 使用TIMESTAMP(4个字节)或DATETIME类型(8个字节)存储时间
⑥ 金额类数据使用decimal
2、表设计规范
① 定长与变长分离
② 常用字段和不常用字段要分离.
3、索引设计规范
① 限制每张表中索引的数量,建议单张表中的索引不超过5个
② 禁止给表的每一列都建立单独索引
③禁止给表的每一列都建立单独索引
④每个Innodb表必须有一个主键

二、事务

事务保证一组原子性的操作,要么全部成功,要么全部失败。一旦失败,回滚之前的所有操作。MySql采用自动提交,如果不是显式的开启一个事务,则每个查询都作为一个事务。

1、事务的四大特性
①原子性
②隔离性
③持久性
④一致性
2、并发事务带来哪些问题
  • 脏读(Dirty read):
  • 丢失修改(Lost to modify)
  • 不可重复读(Unrepeatableread)
  • 幻读(Phantom read)

不可重复度和幻读区别:不可重复读的重点是修改,幻读的重点在于新增或者删除

3、事务的隔离级别有哪些
  • READ-UNCOMMITTED(读取未提交)
  • READ-COMMITTED(读取已提交)
  • REPEATABLE-READ(可重复读)
  • SERIALIZABLE(可串行化)


MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。我们可以通过SELECT @@tx_isolation;命令来查看


这里需要注意的是:与 SQL 标准不同的地方在于InnoDB 存储引擎在 REPEATABLE-READ(可重读)事务隔离级别下使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server)是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) 已经可以完全保证事务的隔离性要求,即达到了 SQL标准的SERIALIZABLE(可串行化)隔离级别。

因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容):,但是你要知道的是InnoDB 存储引擎默认使用 REPEATABLE-READ(可重读)并不会有任何性能损失。

InnoDB 存储引擎在 分布式事务 的情况下一般会用到SERIALIZABLE(可串行化)隔离级别。

三、锁

数据库通过锁机制来解决并发场景-共享锁(读锁)和排他锁(写锁)。读锁是不阻塞的,多个客户端可以在同一时刻读取同一个资源。写锁是排他的,并且会阻塞其他的读锁和写锁。简单提下乐观锁和悲观锁。

  • 乐观锁:通常用于数据竞争不激烈的场景,多读少写,通过版本号和时间戳实现。
  • 悲观锁:通常用于数据竞争激烈的场景,每次操作都会锁定数据。

要锁定数据需要一定的锁策略来配合。

  • 表锁:锁定整张表,开销最小,但是会加剧锁竞争。
  • 行锁:锁定行级别,开销最大,但是可以最大程度的支持并发。

但是MySql的存储引擎的真实实现不是简单的行级锁,一般都是实现了多版本并发控制(MVCC)。MVCC是行级锁的变种,多数情况下避免了加锁操作,开销更低。MVCC是通过保存数据的某个时间点快照实现的。

四、存储引擎

1、查看MySQL当前默认的存储引擎

我们也可以通过下面的命令查看默认的存储引擎。

mysql> show variables like '%storage_engine%';

通过下面的命令查看表的存储引擎

show table status like "table_name" ;
2、 MyISAM和InnoDB区别

MyISAM是MySQL的默认数据库引擎(5.5版之前)。虽然性能极佳,而且提供了大量的特性,包括全文索引、压缩、空间函数等,但MyISAM不支持事务和行级锁,而且最大的缺陷就是崩溃后无法安全恢复。不过,5.5版本之后,MySQL引入了InnoDB(事务性数据库引擎),MySQL 5.5版本后默认的存储引擎为InnoDB。

大多数时候我们使用的都是 InnoDB 存储引擎,但是在某些情况下使用 MyISAM 也是合适的比如读密集的情况下。(如果你不介意 MyISAM 崩溃回复问题的话)。

① 功能差异
② 存储差异
③ 选择差异

这块详细介绍可参考:我的另一篇《优化流程图》

五、大表优化当MySQL

单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下

1. 限定数据的范围

务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内;

2. 读/写分离

数据量增多,单机的数据库不足以支撑业务,需要用到数据库集群。而读写分离,就是将数据库的读和写分离,对应到数据库一般就是主从数据库,一主一从或者一主多从;业务服务器把数据写到主数据库中,读操作都去从库读;主库会同步数据到从库,保证数据的一致性。


这种集群方式,就是将访问的压力从主库转移到从库,单机的数据库不能支撑并发读写的时候,而且读的请求很多的情况下就适合数据库集群。如果写的操作很多的话,那就不适合这种集群方式,因为写的时候是写入主库,主库的压力还是没有变化,同时同步到从库也需要消耗资源。

单机的时候,一般数据库优化就是加索引,但是加了索引对查询有优化,但是写入的时候会有影响,因为写入的数据库不会更新索引。所以在主从数据库中,可以单独的对读库(从数据库)做索引,而写库(主数据库)可以减少缩影而提高写的效率。

但是主从数据库中需要注意:主从同步延迟、分配机制的考虑
①主从同步延迟
  • 二次读取
  • 写了之后的马上读操作操作主库
  • 根据业务,将重要的业务数据的读写都放在主库,其他的业务进行读写分离
② 分配机制的考虑

分配机制的考虑,就是怎么制定去操作去主库写,去从库读。
*数据库中间件

开源的中间件有Mysql Proxy,Atlas。

3. 垂直分区

根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。

简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。 如下图所示,这样来说大家应该就更容易理解了。

  • 垂直拆分的优点: 可以使得列数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。
  • 垂直拆分的缺点: 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行Join来解决。此外,垂直分区会让事务变得更加复杂;
4、水平拆分

保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 水平拆分可以支撑非常大的数据量。

水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。举个例子:我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。


水平拆分能够 支持非常大的数据量存储,应用端改造也少,但 分片事务难以解决 ,跨节点Join性能较差,逻辑复杂。《Java工程师修炼之道》的作者推荐 尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度 ,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。

六、索引优化

1、常见索引列建议
① 出现在SELECT、UPDATE、DELETE语句的WHERE从句中的列,包含在ORDER BY、GROUP BY、DISTINCT中的字段,多表join的关联列。并不要将符合1和2中的字段的列都建立一个索引, 通常将1、2中的字段建立联合索引效果更好。
  • 举个栗子:
    在where条件常用的列上都加上索引
    例: where cat_id=3 and price>100 ; //查询第3个栏目,100元以上的商品
    误: cat_id上,和, price上都加上索引.
    错: 只能用上cat_id或Price索引,因为是独立的索引,同时只能用上1个.
② 避免建立冗余索引、重复索引
③ 索引的列如果是表达式的一部分或者是函数的参数,则失效
④ 针对特别长的字符串,可以使用前缀索引,根据索引的选择性选择合适的前缀长度(详细参考:如何选择合适的前缀长度)
⑤ 使用多列索引的时候,可以通过 AND 和 OR 语法连接
⑥ 将范围查询放在条件查询的最后,防止范围查询导致的右边索引失效的问题
⑦ 索引最好不要选择过长的字符串,而且索引列也不宜为null
⑧ 对于频繁的查询优先考虑覆盖索引
2、选择索引列顺序

3、分析explain执行计划
EXPLAIN SELECT settleId FROM Settle WHERE settleId = "3679"

  • select_type,有几种值:simple(表示简单的select,没有union和子查询),primary(有子查询,最外面的select查询就是primary),union(union中的第二个或随后的select查询,不依赖外部查询结果),dependent union(union中的第二个或随后的select查询,依赖外部查询结果)

  • type,有几种值:system(表仅有一行(=系统表),这是const连接类型的一个特例),const(常量查询), ref(非唯一索引访问,只有普通索引),eq_ref(使用唯一索引或组件查询),all(全表查询),index(根据索引查询全表),range(范围查询)

  • possible_keys: 表中可能帮助查询的索引

  • key,选择使用的索引

  • key_len,使用的索引长度

  • rows,扫描的行数,越大越不好

  • extra,有几种值:Only index(信息从索引中检索出,比扫描表快),where used(使用where限制),Using filesort (可能在内存或磁盘排序),Using temporary(对查询结果排序时使用临时表)

七、语句优化建议

1. 建议使用预编译语句进行数据库操作
2. 避免数据类型的隐式转换
select name,phone from customer where id = '111';
3. 充分利用表上已经存在的索引
4. 禁止使用SELECT * 必须使用SELECT <字段列表> 查询
5. 避免使用不含字段列表的INSERT语句
如:
insert into values ('a','b','c');
应使用:
insert into t(c1,c2,c3) values ('a','b','c');
6. 避免使用子查询,可以把子查询优化为join操作
7. 避免使用JOIN关联太多的表
8. 减少同数据库的交互次数
9. 对应同一列进行or判断时,使用in代替or
10. 禁止使用order by rand() 进行随机排序
11. WHERE从句中禁止对列进行函数转换和计算
  • 不推荐:
where date(create_time)='20190101'
  • 推荐:
where create_time >= '20190101' and create_time < '20190102'
12. 在明显不会有重复值时使用UNION ALL 而不是UNION
13. 拆分复杂的大SQL为多个小SQL
举报

相关推荐

0 条评论