数据库总结

数据库总结

  • 一、 数据库基础
    • 1.数据库范式
    • 2.视图
    • 3.存储过程 函数
    • 4.触发器
    • 5.物化视图
  • 二、事务
    • 1.ACID
    • 2.套嵌事务
    • 3.隔离级别
    • 4.并发事务
  • 三、索引
    • 1. 分类
    • 2.区别
      • 聚簇索引 非聚簇索引
      • 聚集索引 非聚集索引
      • B树索引 B+树索引
      • hash索引 B+索引
    • 3.哪些字段适合建立索引 不适合建索引
    • 联合索引数据结构
    • 最左前缀原则
  • 四、优化
    • 大表优化
  • 5、数据库引擎
    • mysql常见的三种存储引擎
      • 区别
    • InnoDB四大特性
  • 6、数据库锁
    • 行锁和表锁
    • 共享锁和排它锁
    • 死锁的判定原理和具体场景
    • 乐观锁悲观锁
  • 7、日志
  • 8、其他
    • 关系型数据库与非关系型数据库

一、 数据库基础

1.数据库范式

  • 第一范式
    • 解决原子性问题:数据库中每一列的元素都符合原子性要求都是不可分割的,是所有关系型数据库的最基本要求
  • 第二范式
    • 在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。
    • 主键保证数据的唯一性,通过主键可以快速定为到数据,主键自带主键索引 提高数据查询速度(此处不再解释)。
    • 一条数据有多个字段组成,主键的唯一性也保证了整条数据的唯一性,通过其他字段和主键的依赖关系可以更快的定位到数据
  • 第三范式
    • 在第三范式基础上,消除主属性对于候选码的传递依赖与部分依赖,不能部分依赖于候选码的或者传递依赖于候选码。
    • 比如订单表 (简化版)
      订单表
订单id 订单名 商品id 商品名 商品价格 用户id 用户名
订单名 商品id 商品名 商品价格 用户id 用户名 虽然都依赖订单 id 但是 商品名 商品价格是直接依赖于商品id 然后 商品id直接依赖于订单id用户名同理 通过拆分该表为多个表达到三范式

订单表

订单id 订单名 商品id 用户id

用户表:

用户id 用户名

商品表:

商品名 商品价格 用户id
  • 引申:
    • 主属性:候选码的诸属性称为主属性;
    • 非主属性:不包含在任何候选码中的属性称为非主属性;
    • 候选码:某一列或者某几列属性的组合,能够唯一标识一行数据。
  • 总结
    • 应用的范式等级越高,则表越多。表多会带来很多问题:
      • 查询时要连接多个表,增加了查询的复杂度
      • 查询时需要连接多个表,降低了数据库查询性能
    • 范式级别越高,数据冗余度越小,但在实际情况中并不是范式级别越高就越好,比如有些业务需要进行反范式设计,适当增加一些冗余字段,来减少数据库查询次数,来提高性能。
    • 第三范式已经很大程度上减少了数据冗余,出现插入异常,更新异常,和删除异常的概率也大大降低。大多数情况应用到第三范式已经足够

2.视图

  • 定义:

    • 1.视图,本质上是一种虚拟表,在物理上是不存在的,其内容与真实的表相似,包含一系列带有名称的列和行数据。
    • 2.视图并不在数据库中以储存的数据值形式存在。行和列数据来自定义视图的查询所引用基本表,并且在具体引用视图时动态生成。
  • 优点:

    • 查询简单化。
      • 视图能简化用户的操作
    • 数据安全性。
      • 视图使用户能以多种角度看待同一数据,能够对机密数据提供安全保护
      • 视图保证用户只能看到视图中所定义的数据,而不是视图所引用表中的数据,从而提高了数据库中数据的安全性。
      • 使用视图,可以定制用户数据,聚焦特定的数据。
      • 防止篡改或泄露其他数据而造成数据不安全。
      • 逻辑数据独立性。视图对重构数据库提供了一定程度的逻辑独立性
      • 可以合并分离的数据,创建分区视图。如在实际应用中,为方便操作管理,将所有的数据合并到一个表格中,使用union关键字,将分离的数据合并为一个视图。
  • 缺点

    • 性能
      • 数据库必须把视图的查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,即使是视图的一个简单查询,数据库也把它变成一个复杂的结合体,需要花费一定的时间。
    • 修改限制
      • 当用户试图修改视图的某些行时,数据库必须把它转化为对基本表的某些行的修改。事实上,当从视图中插入或者删除时,情况也是这样。对于简单视图来说,这是很方便的,但是,对于比较复杂的视图,可能是不可修改的。
  • 拓展 物化视图(oracle)

