MySql总结


总结一下mysql吧。

mysql逻辑架构

mysql在逻辑上可以分为以下几个部分:

  • 连接管理器:负责与客户的建立建立,并负责进行权限的校验;
  • 查询缓存(8.0中以及去掉了);
  • 解析器:用于解析sql,明确用户的意图;
  • 优化器:对sql的执行进行优化,例如使用哪些索引;
  • 执行器:负责驱动sql语句执行;
  • 存储引擎:负责存储数据;

sql的类型

对于我们程序员来说,写sql语句是日常工作的最主要内容之一,sql语句主要可以分为四种:

  • DDL:数据定义语言,包括create table、create index等;
  • DCL:数据控制语言,包括一些与权限控制相关的sql语句;
  • DML:数据修改语言,包括delete/update/insert;
  • DQL:数据定义语言,就是我们写的最大的select;

mysql锁

锁可以分为乐观锁和悲观锁两大类,对于乐观锁,mysql并没有提供直接的实现,我们可以通过数据表来实现,利用版本号来实现乐观锁的语义。

mysql中提供的锁基本都属于悲观锁,主要有全局锁、表锁、行锁。

  • 全局锁共享模式:flush tables read lock; unlock tables;

  • 全局锁排他模式:flush table write lock; unlock tables;

  • 表锁共享模式:lock table t_name read; unlock tables;

  • 表锁排他模式:lock table t_name write; unlock tables;

  • 行锁共享模式:sql lock in share mode;

  • 行锁排他模式:sql for update;

另外还有一种元数据锁,当对表数据进行修改时,就会加表的元数据写锁,进行crud的时候,会加元数据读向,相当于表锁的另外一种模式。 需要注意一点的是对表结构的修改要极其小心,如果对于表的业务量比较大的话,由于修改表结构会加元数据写锁,此时所有的crud操作都会被阻塞,如果请求量非常大,会瞬间造成大量的业务请求抛异常,造成业务请求堆积,如果上层业务再有重试机制,那就更麻烦了,这些堆积的请求很可能把mysql直接压死,造成整个系统的雪崩,这个坑小编在真实场景中踩过,只不过没有那么严重,所以,修改表结构一定要找DBA来操作。

事务隔离级别

事务的特点:

  • 原子性:在一个事务中的所有操作要么全部成功要么全部失败;
  • 一致性:在事务执行前后,数据应该保持一致,例如转账,综合不能有变化;
  • 隔离性:为解决事务并发问题,应该将不同的事务进行隔离处理,以保证逻辑的正确性;
  • 持久性:事务结束后,数据应该被持久化处理;

并发事务存在的问题:

  • 脏读:一个事务读到了另一个事务中未提交的数据;
  • 不可重复读:在一个事务中多次读取同一条数据得到的结果不一致;
  • 幻读:在一个事务中多次读取同一组数据时,返回的数据总数不一致;

为解决并发事务存在的问题,mysql提供的几种事务隔离级别:

  • read uncommited:读未提交,不能解决任何问题,一般不用;
  • read commited:读已提交,能够读到其他事务中已经提交的数据;
  • repeatable read:可重复读,能够解决不可重复读问题;
  • serializable:串行化,能解决所有问题;

在深入事务原理之前,我们先来明确结构与事务实现相关的概念:

  • MVCC:全称是Multi Version Concurrent Control,即多本版并发控制,mysql通过MVCC机制实现了各种不同的事务隔离级别;
  • 当前读:读取最新版本的数据;
  • 快照读:读取快照数据,不一定是最新版本;

下面再简单说说上面四种事务隔离级别的实现原理。

对于read Uncommited这种隔离级别,因为逻辑比较简单,就是读取最新的数据(注意并不是读取最新版本的数据),所以底层并没有使用到MVCC机制,而就是简单的读取磁盘上最新的数据。Read Commited 和 Repeatable Read这两种隔离级别都用到了MVCC机制,在事务开始时,会创建当前数据的快照(这里的快照并不是复制一份,每个事务都有唯一的一个事务id,通过事务id+版本号的方式来实现,底层数据实际上只有一份,而这里的快照相当于一个指针,指向了数据的不同位置),对于Read Commited,在读取数据的时候,是当前读,也就是读取其他事务以及提交了的最新的数据,而对于Repeatable Read,在整个过程中,都只会从事务开始时的快照中读取数据(但这里只针对以及存在的数据,对于新增的数据无效,也就是幻读),来保证多次读取数据的一致性。Serializable底层也没有使用MVCC机制,而是粗暴的将所有事务串行化处理,虽然最能保证数据的一致,但性能最差,一般也不会使用,在mysql中,默认的事务隔离级别是Repeatable Read。

索引

索引的作用是提高查询效率,那么如果数据数据结构的话,就会知道,能够提高查询效率的数据结构有很多,比如最简单的二叉树,复杂一点的红黑树、哈希表,还有B-Tree,那为什么mysql最终选择了B+Tree来实现索引结构呢?这个问题其实还挺有意思的。

