非关系型数据库和关系型数据库区别,优势比较
非关系型数据库(感觉翻译不是很准确)称为 NoSQL
,也就是 Not Only SQL,不仅仅是 SQL。非关系型数据库不需要写一些复杂的 SQL 语句,其内部存储方式是以 key-value
的形式存在可以把它想象成电话本的形式,每个人名(key)对应电话(value)。常见的非关系型数据库主要有 Hbase、Redis、MongoDB 等。非关系型数据库不需要经过 SQL 的重重解析,所以性能很高;非关系型数据库的可扩展性比较强,数据之间没有耦合性,遇见需要新加字段的需求,就直接增加一个 key-value 键值对即可。
关系型数据库以表格
的形式存在,以行和列
的形式存取数据,关系型数据库这一系列的行和列被称为表,无数张表组成了数据库
,常见的关系型数据库有 Oracle、DB2、Microsoft SQL Server、MySQL等。关系型数据库能够支持复杂的 SQL 查询,能够体现出数据之间、表之间的关联关系;关系型数据库也支持事务,便于提交或者回滚。
它们之间的劣势都是基于对方的优势来满足的。
MySQL 事务四大特性
一说到 MySQL 事务,你肯定能想起来四大特性:原子性
、一致性
、隔离性
、持久性
,下面再对这事务的四大特性做一个描述
原子性(Atomicity)
: 原子性指的就是 MySQL 中的包含事务的操作要么全部成功
、要么全部失败回滚
,因此事务的操作如果成功就必须要全部应用到数据库,如果操作失败则不能对数据库有任何影响。
“这里涉及到一个概念,什么是 MySQL 中的事务?
事务是一组操作,组成这组操作的各个单元,要不全都成功要不全都失败,这个特性就是事务。
在 MySQL 中,事务是在引擎层实现的,只有使用
innodb
引擎的数据库或表才支持事务。
一致性(Consistency)
:一致性指的是一个事务在执行前后其状态一致。比如 A 和 B 加起来的钱一共是 1000 元,那么不管 A 和 B 之间如何转账,转多少次,事务结束后两个用户的钱加起来还得是 1000,这就是事务的一致性。持久性(Durability)
: 持久性指的是一旦事务提交,那么发生的改变就是永久性的,即使数据库遇到特殊情况比如故障的时候也不会产生干扰。隔离性(Isolation)
:隔离性需要重点说一下,当多个事务同时进行时,就有可能出现脏读(dirty read)
、不可重复读(non-repeatable read)
、幻读(phantom read)
的情况,为了解决这些并发问题,提出了隔离性的概念。
“脏读:事务 A 读取了事务 B 更新后的数据,但是事务 B 没有提交,然后事务 B 执行回滚操作,那么事务 A 读到的数据就是脏数据
不可重复读:事务 A 进行多次读取操作,事务 B 在事务 A 多次读取的过程中执行更新操作并提交,提交后事务 A 读到的数据不一致。
幻读:事务 A 将数据库中所有学生的成绩由 A -> B,此时事务 B 手动插入了一条成绩为 A 的记录,在事务 A 更改完毕后,发现还有一条记录没有修改,那么这种情况就叫做出现了幻读。
SQL的隔离级别有四种,它们分别是读未提交(read uncommitted)
、读已提交(read committed)
、可重复读(repetable read)
和 串行化(serializable)
。下面分别来解释一下。
读未提交:读未提交指的是一个事务在提交之前,它所做的修改就能够被其他事务所看到。
读已提交:读已提交指的是一个事务在提交之后,它所做的变更才能够让其他事务看到。
可重复读:可重复读指的是一个事务在执行的过程中,看到的数据是和启动时看到的数据是一致的。未提交的变更对其他事务不可见。
串行化:顾名思义是对于同一行记录,写
会加写锁
,读
会加读锁
。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
这四个隔离级别可以解决脏读、不可重复读、幻象读这三类问题。总结如下
其中隔离级别由低到高是:读未提交 < 读已提交 < 可重复读 < 串行化
隔离级别越高,越能够保证数据的完整性和一致性,但是对并发的性能影响越大。大多数数据库的默认级别是读已提交(Read committed)
,比如 Sql Server、Oracle ,但是 MySQL 的默认隔离级别是 可重复读(repeatable-read)
。
MySQL 常见存储引擎的区别
MySQL 常见的存储引擎,可以使用
SHOW ENGINES
命令,来列出所有的存储引擎
可以看到,InnoDB 是 MySQL 默认支持的存储引擎,支持事务、行级锁定和外键。
MyISAM 存储引擎的特点
在 5.1 版本之前,MyISAM 是 MySQL 的默认存储引擎,MyISAM 并发性比较差,使用的场景比较少,主要特点是
- 不支持
事务
操作,ACID 的特性也就不存在了,这一设计是为了性能和效率考虑的。 - 不支持
外键
操作,如果强行增加外键,MySQL 不会报错,只不过外键不起作用。 - MyISAM 默认的锁粒度是
表级锁
,所以并发性能比较差,加锁比较快,锁冲突比较少,不太容易发生死锁的情况。 - MyISAM 会在磁盘上存储三个文件,文件名和表名相同,扩展名分别是
.frm(存储表定义)
、.MYD(MYData,存储数据)
、MYI(MyIndex,存储索引)
。这里需要特别注意的是 MyISAM 只缓存索引文件
,并不缓存数据文件。 - MyISAM 支持的索引类型有
全局索引(Full-Text)
、B-Tree 索引
、R-Tree 索引
Full-Text 索引:它的出现是为了解决针对文本的模糊查询效率较低的问题。
B-Tree 索引:所有的索引节点都按照平衡树的数据结构来存储,所有的索引数据节点都在叶节点
R-Tree索引:它的存储方式和 B-Tree 索引有一些区别,主要设计用于存储空间和多维数据的字段做索引,目前的 MySQL 版本仅支持 geometry 类型的字段作索引,相对于 BTREE,RTREE 的优势在于范围查找。 - 数据库所在主机如果宕机,MyISAM 的数据文件容易损坏,而且难以恢复。
- 增删改查性能方面:SELECT 性能较高,适用于查询较多的情况
InnoDB 存储引擎的特点
自从 MySQL 5.1 之后,默认的存储引擎变成了 InnoDB 存储引擎,相对于 MyISAM,InnoDB 存储引擎有了较大的改变,它的主要特点是
- 支持事务操作,具有事务 ACID 隔离特性,默认的隔离级别是
可重复读(repetable-read)
、通过MVCC(并发版本控制)来实现的。能够解决脏读
和不可重复读
的问题。 - InnoDB 支持外键操作。
- InnoDB 默认的锁粒度
行级锁
,并发性能比较好,会发生死锁的情况。 - 和 MyISAM 一样的是,InnoDB 存储引擎也有
.frm文件存储表结构
定义,但是不同的是,InnoDB 的表数据与索引数据是存储在一起的,都位于 B+ 数的叶子节点上,而 MyISAM 的表数据和索引数据是分开的。 - InnoDB 有安全的日志文件,这个日志文件用于恢复因数据库崩溃或其他情况导致的数据丢失问题,保证数据的一致性。
- InnoDB 和 MyISAM 支持的索引类型相同,但具体实现因为文件结构的不同有很大差异。
- 增删改查性能方面,如果执行大量的增删改操作,推荐使用 InnoDB 存储引擎,它在删除操作时是对行删除,不会重建表。
MyISAM 和 InnoDB 存储引擎的对比
锁粒度方面
:由于锁粒度不同,InnoDB 比 MyISAM 支持更高的并发;InnoDB 的锁粒度为行锁、MyISAM 的锁粒度为表锁、行锁需要对每一行进行加锁,所以锁的开销更大,但是能解决脏读和不可重复读的问题,相对来说也更容易发生死锁可恢复性上
:由于 InnoDB 是有事务日志的,所以在产生由于数据库崩溃等条件后,可以根据日志文件进行恢复。而 MyISAM 则没有事务日志。查询性能上
:MyISAM 要优于 InnoDB,因为 InnoDB 在查询过程中,是需要维护数据缓存,而且查询过程是先定位到行所在的数据块,然后在从数据块中定位到要查找的行;而 MyISAM 可以直接定位到数据所在的内存地址,可以直接找到数据。表结构文件上
:MyISAM 的表结构文件包括:.frm(表结构定义),.MYI(索引),.MYD(数据);而 InnoDB 的表数据文件为:.ibd和.frm(表结构定义);
MySQL 基础架构
这道题应该从 MySQL 架构来理解,我们可以把 MySQL 拆解成几个零件,如下图所示
大致上来说,MySQL 可以分为 Server
层和 存储引擎
层。
Server 层包括连接器、查询缓存、分析器、优化器、执行器,包括大多数 MySQL 中的核心功能,所有跨存储引擎的功能也在这一层实现,包括 存储过程、触发器、视图等。
存储引擎层包括 MySQL 常见的存储引擎,包括 MyISAM、InnoDB 和 Memory 等,最常用的是 InnoDB,也是现在 MySQL 的默认存储引擎。存储引擎也可以在创建表的时候手动指定,比如下面
CREATE TABLE t (i INT) ENGINE = <Storage Engine>;
然后我们就可以探讨 MySQL 的执行过程了
连接器
首先需要在 MySQL 客户端登陆才能使用,所以需要一个连接器
来连接用户和 MySQL 数据库,我们一般是使用
mysql -u 用户名 -p 密码
来进行 MySQL 登陆,和服务端建立连接。在完成 TCP 握手
后,连接器会根据你输入的用户名和密码验证你的登录身份。如果用户名或者密码错误,MySQL 就会提示 Access denied for user,来结束执行。如果登录成功后,MySQL 会根据权限表中的记录来判定你的权限。
查询缓存
连接完成后,你就可以执行 SQL 语句了,这行逻辑就会来到第二步:查询缓存。
MySQL 在得到一个执行请求后,会首先去 查询缓存
中查找,是否执行过这条 SQL 语句,之前执行过的语句以及结果会以 key-value
对的形式,被直接放在内存中。key 是查询语句,value 是查询的结果。如果通过 key 能够查找到这条 SQL 语句,就直接返回 SQL 的执行结果。
如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果就会被放入查询缓存中。可以看到,如果查询命中缓存,MySQL 不需要执行后面的复杂操作,就可以直接返回结果,效率会很高。
但是查询缓存不建议使用
为什么呢?因为只要在 MySQL 中对某一张表执行了更新操作,那么所有的查询缓存就会失效,对于更新频繁的数据库来说,查询缓存的命中率很低。