3.存储过程 函数

  • 定义
    • 存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。
    • 存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。
    • 存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。
  • 区别
    • 函数有返回值且只返回一个,可以返回一个表对象,存储过程可以不返回值,也可以返回多个值
    • 函数注重返回的值,实现的功能针对性较强。而存储过程注重执行过程,功能要强大,可以实现更复杂的逻辑如修改表等操作
    • 存储过程需要单独调用,函数可以嵌套在查询语句中使用。
    • 存储过程的参数可以有IN,OUT,INOUT三种类型,而函数只能有IN类
    • 存储过程声明时不需要返回类型,而函数声明时需要描述返回类型,且函数体中必须包含一个有效的RETURN语句
    • 存储过程可以使用非确定函数,不允许在用户定义函数主体中内置非确定函数
    • 存储过程只在创建的时候进行编译,以后每次执行存储过程都不需要重新编译
    • SQL语句中不可用存储过程,而可以使用函数。
    • mysql 有很多内置函数可以直接使用,而存储过程均需要我们自己编写
  • 相同点
    • 存储过程和函数都是事先经过编译并存储在数据库中的一段SQL语句集合
    • 存储过程和函数执行不是由程序调用,也不是手动启动,而是由事件触发、激活从而实现执行的
  • 参考文献

4.触发器

  • 定义
    • 触发器是一种与表操作相关的数据库对象,也是一种特殊的存储过程。当触发器所在表上出现指定事件时就调用这个对象,执行触发器中定义的SQL语句集合。
    • 目前MySQL只支持行触发器不支持语句级触发器,Oracle数据库支持语句级触发器。
  • 应用
    • 可以通过触发器对数据库中的相关表实现级联更改
    • 日志记录
    • 数据校验 防止恶意或者错误的insert、update和delete操作,并强制执行check约束定义的限制更为复杂的其他限制。
    • 评估数据修改前后表的状态,并根据该差异才去措施
  • 特点
    • 触发事件的操作和触发器里的SQL语句是一个事务操作,具有原子性
    • 触发器定义在表上,附着在表上
    • 语句级触发 消耗资源 浪费时间
  • 类型
    • DML(数据操作语言,Data Manipulation Language)触发器
      • a i d 时触发
      • 附加在特定表或视图上
      • 当数据库服务器中发生数据操作语言事件时触发这类触发器
      • a. Before Insert
      • b. After Insert
      • c. Before Update
      • d. After Update
      • e. Before Delete
      • f. After Delete
    • DDL(数据定义语言,Data Definition Language)触发器
      • DDL触发器是当服务器或者数据库中发生数据定义语言
      • 主要是以create,drop,alter开头的语句 事件时被激活使用
      • 使用DDL触发器可以防止对数据架构进行的某些更改或记录数据中的更改或事件操作。
    • 登录触发器
  • 总结
    • MySQL触发器始终是基于表中的一条记录触发,而不是一组SQL语句。因此,如果需要变动整个数据集而数据集数据量又较大时,触发器效果会非常低。
    • 每一个表的一个事件只能定义一个触发器
    • 一个MySQL触发器可能会关联到另外一张表或几张表的操作。因此,会导致数据库服务器负荷也会相应的增加一倍或几倍,如果出现因为触发器问题导致的性能问题,会很难定位问题位置和原因。
    • 在基于锁的操作中,触发器可能会导致锁等待或死锁。触发器执行失败,原来执行的SQL语名也会执行失败。而因为触发器导致的失败结果和失败原因,往往很难排查。

