事务
事务是逻辑上的一组操作,要么都执行,要么都不执行。
数据库的事务特性 ACID :
- 原子性:事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
- 一致性:执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
- 隔离性:并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间的数据库是独立的;
- 持久性:一个事务被提交之后,它对数据库中的数据的改变是持久的,也就是说数据库发生故障也不该对其有任何影响。
并发事务导致的问题
- 脏读:一个事物读取到另外一个事物还未提交的数据。
- 不可重复读:一个事物先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。
- 幻读:一个事物按照条件查询数据时,没有对应的数据行,但是在插入数据时又发现这行数据已经存在了,好像出现了”幻影“,称之为幻读。
事务的隔离级别:
- READ-UNCOMMITTED(读取未提交):最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读和不可重复读。
- READ-COMMITTED(读取已提交):允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读和不可重复读仍有可能发生。
- REPEATABLE-READ(可重复读):对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。这是InnoDB引擎默认的隔离级别
- SERIALIZABLE(可串行化):最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读、幻读。
- MySql InnoDB引擎通过 redo log(重做日志)保证事务的持久性,使用 undo log(回滚日志) 来保证事务的原子性。
- MySql InnoDB 引擎通过锁机制、MVCC 等手段来保证事务的隔离性(默认支持的隔离级别是
REPEATABLE-READ 可重复读
)。 - 保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。
锁级别和机制
- InnoDB 支持行级锁和表级锁,默认为行级锁。
行级锁和表级锁的对比:
- 表级锁: MySql 中锁定粒度最大的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM 和 InnoDB 引擎都支持表级锁。
- 行级锁: MySql 中锁定 粒度最小 的一种锁,只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
数据库设计的三大范式
- 第一范式:确保每列保持原子性,每列都不可以再分
- 第二范式:确保表中每列都和主键相关
- 第三范式:确保每列都和主键列直接相关,而不是间接相关
索引
什么是索引
- 索引是帮助MySQL高效获取数据的数据结构(有序)
- 提高数据检索的效率,降低数据库的IO成本(不需要全表扫描)
- 通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗
在mysql的默认存储引擎InnoDB中,使用的B+树来实现索引的,B+树类似B树,也是一个多阶的树,不过它的非叶子结点不存储数据,只存储指针,叶子结点才存储数据,并且叶子结点之间还有双向循环链表。这些特点使得B+树相比于B树:1、查询效率更加稳定、2、B+树便于扫库和区间查询。
聚簇索引和非聚簇索引
- 聚簇索引(聚集索引):数据和索引放到一块,B+树的叶子结点保存了整行数据,有且只有一个,一般是主健索引
- 非聚簇索引(二级索引):数据和索引分开存储,B+树的叶子结点保存对应的主键,可以有多个
回表查询
通过二级索引找到对应的主键值,再到聚集索引中查找整行数据,这个过程就是回表
覆盖索引
覆盖索引是指查询使用了索引,并且需要返回到列,在该索引中已经全部能够找到。也就是说只需要查询一次,不需要去回表查询。
慢SQL
如何定位慢查询?
我们当时经常碰到这种满查询的情况,因为我们的系统是发展了20多年,里面的数据量很大,比如有几十个模块,每个模块下都有几十上百个元数据以及对应的应用。每个应用里面可能都有几十个字段。系统内有个录制spr的功能,它会录制你在某段时间内所有发出的请求、执行的sql、报错的堆栈,这里面执行的sql后面就有时间,可以很直观的看到这条sql所用的时间,接着就可以把这条sql复制下来看看是哪方面的问题了。
如果没有这个spr工具的话,也可以通过部署Arthas(阿尔萨斯)、Prometheus(普罗米修斯)、Skywalking这些工具来查看。
还可以通过mysql的慢日志工具查询,以及数据库连接池比如Druid(德鲁伊) 这种,也都有慢sql的记录
如何分析慢查询sql语句?
在我之前工作中有一套规范,比如不要用select *
、不要在in后面加太多,如果太多就用一个子句、不要在代码的循环中查sql。通过遵守这些规范就可以避免部分sql层面带来的性能问题。
其次就是使用好explain工具。里面的key可以判断出该语句有没有命中索引,还有extra字段,额外的优化建议,type是sql的连接类型,避免是index(全索引扫描)和all(全盘扫描)