mysql常用引擎包括:MYISAM、Innodb、Memory、MERGE
MYISAM:全表锁,拥有较高的执行速度,不支持事务,不支持外键,并发性能差,占用空间相对较小,对事务完整性没有要求,以select、insert为主的应用基本上可以使用这引擎。
Innodb:行级锁,提供了具有提交、回滚和崩溃回复能力的事务安全,支持自动增长列,支持外键约束,并发能力强,占用空间是MYISAM的2.5倍,处理效率相对会差一些。
Memory:全表锁,存储在内容中,速度快,但会占用和数据量成正比的内存空间且数据在mysql重启时会丢失,默认使用HASH索引,检索效率非常高,但不适用于精确查找,主要用于那些内容变化不频繁的代码表
MERGE:是一组MYISAM表的组合 。
InnorDB | MYISAM | INNODB |
---|---|---|
事务支持 | 不支持 | 支持 |
数据行锁定 | 不支持 | 支持 |
数据表锁定 | 支持 | 支持 |
外键约束 | 不支持 | 支持 |
全文索引 | 支持 | 不支持 |
表空间的大小 | 较小 | 较大,约为MAISAM的两倍 |
MyISAM:强调的是性能,每次查询具有原子性,其执行数度比InnoDB类型更快,但是不提供事务支持。
InnoDB:提供事务支持事务,外部键等高级数据库功能。 具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。
InnoDB支持行级锁,而MyISAM支持表级锁,用户在操作myisam表时,select,update,delete,insert语句都会给表自动加锁,如果加锁以后的表满足insert并发的情况下,可以在表的尾部插入新的数据。
InnoDB支持MVCC,而MyISAM不支持。
InnoDB支持外键,而MyISAM不支持。
MyISAM:允许没有任何索引和主键的表存在,索引都是保存行的地址。
InnoDB:如果没有设定主键或者非空唯一索引,就会自动生成一个6字节的主键(用户不可见),数据是主索引的一部分,附加索引保存的是主索引的值。
InnoDB不支持全文索引,而MyISAM支持。
MyISAM:数据是以文件的形式存储,所以在跨平台的数据转移中会很方便。在备份和恢复时可单独针对某个表进行操作。
InnoDB:免费的方案可以是拷贝数据文件、备份 binlog,或者用 mysqldump,在数据量达到几十G的时候就相对痛苦了。
MyISAM:每个MyISAM在磁盘上存储成三个文件。第一个文件的名字以表的名字开始,扩展名指出文件类型。.frm文件存储表定义。数据文件的扩展名为.MYD(MYData)。索引文件的扩展名是.MYI (MYIndex)。
InnoDB:所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB。
InnorDB支持MVCC,Multiversion Concurrency Control 间隙锁 。
MyISAM不支持MVCC。
MyIsam 存储引擎独立于操作系统,简单说就是可用在windows上使用,也可用将数据转移到Lunex操作系统上。系统兼容性很好。这种存储引擎在建表的时候,它会创建3个文件。分别是(.frm, .MYD, .MYI)。
简单说明一下:
.frm 存储表的定义(也就是表结构)
.MYD 就是表里面的数据
.MYI存储索引
这样的划分操作系统对大文件的操作是比较慢的,这样将表分为三个文件,那么.MYD这个文件单独来存放数据自然可以优化数据库的查询等操作。
特点:
1、不支持事务
2、不支持外键
3、查询速度很快。如果数据库insert和update的操作比较多的话采用表锁效率低(建议使用innodb)。
4、对表进行加锁
5、支持3种不同的存储方式,分别是:静态表、动态表、压缩表。
**静态表:**静态表的字段都是非变长类型的。优点是非常迅速,容易缓存,出现故障容易恢复;缺点是占用空间通常比动态表多(因为存储是会按照宽度定义补足空格)。
注意:在取数据时,会将数据后面的空格都去掉,如果数据后面本身有空格,那么也会被去掉。
**动态表:**记录不是固定长度的,这样的优点时空间占用少;缺点:频繁的更新,删除表易造成碎片,需要定期执行OPTIMIZE TABLE或者myisamchk-r命令来改善性能。
**压缩表:**因为每个记录都是单独压缩的,所以只有非常小的访问开支。
InnoDB是一个事务型的存储引擎,有行级锁定和外键约束,提供了具有提交,回滚和崩溃恢复的事务安全,但是对比MyLSAM引擎,写的效率会比差一些,并且会占用更多的磁盘空间以保存数据和索引。
特点:
1、更新多的表,适合处理多重并发的更新请求。
2、支持事务。
3、可以从灾难中恢复(通过bin-log日志等)。
4、外键约束。只有他支持外键。
5、支持自动增加列属性auto_increment。
**第一范式(1NF):**属性不可分割,即每个属性都是不可分割的原子项。
**第二范式(2NF):**满足第一范式;且不存在部分依赖,即非主属性必须完全依赖于主属性。(主属性即主键;完全依赖是针对于联合主键的情况,非主键列不能只依赖于主键的一部分)
**第三范式(3NF):**满足第二范式;且不存在传递依赖,即非主属性不能与非主属性之间有依赖关系,非主属性必须直接依赖于主属性,不能间接依赖主属性。(A -> B, B ->C, A -> C)
事务( transaction) 是作为一个单元的一组有序的数据库操作。如果组中的所有操作都成功, 则认为事务成功, 即使只有一个操作失败, 事务也不成功。如果所有操作完成, 事务则提交, 其修改将作用于所有其他数据库进程。如果一个操作失败, 则事务将回滚, 该事务所有操作的影响都将取消。事务回滚是指将该事务已经完成的对数据库的更新操作撤销。 数据库事务特性包括原子性(Atomic)、一致性(Consistency)、隔离性(Isolation)、持久性(Durabiliy)。简称ACID。
**原子性:**组成一个事务的多个数据库操作是一个不可分割的原子单元,只有所有操作都成功,整个事务才会提交。任何一个操作失败,已经执行的任何操作都必须撤销,让数据库返回初始状态。
**一致性:**事务操作成功后,数据库所处的状态和它的业务规则是一致的。即数据不会被破坏。如A转账100元给B,不管操作是否成功,A和B的账户总额是不变的。
**隔离性:**在并发数据操作时,不同的事务拥有各自的数据空间,它们的操作不会对彼此产生干扰。
**持久性:**一旦事务提交成功,事务中的所有操作都必须持久化到数据库中。
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。
脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。
不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
**Read Uncommitted(读取未提交内容——脏读:**在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。
**Read Committed(读取提交内容):**这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。
Repeatable Read(可重读): 这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读(PhantomRead)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control 间隙锁)机制解决了该问题。注:其实多版本只是解决不可重复读问题,而加上间隙锁(也就是它这里所谓的并发控制)才解决了幻读问题。
Serializable(可串行化): 这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
**主键索引:**索引列中的值必须是唯一的,不允许有空值。
**普通索引:**MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值。
**唯一索引:**索引列中的值必须是唯一的,但是允许为空值。
**全文索引:**只能在文本类型CHAR,VARCHAR,TEXT类型字段上创建全文索引。字段长度比较大时,如果创建普通索引,在进行like模糊查询时效率比较低,这时可以创建全文索引。MyISAM和InnoDB中都可以使用全文索引。
**空间索引:**MySQL在5.7之后的版本支持了空间索引,而且支持OpenGIS几何数据模型。MySQL在空间索引这方面遵循OpenGIS几何数据模型规则。
**前缀索引:**在文本类型如CHAR,VARCHAR,TEXT类列上创建索引时,可以指定索引列的长度,但是数值类型不能指定。
其他(按照索引列数量分类):
组合索引的使用,需要遵循最左前缀匹配原则(最左匹配原则)。一般情况下在条件允许的情况下使用组合索引替代多个单列索引使用。
数据库索引是数据库管理系统中一个排序的数据结构, 提高检索速度。索引的实现通常使用B树及其变种B+树 。创建唯一性索引, 保证数据库表中每一行数据的唯一性。加速表和表之间的连接。使用分组和排序子句进行数据检索时, 可以显著减少查询中分组和排序的时间。
创建索引和维护索引需要耗费时间, 这个时间随着数据量的增加而增加; 索引需要占用物理空间, 不光是表需要占用数据空间, 每个索引也需要占用物理空间; 当对表进行增、删、改的时候索引也要动态维护, 这样就降低了数据的维护速度。
1、在最频繁使用的、用以缩小查询范围的字段上建立索引。
2、在频繁使用的、需要排序的字段上建立索引。
3、对于查询中很少涉及的列或者重复值比较多的列, 不宜建立索引。
4、对于一些特殊的数据类型, 不宜建立索引, 比如文本字段( text) 等 。
1、经常增删改的列不要建立索引;
2、有大量重复的列不建立索引;
3、表记录太少不要建立索引。
MySQL 中有共享锁和排它锁,也就是读锁和写锁。
**共享锁:**不堵塞,多个用户可以同一时刻读取同一个资源,相互之间没有影响。
**排它锁:**一个写操作阻塞其他的读锁和写锁,这样可以只允许一个用户进行写入,防止其他用户读取正在写入的资源。
**表锁:**系统开销最小,会锁定整张表,MyISAM 使用表锁。
**行锁:**容易出现死锁,发生冲突概率低,并发高,InnoDB 支持行锁(必须有索引才能实现,否则会自动锁全表,那么就不是行锁了)。
说的是数据库被外界(包括本系统当前的其他事物以及来自外部系统的事务处理)修改保持着保守态度,因此在整个数据修改过程中,将数据处于锁状态。悲观锁的实现往往是依靠数据库提供的锁机制,也只有数据库层面提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统汇总实现了加锁机制,也是没有办法保证系统不会修改数据。
在悲观锁的情况下,为了保证事务的隔离性,就需要一致性锁定读。读取数据时给加锁,其它事务无法修改这些数据。修改删除数据时也要加锁,其它事务无法读取这些数据。
相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。
而乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本(Version)记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个“version”字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
MySQL 行锁只能加在索引上,如果操作不走索引,就会升级为表锁。因为 InnoDB 的行锁是加在索引上的,如果不走索引,自然就没法使用行锁了,原因是 InnoDB 是将 primary key index和相关的行数据共同放在 B+ 树的叶节点。InnoDB 一定会有一个 primary key,secondary index 查找的时候,也是通过找到对应的 primary,再找对应的数据行。
当非唯一索引上记录数超过一定数量时,行锁也会升级为表锁。测试发现当非唯一索引相同的内容不少于整个表记录的二分之一时会升级为表锁。因为当非唯一索引相同的内容达到整个记录的二分之一时,索引需要的性能比全文检索还要大,查询语句优化时会选择不走索引,造成索引失效,行锁自然就会升级为表锁。
1、设置获取锁的超时时间,至少能保证最差情况下,可以退出程序,不至于一直等待导致死锁;
2、设置按照同一顺序访问资源,类似于串行执行;
3、避免事务中的用户交叉;
4、保持事务简短并在一个批处理中;
5、使用低隔离级别;
6、使用绑定链接
多版本并发控制(MVCC=Multi-Version Concurrency Control),是一种用来解决读 - 写冲突的无锁并发控制。也就是为事务分配单向增长的时间戳,为每个修改保存一个版本。版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照(复制了一份数据)。这样在读操作不用阻塞写操作,写操作不用阻塞读操作的同时,避免了脏读和不可重复读。
MVCC 可以为数据库解决什么问题?
在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能。同时还可以解决脏读、幻读、不可重复读等事务隔离问题,但不能解决更新丢失问题。
MVCC 的目的就是多版本并发控制,在数据库中的实现,就是为了解决读写冲突,它的实现原理主要是依赖记录中的 3 个隐式字段、undo 日志、Read View 来实现的。
当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下:
1、限定数据的范围
务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内;
2、读写分离
经典的数据库拆分方案,主库负责写,从库负责读;
3、垂直分区
根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。
简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表
垂直拆分的优点: 可以使得列数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。
垂直拆分的缺点: 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行Join来解决。此外,垂直分区会让事务变得更加复杂;
4、水平分区
保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 水平拆分可以支撑非常大的数据量。
水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。举个例子:我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。
水平拆分可以支持非常大的数据量。需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL并发能力没有什么意义,所以 水平拆分最好分库 。
水平拆分能够 支持非常大的数据量存储,应用端改造也少,但 分片事务难以解决 ,跨节点Join性能较差,逻辑复杂。《Java工程师修炼之道》的作者推荐 尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度 ,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。
下面补充一下数据库分片的两种常见方案:
客户端代理: 分片逻辑在应用端,封装在jar包中,通过修改或者封装JDBC层来实现。
因为要是分成多个表之后,每个表都是从 1 开始累加,这样是不对的,我们需要一个全局唯一的 id来支持。
生成全局 id 有下面这几种方式:
**UUID:**不适合作为主键,因为太长了,并且无序不可读,查询效率低。比较适合用于生成唯一的名字的标示比如文件的名字。
**数据库自增 id:**两台数据库分别设置不同步长,生成不重复ID的策略来实现高可用。这种方式生成的 id 有序,但是需要独立部署数据库实例,成本高,还会有性能瓶颈。
**利用 redis 生成 id:**性能比较好,灵活方便,不依赖于数据库。但是,引入了新的组件造成系统更加复杂,可用性降低,编码更加复杂,增加了系统成本。
1、取得链接到 MySQL 中的连接器。
2、查询缓存,key 为 SQL 语句,value 为查询结果,如果查到就直接返回。不建议使用次缓存,在 MySQL 8.0 版本已经将查询缓存删除,也就是说 MySQL 8.0 版本后不存在此功能。
3、分析器,分为词法分析和语法分析。此阶段只是做一些 SQL 解析,语法校验。所以一般语法错误在此阶段。
4、优化器,是在表里有多个索引的时候,决定使用哪个索引;或者一个语句中存在多表关联的时候(join),决定各个表的连接顺序。
5、执行器,通过分析器让 SQL 知道你要干啥,通过优化器知道该怎么做,于是开始执行语句。执行语句的时候还要判断是否具备此权限,没有权限就直接返回提示没有权限的错误;有权限则打开表,根据表的引擎定义,去使用这个引擎提供的接口,获取这个表的第一行,判断 id 是都等于 1。如果是,直接返回;如果不是继续调用引擎接口去下一行,重复相同的判断,直到取到这个表的最后一行,最后返回。
Mysql内建的复制功能是构建大型,高性能应用程序的基础。将Mysql的数据分布到多个系统上去,这种分布的机制,是通过将Mysql的某一台主机的数据复制到其它主机(slaves)上,并重新执行一遍来实现的。 复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器。主服务器将更新写入二进制日志文件,并维护文件的一个索引以跟踪日志循环。这些日志可以记录发送到从服务器的更新。 当一个从服务器连接主服务器时,它通知主服务器在日志中读取的最后一次成功更新的位置。从服务器接收从那时起发生的任何更新,然后封锁并等待主服务器通知新的更新。
过程如下:
1、主服务器把更新记录到二进制日志文件中。
2、从服务器把主服务器的二进制日志拷贝到自己的中继日志(replay log)中。
3、从服务器重做中继日志中的事件,把更新应用到自己的数据库上。
基于语句的复制: 在主服务器上执行的SQL语句,在从服务器上执行同样的语句。MySQL默认采用基于语句的复制,效率比较高。 一旦发现没法精确复制时,会自动选着基于行的复制。
**基于行的复制:**把改变的内容复制过去,而不是把命令在从服务器上执行一遍. 从mysql5.0开始支持。
**混合类型的复制:**默认采用基于语句的复制,一旦发现基于语句的无法精确的复制时,就会
采用基于行的复制。
日志类别 | 说明 | 备注 |
---|---|---|
错误日志 | 错误日志记录了当MySQL启动、停止或者服务器运行过程中发生任何严重错误时的相关信息,当数据库出现任何故障导致无法正常使用时,可以首先查看此日志; | 默认开启 |
二进制日志(binlog日志) | 记录了所有的DDL和DML语句,但不包括数据查询语句,此日志对于灾难恢复时非常重要,并且MySQL的主从复制、增量恢复,也是通过该binlog 实现的。 | 默认未开启,需要手动开启 |
查询日志 | 它记录了客户端的所有操作语句,包括增删改查所有语句; | 默认未开启,需要手动开启,注意,高并发场景企业里普通查询日志一般是关闭的(默认也是关闭的),主要是因为IO性能问题; |
慢查询日志 | 它是用来记录查询效率较低的SQL语句的日志,慢查询日志记录所有执行时间超过参数long_query_time设置值,默认值为10s, | 默认未开启,需要手动开启 |
审计日志 | 根据国家等保审计合规要求,数据库要开启审计功能,它主要记录用户登录,对数据库的操作管理,对数据库受到的风险行为进行告警,对攻击行为及时阻断,通过审计日志可以对用户访问数据库行为进行记录、分析和汇报,用来帮助用户事后生成合规报告、事故追根溯源,同时加强内外部数据库网络行为记录,提高数据资产安全。 | MySQL企业版有此功能,属于收费组件,此测试社会版使用第三方开源插件。 |
redo log | 重做日志,提供前滚操作,通常是物理日志,记录的是数据页的物理修改,而不是某一行或者某几行修改成怎样,它用来恢复提交后的物理数据页,数据数据页,并且只能恢复到最后一次提交的位置; | undo log不是redo log的逆向过程,他们都是用来恢复的日志; |
undo log | 回滚日志,提供回滚操作,用来回滚到某个版本,undo log一般是逻辑日志,根据每行记录进行回滚; |
**内联接(Inner Join):**匹配2张表中相关联的记录。
**左外联接(Left Outer Join):**除了匹配2张表中相关联的记录外,还会匹配左表中剩余的记录,右表中未匹配到的字段用NULL表示。
**右外联接(Right Outer Join):**除了匹配2张表中相关联的记录外,还会匹配右表中剩余的记录,左表中未匹配到的字段用NULL表示。在判定左表和右表时,要根据表名出现在Outer Join的左右位置关系。
数据完整性(Data Integrity)是指数据的精确(Accuracy)和可靠性(Reliability)。
分为以下四类:
1、实体完整性: 规定表的每一行在表中是惟一的实体。
**2、域完整性:**是指表中的列必须满足某种特定的数据类型约束,其中约束又包括 取值范围、精度等规定。
3、参照完整性: 是指两个表的主关键字和外关键字的数据应一致,保证了表之间的数据的一致性,防止了数据丢失或无意义的数据在数据库中扩散。
**4、用户定义的完整性:**不同的关系数据库系统根据其应用环境的不同,往往还需要一些特殊的约束条件。用户定义的完整性即是针对某个特定关系数据库的约束条件, 它反映某一具体应用必须满足的语义要求。
**5、与表有关的约束:**包括列约束(NOT NULL( 非空约束))和表约束(PRIMARY KEY、foreign key、check、UNIQUE) 。