5.物化视图

  • 物化视图是查询结果的预运算,不同于简单的视图,物化视图的结果一般存储于表中。
  • 物化视图用于需要对查询立即做出响应,而又需要耗费很长时间获得结果。
  • 物化视图必须能快速更新。它取决于对更新频率和内容的准确性的要求。一般说来物化视图能够在一定时间内及时更新。
  • 可以通过存储过程和mysql的触发器去进行更新。
  • 物化视图等同于快照,远程数据库的本地副本,数据库中数据的汇总表,副本状态为只读
  • 该视图不是基于基表的虚表,而且根据基表实际存在的实表,即物化视图的数据存储在非易失的存储设备上。
  • 物化视图可以用在预先计算并保存多表的链接等耗时较多的SQL操作结果。
  • MySQL数据库本身不支持物化视图,mysql数据库中的视图总是虚拟的。需要借助触发器实现物化视图。

二、事务

1.ACID

  • 原子性:事务的操作不可分割,要么全部成功,失败则全部失败(使用回滚日志)

    • 利用Innodb的undo log回滚日志,当事务回滚时能够撤销所有已经成功执行的sql语句。
    • 当事务对数据库进行修改时,InnoDB会生成对应的undo log;
    • 如果事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。
    • undo log属于逻辑日志,它记录的是sql执行相关的信息。当发生回滚时,InnoDB会根据undo log的内容做与之前相反的工作
      • 当delete一条数据的时候,就需要记录这条数据的信息,回滚的时候,insert这条旧数据
      • 当update一条数据的时候,就需要记录之前的旧值,回滚的时候,根据旧值执行update操作
      • 当insert一条数据的时候,就需要这条记录的主键,回滚的时候,根据主键执行delete操作
  • 一致性:所有事务对一个数据的读取结果相同

    • 数据库层面:在并发的场景下,事务的一致性要基于原子性和隔离性,以及持久性才能实现,没有并发场景的话没有隔离性也能实现一致性。前面的三个特性的最终目的就是为了保证数据的一致性。
    • 应用层面:如果代码逻辑出现问题,一致性就会被破坏,比如A给B转账50,只给B的账户+50,却没有扣除A账户50,那么数据就不一致了。所以代码逻辑正确是保证一致性的前提。
  • 隔离性:一个事务所做的修改的在最终提交前,别的事务不可见

    • MVCC机制:即多版本并发控制,一个行记录数据有多个版本的快照数据,每个版本的数据行后面都额外增加了两个隐藏列,一个记录创建版本号,一个记录删除版本号。MVCC使用的快照数据是存储在Undo log日志中的,该日志通过回滚指针把一个数据行的所有快照连接起来。用来实现读提交和可重复读隔离级别。
    • Innodb存储引擎,支持表锁,行锁,和间隙锁,在update,insert,delete时都会加上锁,而读取操作要读取的行如果被锁,就要回溯读取该行的快照,MVCC根据隔离级别和快照隐藏列来决定快照数据是否可见。
  • 持久性:事务对数据的修改会永远保存在数据库中

    • MySQL提供了buffer pool缓存机制,需要定期“刷脏”,即修改过的缓存数据回写到数据库,保证一致性。
    • 当数据库宕机时,如果缓存未写回,就保证不了一致性。此时需要利用redo log将数据恢复到数据库。
    • 每次修改都先记录到redo log,然后再写到缓存,这叫做WAL,预写式日志。保证数据不会因MySQL宕机而消失(宕机后缓存也不见了)
    • 当数据修改的时候,不仅在内存中操作,还会在redo log中记录操作。当事务提交时,会将redo log进行刷盘(两阶段提交,redo log 一部分在内存,一部分在磁盘)。
    • 当数据库宕机重启的时候,会将redo log 的内容恢复到数据库,再根据binlog 和 undo log决定是回滚还是提交数据。
    • Redo log执行速度比刷脏要快,因为
      • redo log是物理日志,体积小,只记录哪一页修改了什么。
      • 刷脏是随机IO,因为每次修改的数据位置随机,但写redo log是追加操作,属于顺序IO。
      • 刷脏是以数据页(Page)为单位的,MySQL默认页大小是16KB,一个Page上一个小修改都要整页写入;而redo log中只包含真正需要写入的部分,无效IO大大减少。