我们都知道,目前在使用mysql的时候,一张表的数据量一般在500万~2000万之间,那么如何在这个数据量下面以最短的时间找到需要的数据呢?数据最终都是被存储在磁盘上的,那么读取数据的时候就会涉及到磁盘IO,磁盘IO可以说是计算机世界里最耗时的操作之一,cup的运算速度和磁盘IO速度做对比的话,可以用天上一天 地上一年来形容,所以,向提高数据的检索效率,就必须减少磁盘IO次数,而从磁盘读取数据的时候,都是以磁盘页为单位来存储,基于以上逻辑,我们来分析一下上面的几种数据结构是否适合实现mysql的索引结构。

首先说二叉树,二叉树的检索效率O(logn),看起来很快,但是二叉树有两个致命的问题,一个是在极端情况下会退化成链表,另外,在大数据量下,树的高度不可控,如果把一次节点搜索看做一次磁盘IO的话,那么当N很大时,磁盘IO次数太多,所以二叉树被pass了。

红黑树结构实际上和二叉树差不多,虽然它解决了退化成链表的问题,但是树的高度仍然不可控,同时调整树平衡的自旋过程也会造成一定的性能开销,所以红黑树也被pass了。

哈希表的检索效率理论上可以达到O(1),但是由于哈希表是基于hashcode来存储数据的,对顺序查询不友好,例如要进行一个范围查询,哈希表就只能做全表扫描,另外哈希表对于排序也不友好,而这些操作都是mysql的核心功能,不能因为要提高查询性能就把这些核心功能给删掉,所以哈希表也被pass了。

最后来看看B-Tree,B-Tree与B+Tree最大的区别在于,B-Tree中的节点处理粗出key值外,还会存储key对应值,在mysql中,一个节点对应了一个磁盘页,默认大小为16kb,如果把value值也放进去的话,就会造成一个节点能够存储的key的数量变少,造成树的高度增加,所以基于性能考虑,B-Tree最后也不合格。

那我们在来看看B+Tree,B+Tree由于在非叶子节点上只存储key值不存储value,所以一个节点(磁盘页)可以存储更多的key。同时mysql在原始B+Tree的基础上做了一些优化,叶子节点构造成了一个双向链表,很方便进行范围查找。

mysql中的索引主要分为两种,一种是聚簇所以,一种是非聚簇索引。聚簇索引也叫主键索引,非聚簇索引也叫二级索引,聚簇索引的叶节点存储了具体的行数据,而非聚簇索引的叶子节点存储了行的主键值,当通过非聚簇索引查询数据的时候,一般首先会找到行数据对应的主键值,然后再在聚簇索引里查询具体的行数据,这个过程叫做回表。

在我们常用的两种存储引擎InnoDB和MyIsam中,索引的存储结构有一点区别,在MyIsam的聚簇索引中,叶节点存储的不是具体的行数据,而是数据才磁盘上的物理地址。

一般在创建表的时候都需要制定主键字段,同时最好使用int类型的自增字段,那为什么呢?因为如果不指定主键的话,mysql内部也会为表生成主键值,目的是用来构建主键索引,因为数据表本身其实就是一个主键索引,如果不指定主键字段的话会影响一部分性能,另外,主键字段类型不建议使用其他类型,比如字符串,因为字符串占用的空间比较多,造成B+Tree中的节点能存储的key的数量变少,树的高度增加,增加查找数据是磁盘IO次数。

MySql中的日志

  • 慢查询日志;
  • bin log;
  • redo log;
  • undo log;
  • relay log;

慢查询日志记录执行时间比配置的时间长的慢sql,redo log是InnoDB存储引擎的日志,用来提高写数据的性能,bin log是server层的日志,用来进行数据同步和数据恢复,undo log主要用来实现事务的回滚,同时mvcc机制也是基于undo log来实现的,relay log用来实现数据同步,存在于slave节点上。

Sql设计优化

下面来总结一下sql语句的一些优化策略:

  • 最左前缀法则;
  • 不在索引列上左操作(包括函数、计算和类型转换),会导致索引失效;
  • 存储引擎不能使用索引中范围条件右边的列;
  • 尽量使用索引覆盖,避免select *;
  • 避免使用不等值查询,会导致索引失效(!=,<>);
  • 避免对null值进行判断,会导致索引失效(is null, is not null);
  • Like查询如果以通配符开头,会导致索引失效,可以用覆盖索引做优化,避免扫索引但不扫全表;
  • 字符串不加单引号,会导致类型转换,最终导致索引失效;
  • 少用 or 和 in,有可能会使索引失效;
  • 对于关联查询的sql,在关联字段是行加索引;
  • 对于in和exist语句的优化,In语句是 in 里面的select先执行,保证其为小标;
  • Count的优化,count(*) = count(1) > count(id) > count(field);

分库分表

读写分离

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值