2.套嵌事务

  • 嵌套事务是外部事务的一个子事务,是一个外部事务的组成部分,当嵌套事务发生异常,嵌套事务回滚,外部事务不会滚,外部事务发生异常回滚时,嵌套事务也要回滚。
  • Spring的PROPAGATION_NESTED传播机制就是如果当前有事务,则创建一个事务做为当前事务的一个嵌套事务,否则创建一个新事务。

3.隔离级别

  • InnoDB默认是可重复读的(REPEATABLE READ)使用行锁+间隙锁,防止幻读发生
  • 未提交读(READ UNCOMMITTED)
    • 事务中的修改,即使没有提交,对其它事务也是可见的。
  • 提交读(READ COMMITTED)
    • 一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。
  • 可重复读(REPEATABLE READ)
    • 保证在同一个事务中多次读取同样数据的结果是一样的。
  • 可串行化(SERIALIZABLE)
    • 强制事务串行执行。
隔离级 别脏读可能性 不可重复读可能性 幻影读可能性
未提交读
提交读 ×
可重复读 × ×
可串行化 × × ×
  • 丢失修改
    • T1 和 T2 两个事务都对一个数据进行修改,T1 先修改,T2 随后修改,T2 的修改覆盖了 T1 的修改。
  • 读脏数据
    • T1 修改一个数据,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。
  • 不可重复读
    • T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。
  • 幻影读
    • T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。
  • 总结
    • 不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
      参考文献

4.并发事务

  • 脏读(Dirty read)
    当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
  • 丢失修改(Lost to modify)
    指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
  • 不可重复读(Unrepeatableread)
    指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
  • 幻读(Phantom read)
    幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

三、索引

1. 分类

  • 物理方式
    • 聚簇索引
    • 非聚簇索引
  • 逻辑方式(功能角度)
    • 普通索引 : 可以直接建立
    • 唯一索引 :唯一索引在普通索引的前提上 需要建立索引的列的值唯一
    • 主键索引 :主键索引需要在唯一索引的前提上要求建立索引的列不为空
    • 全文索引 :全文索引使用场景不多,目前使用最多在全文的搜素引擎上面。
  • 字段个数
    • 单一索引
    • 联合索引 :联合索引是指拥有两个及两个以上列作为索引。在使用的时候需要注意最左匹配原则;
  • 数据结构
    • hash索引 :适用于单次查询,查询时间复杂度为1.
    • B树索引
    • B+树索引 :适用于范围查询,查询时间复杂度logn。

2.区别

聚簇索引 非聚簇索引

  • 聚簇索引的每个节点不仅保存索引同时还保存数据,所以只要找到索引后就可以直接获得元素,而非聚簇索引可以简单的理解为指针,每个节点保存索引的同时保存的是指向真正数据的索引,再通过真正数据的索引找到磁盘中的数据,所以需要两次查找。
  • 聚簇索引 指表中的数据行按照索引排序的方式进行排列 所以每张表只有一个聚簇索引。非聚簇索引只维护单独的索引表(只维护索引表不维护索引索引指向的数据),所以每张表可以有多个非聚簇索引。

聚集索引 非聚集索引

  • 相同点:都是使用的b+树,非叶子节点存key,叶子节点存key+value
  • 非聚集索引逻辑位置和物理位置是不一致的,数据的存储是无序的,同时它的叶子节点存储的指向数据记录的指针,因此找到节点数据后需要进行回表查询(进行聚簇索引),找到具体的数据记录
  • 聚集索引逻辑位置和物理位置是一致的,索引的顺序就是数据存储的顺序,同时它的叶子节点是直接存储数据的,因此找到节点就找到实际数据,不需要回表查询。每个表只有主键会使用聚簇索引,因此每个表只存在一个聚簇索引
  • 聚集索引:InnoDB存储引擎表是索引表,即表中数据按照主键顺序存放。而聚集索引就是按照每张表的主键构建一颗B+树,同时叶子结点存放的即为整张表的行记录数据,也将聚集索引的叶子结点称为数据页。聚集索引的这个特性决定了索引组织表中的数据也是索引一部分。数据页上存放完整的每行记录,而非数据页的索引页中,存放的仅仅是键值及指向数据页的偏移量。许多文档告诉读者,聚集索引按照顺序物理地址存储数据。但是聚集索引如果必须按照物理地址连续存储,维护成本非常之高。所以聚集索引的存储不是物理上连续的,而且逻辑上连续的。
  • 聚集索引的主键排序查找和范围查找速度非常快,叶子结点数据就是用户所要查询的数据。
  • 非聚集索引(辅助索引):叶子节点并不包含行记录的全部数据。叶子节点除了包含键值以外,每个叶子节点中的索引行中还包含了一个书签,书签告诉InnoDB存储引擎那里找到相应的行数据。但不一定都要回表查询,这涉及到查询语句所要求的字段是否全部命中了索引,如果全部命中了索引,那么就不必再进行回表查询。
    • 举个简单的例子,假设我们在员工表的年龄上建立了索引,那么当进行select age from employee where age < 20的查询时,在索引的叶子节点上,已经包含了age信息,不会再次进行回表查询。

数据库总结_第1张图片

B树索引 B+树索引

  • B树只适合随机检索。而B+树同时支持随机检索和顺序检索;
  • B+树空间利用率更高。可减少I/O次数,磁盘读写代价更低。一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗。B+树的内部结点并没有指向关键字具体信息的指针,只是作为索引使用,节点能容纳的关键字数量更多,一次性读入内存中可以查找的关键字也就越多,相对的,IO读写次数也就降低了。而IO读写次数是影响索引检索效率的最大因素;
  • B+树的查询效率更加稳定。B树搜索有可能会在非叶子结点结束,越靠近根节点的记录查找时间越短,只要找到关键字即可确定记录的存在,其性能等价于在关键字全集内做一次二分查找。而在B+树中,顺序检索比较明显,随机检索时,任何关键字的查找都必须走一条从根节点到叶节点的路,所有关键字的查找路径长度相同,导致每一个关键字的查询效率相当。
  • B树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。B+树的叶子节点使用指针顺序连接在一起,只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作。
  • B+树增删文件(节点)时效率更高。因为B+树的叶子节点包含所有关键字,并以有序的链表结构存储,这样可很好提高增删效率。
  • B树中,可以将键和值存放在内部节点和叶子节点;但在B+树中,内部节点都是键,没有值,叶子节点同时存放键和值。
  • B+树的叶子节点有一条链相连,而B树的叶子节点各自独立。
  • 范围查找B+树效率更高。B树查找范围需要进行中序遍历,而B+树只需要遍历叶子节点就可以解决对全部关键字信息的扫描,因为叶子节点之间是由双向链表连接的,所以对于范围查询,B+树性能更高

hash索引 B+索引

  • hash索引进行等值查询更快(一般情况下),但是却无法进行范围查询。
  • IO次数比B+树少,不需要从根节点读到叶子结点
  • hash索引对于联合索引是对查询字段进行联合hash计算,导致无法使用最左原则. B+树索引可以使用最左的原则
  • hash索引不支持使用索引进行排序
  • 索引的顺序与原顺序无法保持一致,不能支持范围查询
  • hash索引不支持模糊查询也不支持order by排序

3.哪些字段适合建立索引 不适合建索引

  • 适合建索引
    • 表的主键,外键必须有索引
    • 在经常需要搜索的列上,可以加快搜索的速度
    • 在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构
    • 在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度
    • 在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的
    • 在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间
    • 在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度
    • 排序、分组、统计字段可以建立索引
    • 索引应该建立在选择性高(关键字重复率底)的字段上
  • 不适合建索引
    • 对于那些在查询中很少使用或者参考的列不适合创建索引
    • 频繁更新的字段不适合建立索引
    • 只有很少数据值的列不适合增加索引
    • 定义为text, image和bit数据类型的列不适合建索引
      这些列的数据量要么相当大,要么取值很少。
    • 修改性能远远大于检索性能时,不适合创建索引
      修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能
    • 数据重复且发布比较均匀的的字段不适合建索引(唯一性太差的字段不适合建立索引),例如性别,真假值
    • 参与列计算的列不适合建索引

联合索引数据结构

联合索引根据最左匹配原则,根据每一列的大小进行排序 可以简单理解为order by a,b,c。
从联合索引的底层数据结构可以更加容易理解最左匹配原则
对于不同的数据库引擎,叶子节点保存的内容也不相同。非叶子节点保存的是联合索引key 的键值对,并且按照一定的顺序在叶节点间排队,对于innodb,叶子节点存储的是联合索引key键值对+主键索引;对于MyIsam叶子节点保存的是联合索引key键值对+data内存地址。

最左前缀原则

  • 在mysql建立联合索引时会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配,explain会纠正sql语句该以什么样的顺序执行效率最高,最后才生成真正的执行计划。
  • 假如现在有一个联合索abc,那么他的索引规则就是先对a先进行排序,然后在a排序的基础上再对b排序,然后再对c进行排序,所以只有a是绝对有序的,其他的都是乱序的,因此我们需要先使用索引a才会起到索引快速查询的作用,如果只是单纯的只使用b或c,是起不到快速查询的作用。建立索引的时候会将查询频繁的列放在where 条件最左边。部分条件进行查询的时候会走部分索引。根据物理结构模式来判断是进行全表扫描还是走索引。如果是不匹配最左原则的直接进去全表扫描。

四、优化

大表优化

  • 可以做表拆分,减少单表字段数量,优化表结构。主要两种拆分 垂直拆分,水平拆分。

  • 在保证主键有效的情况下,检查主键索引的字段顺序,使得查询语句中条件的字段顺序和主键索引的字段顺序保持一致。

  • 分库分表

    • 事务支持 分库分表后,就成了分布式事务了。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价; 如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担。
  • 跨库join
    只要是进行切分,跨节点Join的问题是不可避免的。但是良好的设计和切分却可以减少此类情况的发生。解决这一问题的普遍做法是分两次查询实现。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求得到关联数据。

    • 分库分表方案产品跨节点的count,order by,group by以及聚合函数问题。
      这些是一类问题,因为它们都需要基于全部数据集合进行计算。多数的代理都不会自动处理合并工作。
      • 解决方案:与解决跨节点join问题的类似,分别在各个节点上得到结果后在应用程 序端进行合并。和join不同的是每个结点的查询可以并行执行,因此很多时候它的速度要比单一大表快很多。但如果结果集很大,对应用程序内存的消耗是一个问题。
    • ID问题
      一旦数据库被切分到多个物理结点上,我们将不能再依赖数据库自身的主键生成机制。一方面,某个分区数据库自生成的ID无法保证在全局上是唯一的;另一方面,应用程序在插入数据之前需要先获得ID,以便进行SQL路由.
      • 一些常见的主键生成策略
        UUID 使用UUID作主键是最简单的方案,但是缺点也是非常明显的。由于UUID非常的长,除占用大量存储空间外,最主要的问题是在索引上,在建立索引和基于索引进行查询时都存在性能问题。

    (优化问题自行补充)

5、数据库引擎

mysql常见的三种存储引擎

区别

  • InnoDB:支持事务,支持外键,支持崩溃修复能力和并发控制,表数据和索引数据是放在一起的,支持行级锁。

  • InnoDB是聚集索引,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此主键不应该过大,因为主键太大,其他索引也都会很大。而MyISAM是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。InnoDB中,只有主键索引是聚簇索引,如果没有主键,则挑选一个唯一键建立聚簇索引。

  • InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快。

  • Innodb不支持全文索引,而MyISAM支持全文索引,查询效率上MyISAM要高。5.6.4以后版本开始支持FULLTEXT类型的索引

  • MyISAM:表数据和索引数据是分开存储的,不支持事务,不支持行级锁,并发性能低,存储空间占用比较小。

  • MyISAM设计简单,数据以紧密格式存储。对于只读数据,或者表比较小、可以容忍修复操作。

  • MyISAM 可以手工或者自动执行检查和修复操作,但是事务恢复以及崩溃恢复可能导致一些数据丢失,而且修复操作时非常慢的。

  • MEMORY:所有的数据都在内存中,数据处理速度很快,但安全性不高,关机或重启数据都会消失,默认使用Hash索引。

  • 系统奔溃后,MyISAM恢复起来更困难

InnoDB四大特性

  • 插入缓冲
    只对于非聚集索引(非唯一)的插入和更新有效,对于每一次的插入不是写到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,如果在则直接插入;若不在,则先放到Insert Buffer 中,再按照一定的频率进行合并操作,再写回disk。这样通常能将多个插入合并到一个操作中,目的还是为了减少随机IO带来性能损耗。

  • 二次写
    在应用(apply)重做日志前,用户需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,再进行重做,这就是double write
    double write缓存是位于系统空间表的存储区域,用来缓存InnoDB的数据页(从buffer pool中flush之后并写入到数据库之前),所以当操作系统或者数据库进程在数据页写磁盘的过程中崩溃,Innodb可以在double write缓存中找到数据页的备份而用来执行crash恢复。

  • 自适应哈希
    Innodb存储引擎会监控对表上二级索引的查找,如果发现某二级索引被频繁访问,二级索引成为热数据,建立哈希索引可以带来速度的提升

  • 预读
    InnoDB使用两种预读算法来提高I/O性能:线性预读(linear read-ahead)和随机预读(randomread-ahead)线性预读以区为单位,而随机预读以区中的页为单位,线性预读着眼于将下一个区提前读入buffer pool中,而随机预读着眼于将当前区的剩余页提前读入buffer pool。

6、数据库锁

  • 按锁的粒度划分,可分为表级锁、行级锁、页级锁
  • 按锁级别划分,可分为共享锁、排他锁
  • 按加锁方式,可分为自动锁、显式锁
  • 按操作划分,可分为DML锁,DDL锁
  • 按使用方式划分,可分为乐观锁,悲观锁
  • l 排它锁(Exclusive),简写为 X 锁,又称写锁。
  • l 共享锁(Shared),简写为 S 锁,又称读锁。
    有以下两个规定:
    • l 一个事务对数据对象 A 加了 X 锁,就可以对 A 进行读取和更新。加锁期间其它事务不能对 A 加任何锁。
    • l 一个事务对数据对象 A 加了 S 锁,可以对 A 进行读取操作,但是不能进行更新操作。加锁期间其它事务能对 A 加 S 锁,但是不能加 X 锁。

行锁和表锁

  • 表锁特点
    • 开销小,加锁快
    • 不会出现死锁
    • 粒度最大,发生锁冲突的概率最高
    • 并发性最低
  • 行锁特点
    • 开销大,加锁慢
    • 出现死锁机率较大
    • 粒度最小,发生锁冲突的概率最小
    • 并发性最高

共享锁和排它锁

  • 共享锁:允许事务读取一行,事务t1获取了r行的共享锁,其他事务t也可以获取r行的共享锁,阻止排他锁
  • 排他锁:允许事务删除或更新一行数据,如果事务t1获取了r行的排他锁那么,其他事务t就不能获取r行的共享锁和排它锁

死锁的判定原理和具体场景

数据库是一个多用户使用的共享资源,当多个用户 并发地存取数据的时候,在数据库中就会发生多个事务同时存取同一个数据的情况,加锁是进行数据库并发控制的一种非常重要的技术。在实际应用中,如果两个事务需要一组有冲突的锁,而不能继续进行下去,这时便发生了死锁。
例如:当前t1获取了r2的共享锁,t2获取了r1的排他锁,此时t1想要获取r1,t2想要获取r2,这就出现了相互等待资源的情况,即死锁。

  • 碰撞检测:
    MySQL error log中看到关于死锁的警告信息,或者在show engine InnoDB status
  • 等锁超时:
    当死锁出现时,Innodb会主动探知到死锁,并回滚了某一苦苦等待的事务。

乐观锁悲观锁

  • 悲观锁
    总是假设最坏的情况,每次去取数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。
  • 乐观锁
    总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中JUC中的大部分都是基于CAS实现的。
  • 两种锁的使用场景:
    从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

7、日志

  • 错误日志:对MySql的启动、运行、关闭过程进行记录,Mysql遇到问题时应先查看该日志定位问题。Show variables like “log_error”
  • 慢查询日志:帮助DBA定位有问题SQL语句,从SQL语句层面优化,在mysql设阈值,将运行时间超过该值的所有sql语句记录到慢日志文件中long_query_time来设置。
  • 二进制日志:记录对Mysql数据库执行更新的所有操作,但不包括select、show。
    查询日志:和慢查询日志相同
  • undo redo binlog
    • redo log 是重做日志,提供 前滚 操作;
    • undo log 是回退日志,提供 回滚 操作。
    • 只用 undo log 实现原子性和持久性的缺陷:
  • redo/undo log 和 binlog区别
    • 层次不同。redo/undo 是 innodb 引擎层维护的,而 binlog 是 mysql server 层维护的,跟采用何种引擎没有关系,记录的是所有引擎的更新操作的日志记录。
    • 记录内容不同。redo/undo 记录的是 每个页/每个数据 的修改情况,属于物理日志+逻辑日志结合的方式(redo log 是物理日志,undo log 是逻辑日志)。binlog 记录的都是事务操作内容,binlog 有三种模式:Statement(基于 SQL 语句的复制)、Row(基于行的复制) 以及 Mixed(混合模式)。不管采用的是什么模式,当然格式是二进制的,
    • 记录时机不同。redo/undo 在 事务执行过程中 会不断的写入,而 binlog 是在 事务最终提交前 写入的。binlog 什么时候刷新到磁盘跟参数 sync_binlog 相关。

8、其他

关系型数据库与非关系型数据库

  • 关系型数据库
    • SQL lite
    • Oracle
    • Mysql
  • 非关系型数据库
    • MongoDb
    • Redis
    • HBase
  • 关系型数据库特性优缺点
    • 优点
      • 容易理解:二维表结构是非常贴近逻辑世界一个概念,关系模型相对网状、层次等其他模型来说更容易理解;
      • 使用方便:通用的SQL语言使得操作关系型数据库非常方便;
      • 易于维护:丰富的完整性(实体完整性、参照完整性和用户定义的完整性)大大减低了数据冗余和数据不一致的概率;
      • 支持SQL,可用于复杂的查询。
    • 缺点
      • 为了维护一致性所付出的巨大代价就是其读写性能比较差;
      • 固定的表结构;
      • 高并发读写需求;
      • 海量数据的高效率读写;
    • 特性
      • 关系型数据库,是指采用了关系模型来组织数据的数据库;
      • 关系型数据库的最大特点就是事务的一致性;
      • 简单来说,关系模型指的就是二维表格模型,而一个关系型数据库就是由二维表及其之间的联系所组成的一个数据组织。
  • 非关系型数据库特性优缺点
    • 优点
      • 无需经过sql层的解析,读写性能很高
      • 基于键值对,数据没有耦合性,容易扩展;
    • 缺点
      • 不提供sql支持,学习和使用成本较高
      • 无事务处理,附加功能bi和报表等支持也不好;
    • 特性
      • 使用键值对存储数据;
      • 分布式;
      • 一般不支持ACID特性
      • 非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合。
  • 关系型数据库以mysql为代表,非关系型数据库以redis为代表,关系型数据库由于他的方便理解以及使用被广泛的传播,同时关系型数据库将数据存储磁盘 所以数据的读写需要频繁的磁盘io,在高并发读写场景下成为系统的瓶颈。 关系型数据库将数据存储在内存中无需经过sql的解析,读写性能较高,基于键值对的特性使得NOSQL便于拓展 ,在目前网络中往往两者搭配使用,将频繁使用的数据放入缓存redis中提前将数据预热 减少数据库压力。

如果不对的地方麻烦指出谢谢

你可能感兴趣的:(Java初学者,Mysql知识整理,面试题集)