数据库面试题

基础知识
什么是SQL?
结构化查询语言(Structured Query Language)简称SQL,是一种数据库查询语言,用于存取数据、查询、更新和管理关系数据库系统。

数据库三大范式是什么
第一范式:每个列都不可以再拆分。
第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。
第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。
索引
什么是索引?
索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。

索引有哪些优缺点?
索引的优点

通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性
可以大大加快数据的检索速度,这也是创建索引的最主要的原因
可以加速表和表之间的连接
索引的缺点

时间方面:创建索引和维护索引要耗费时间,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,会降低增/改/删的执行效率
空间方面:索引需要占物理空间
索引有哪几种类型?
主键索引: 数据列不允许重复,不允许为NULL,一个表只能有一个主键。

唯一索引: 数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。

可以通过 ALTER TABLE table_name ADD UNIQUE (column); 创建唯一索引
可以通过 ALTER TABLE table_name ADD UNIQUE (column1,column2); 创建唯一组合索引
普通索引: 基本的索引类型,没有唯一性的限制,允许为NULL值。

可以通过ALTER TABLE table_name ADD INDEX index_name (column);创建普通索引
可以通过ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);创建组合索引
全文索引: 是目前搜索引擎使用的一种关键技术。

创建索引的原则
适合创建索引条件

主键自动建立唯一索引
频繁作为查询条件的字段应该建立索引
查询中与其他表关联的字段,外键关系建立索引
单键/组合索引的选择问题,组合索引性价比更高
查询中排序的字段,排序字段若通过索引去访问将大大提高排序效率
查询中统计或者分组字段
不适合创建索引条件

表记录少的
经常增删改的表或者字段
where条件里用不到的字段不创建索引
过滤性不好的不适合建索引
索引失效的场景
or、like ‘%xxx’、组合索引不满足最左匹配原则、使用函数、使用运算符操作、NOT IN

使用索引查询一定能提高查询的性能吗?
索引一般适用对数据的增删改操作不多的列
如果数据量太少, 索引不一定会比直接查询来的快
什么是前缀索引?
前缀索引也叫局部索引,比如给身份证的前 10 位添加索引,类似这种给某列部分信息添加索引的方式叫做前缀索引。
前缀索引能有效减小索引文件的大小,让每个索引页可以保存更多的索引值,从而提高了索引查询的速度。但前缀索引也有它的缺点,不能在 order by 或者 group by 中触发前缀索引,也不能把它们用于覆盖索引。
当字符串本身可能比较长,而且前几个字符就开始不相同,适合使用前缀索引;相反情况下不适合使用前缀索引
什么是最左前缀原则?什么是最左匹配原则
顾名思义,就是最左优先,在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。
最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式
B树和B+树的区别,数据库为什么使用B+树而不是B树
参考文档

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

使用B+树的好处
由于B+树的内部节点只存放键,不存放值,因此,一次读取,可以在内存页中获取更多的键,有利于更快地缩小查找范围。 B+树的叶节点由一条链相连,因此,当需要进行一次全数据遍历的时候,B+树只需要使用O(logN)时间找到最小的一个节点,然后通过链进行O(N)的顺序遍历即可。而B树则需要对树的每一层进行遍历,这会需要更多的内存置换次数,因此也就需要花费更多的时间

Hash索引和B+树所有有什么区别或者说优劣呢?
hash索引底层就是hash表,进行查找时,调用一次hash函数就可以获取到相应的键值,之后进行回表查询获得实际数据。B+树底层实现是多路平衡查找树。对于每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据。

那么可以看出他们有以下的不同:

hash索引进行等值查询更快(一般情况下),但是却无法进行范围查询。因为在hash索引中经过hash函数建立索引之后,索引的顺序与原顺序无法保持一致,不能支持范围查询。而B+树的的所有节点皆遵循(左节点小于父节点,右节点大于父节点,多叉树也类似),天然支持范围。
hash索引不支持使用索引进行排序,原理同上。hash索引不支持模糊查询以及多列索引的最左前缀匹配。原理也是因为hash函数的不可预测。AAAA和AAAAB的索引没有相关性。
hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询。
hash索引虽然在等值查询上较快,但是不稳定。性能不可预测,当某个键值存在大量重复的时候,发生hash碰撞,此时效率可能极差。而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节点,且树的高度较低。
因此,在大多数情况下,直接选择B+树索引可以获得稳定且较好的查询速度。而不需要使用hash索引。
什么是聚簇索引?何时使用聚簇索引与非聚簇索引
聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据
非聚簇索引:将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行,myisam通过key_buffer把索引先缓存到内存中,当需要访问数据时(通过索引访问数据),在内存中直接搜索索引,然后通过索引找到磁盘相应数据,这也就是为什么索引不在key buffer命中时,速度慢的原因
联合索引是什么?为什么需要注意联合索引中的顺序?
MySQL可以使用多个字段同时建立一个索引,叫做联合索引。在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否则无法命中索引。

事务
什么是数据库事务?
事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。

事物的四大特性(ACID)是什么?
原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
一致性: 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
什么是脏读?幻读?不可重复读?
脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
什么是事务的隔离级别?MySQL的默认隔离级别是什么?Oracle的默认隔离级别是什么?
READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
mysql Innodb在RR级别如何避免幻读
参考链接

理解innodb的锁(record,gap,Next-Key lock)
参考链接


隔离级别与锁的关系
在Read Uncommitted级别下,读取数据不需要加共享锁,这样就不会跟被修改的数据上的排他锁冲突
在Read Committed级别下,读操作需要加共享锁,但是在语句执行完以后释放共享锁;
在Repeatable Read级别下,读操作需要加共享锁,但是在事务提交之前并不释放共享锁,也就是必须等待事务执行完毕以后才释放共享锁。
SERIALIZABLE 是限制性最强的隔离级别,因为该级别锁定整个范围的键,并一直持有锁,直到事务完成。
按照锁的粒度分数据库锁有哪些?锁机制与InnoDB锁算法
在关系型数据库中,可以按照锁的粒度把数据库锁分为行级锁(INNODB引擎)、表级锁(MYISAM引擎)和页级锁(BDB引擎 )。

行级锁:行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁 和 排他锁。
特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
表级锁:表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MYISAM与INNODB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。
特点:开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。
页级锁:页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。
MySQL中InnoDB引擎的行锁是怎么实现的?
InnoDB是基于索引来完成行锁
例: select * from tab_with_index where id = 1 for update;
for update 可以根据条件来完成行锁锁定,并且 id 是有索引键的列,如果 id 不是索引键那么InnoDB将完成表锁,并发将无从谈起

InnoDB存储引擎的锁的算法有三种
Record lock:单个行记录上的锁
Gap lock:间隙锁,锁定一个范围,不包括记录本身
Next-key lock:record+gap 锁定一个范围,包含记录本身

什么是死锁?怎么解决?
死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。常见的解决死锁的方法:

如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。
在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;
对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;
数据库的乐观锁和悲观锁是什么?怎么实现的?
悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。在查询完数据的时候就把事务锁起来,直到提交事务。实现方式:使用数据库中的锁机制
乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。在修改数据的时候把事务锁起来,通过version的方式来进行锁定。实现方式:乐一般会使用版本号机制或CAS算法实现。
两种锁的使用场景:

乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。
但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。
视图
什么是视图?
为了提高复杂SQL语句的复用性和表操作的安全性,MySQL数据库管理系统提供了视图特性。所谓视图,本质上是一种虚拟表,在物理上是不存在的,其内容与真实的表相似,包含一系列带有名称的列和行数据。但是,视图并不在数据库中以储存的数据值形式存在。行和列数据来自定义视图的查询所引用基本表,并且在具体引用视图时动态生成。

视图使开发者只关心感兴趣的某些特定数据和所负责的特定任务,只能看到视图中所定义的数据,而不是视图所引用表中的数据,从而提高了数据库中数据的安全性。

为什么要使用视图?视图的优缺点
视图的优点

查询简单化。视图能简化用户的操作
数据安全性。视图使用户能以多种角度看待同一数据,能够对机密数据提供安全保护
逻辑数据独立性。视图对重构数据库提供了一定程度的逻辑独立性
视图的缺点
性能。数据库必须把视图的查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,即使是视图的一个简单查询,数据库也把它变成一个复杂的结合体,需要花费一定的时间。
修改限制。当用户试图修改视图的某些行时,数据库必须把它转化为对基本表的某些行的修改。事实上,当从视图中插入或者删除时,情况也是这样。对于简单视图来说,这是很方便的,但是,对于比较复杂的视图,可能是不可修改的,这些视图有如下特征:1.有UNIQUE等集合操作符的视图。2.有GROUP BY子句的视图。3.有诸如AVG\SUM\MAX等聚合函数的视图。 4.使用DISTINCT关键字的视图。5.连接表的视图(其中有些例外)
视图的使用场景有哪些?
重用SQL语句;
简化复杂的SQL操作。在编写查询后,可以方便的重用它而不必知道它的基本查询细节;
使用表的组成部分而不是整个表;
保护数据。可以给用户授予表的特定部分的访问权限而不是整个表的访问权限;
更改数据格式和表示。视图可返回与底层表的表示和格式不同的数据。
常用SQL语句
SQL语句主要分为哪几类
数据定义语言DDL(Data Ddefinition Language)CREATE,DROP,ALTER,主要为以上操作 即对逻辑结构等有操作的,其中包括表结构,视图和索引。
数据查询语言DQL(Data Query Language)SELECT,这个较为好理解 即查询操作,以select关键字。各种简单查询,连接查询等 都属于DQL。
数据操纵语言DML(Data Manipulation Language)INSERT,UPDATE,DELETE,主要为以上操作 即对数据进行操作的,对应上面所说的查询操作 DQL与DML共同构建了多数初级程序员常用的增删改查操作。而查询是较为特殊的一种 被划分到DQL中。
数据控制功能DCL(Data Control Language)GRANT,REVOKE,COMMIT,ROLLBACK,主要为以上操作 即对数据库安全性完整性等有操作的,可以简单的理解为权限控制等。
超键、候选键、主键、外键分别是什么?
超键:在关系中能唯一标识元组的属性集称为关系模式的超键。一个属性可以为作为一个超键,多个属性组合在一起也可以作为一个超键。超键包含候选键和主键。
候选键:是最小超键,即没有冗余元素的超键。
主键:数据库表中对储存数据对象予以唯一和完整标识的数据列或属性的组合。一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null)。
外键:在一个表中存在的另一个表的主键称此表的外键。
SQL 约束有哪几种?
NOT NULL: 用于控制字段的内容一定不能为空(NULL)。
UNIQUE: 控件字段内容不能重复,一个表允许有多个 Unique 约束。
PRIMARY KEY: 也是用于控件字段内容不能重复,但它在一个表只允许出现一个。
FOREIGN KEY: 用于预防破坏表之间连接的动作,也能防止非法数据插入外键列,因为它必须是它指向的那个表中的值之一。
CHECK: 用于控制字段的值范围。
六种关联查询的区别是什么?
内连接(INNER JOIN),返回左表和右表同时存在的行
左外连接(LEFT JOIN),返回左表中的所有行,如果左表中行在右表中没有匹配行,则在相关联的结果集中右表的所有字段均为NULL。
右外连接(RIGHT JOIN),返回右表中的所有行,如果右表中行在左表中没有匹配行,则在左表中相关字段返回NULL值。
联合查询(UNION与UNION ALL):
Union:对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序; Union All:对两个结果集进行并集操作,包括重复行,不进行排序;
全连接(FULL JOIN),返回两个连接中所有的记录数据,是左外链接和右外链接的并集。
交叉连接(CROSS JOIN),两个表做笛卡尔积,得到的结果集的行数是两个表中的行数的乘积。
varchar与char的区别
char的特点

char表示定长字符串,长度是固定的;
如果插入数据的长度小于char的固定长度时,则用空格填充;
因为长度固定,所以存取速度要比varchar快很多,甚至能快50%,但正因为其长度固定,所以会占据多余的空间,是空间换时间的做法;
对于char来说,最多能存放的字符个数为255,和编码无关
varchar的特点

varchar表示可变长字符串,长度是可变的;
插入的数据是多长,就按照多长来存储;
varchar在存取方面与char相反,它存取慢,因为长度不固定,但正因如此,不占据多余的空间,是时间换空间的做法;
对于varchar来说,最多能存放的字符个数为65532
SQL优化
SQL的生命周期?
应用服务器与数据库服务器建立一个连接
数据库进程拿到请求sql
解析并生成执行计划,执行
读取数据到内存并进行逻辑处理
通过步骤一的连接,发送结果到客户端
关掉连接,释放资源
MySQL执行顺序?
一共有十一个步骤,最先执行的是FROM操作,最后执行的是LIMIT操作。每个操作都会产生一个虚拟表,该虚拟表作为一个处理的输入,看下执行顺序:

FROM:对FROM子句中的左表<left_table>和右表<right_table>执行笛卡儿积,产生虚拟表VT1;
ON: 对虚拟表VT1进行ON筛选,只有那些符合<join_condition>的行才被插入虚拟表VT2;
JOIN: 如果指定了OUTER JOIN(如LEFT OUTER JOIN、RIGHT OUTER JOIN),那么保留表中未匹配的行作为外部行添加到虚拟表VT2,产生虚拟表VT3。如果FROM子句包含两个以上的表,则对上一个连接生成的结果表VT3和下一个表重复执行步骤1~步骤3,直到处理完所有的表;
WHERE: 对虚拟表VT3应用WHERE过滤条件,只有符合<where_condition>的记录才会被插入虚拟表VT4;
GROUP By: 根据GROUP BY子句中的列,对VT4中的记录进行分组操作,产生VT5;
CUBE|ROllUP: 对VT5进行CUBE或ROLLUP操作,产生表VT6;
HAVING: 对虚拟表VT6应用HAVING过滤器,只有符合<having_condition>的记录才会被插入到VT7;
SELECT: 第二次执行SELECT操作,选择指定的列,插入到虚拟表VT8中;
DISTINCT: 去除重复,产生虚拟表VT9;
ORDER BY: 将虚拟表VT9中的记录按照<order_by_list>进行排序操作,产生虚拟表VT10;
LIMIT: 取出指定街行的记录,产生虚拟表VT11,并返回给查询用户
数据库结构优化
将字段很多的表分解成多个表
增加中间表
增加冗余字段
为表中的字段选择合适的数据类型
大表数据查询,怎么优化
优化shema、sql语句+索引;
加缓存,memcached, redis;
主从复制,读写分离;
垂直拆分,根据你模块的耦合度,将一个大的系统分为多个小的系统,也就是分布式系统;
水平切分,针对数据量大的表,这一步最麻烦,最能考验技术水平,要选择一个合理的sharding key, 为了有好的查询效率,表结构也要改动,做一定的冗余,应用也要改,sql中尽量带sharding key,将数据定位到限定的表上去查,而不是扫描全部的表;
超大分页怎么处理?
解决超大分页,其实主要是靠缓存,可预测性的提前查到内容,缓存至redis等k-V数据库中,直接返回即可.

慢查询日志,慢查询都怎么优化?
数据库中设置SQL慢查询
利用explain关键字可以模拟优化器执行SQL查询语句,来分析sql慢查询语句
索引没起作用的情况

使用LIKE关键字的查询语句:在使用LIKE关键字进行查询的查询语句中,如果匹配字符串的第一个字符为“%”,索引不会起作用。只有“%”不在第一个位置索引才会起作用。
使用多列索引的查询语句:MySQL可以为多个字段创建索引。一个索引最多可以包括16个字段。对于多列索引,只有查询条件使用了这些字段中的第一个字段时,索引才会被使用。
优化数据库结构

将字段很多的表分解成多个表 :对于字段比较多的表,如果有些字段的使用频率很低,可以将这些字段分离出来形成新表。因为当一个表的数据量很大时,会由于使用频率低的字段的存在而变慢。
增加中间表:对于需要经常联合查询的表,可以建立中间表以提高查询效率。通过建立中间表,把需要经常联合查询的数据插入到中间表中,然后将原来的联合查询改为对中间表的查询,以此来提高查询效率。
分解关联查询

将一个大的查询分解为多个小查询:每一个表进行一次单表查询,然后将查询结果在应用程序中进行关联
优化LIMIT分页

先查询出主键id值:先查询出90000条数据对应的主键id的值,然后直接通过该id的值直接查询该id后面的数据。
select id,title from collect where id>=(select id from collect order by id limit 90000,1) limit 10;

关延迟联,如果这个表非常大,那么这个查询可以改写成如下的方式:
Select news.id, news.description from news inner join (select id from news order by title limit 50000,5) as myNew using(id);

建立复合索引 acct_id和create_time
select * from acct_trans_log WHERE acct_id = 3095 order by create_time desc limit 0,10

分析具体的SQL语句

百万级别或以上的数据如何删除
总的指导原则是先删除数据,不影响数据一致性的要求,之后分阶段分步骤的进行空间回收。避免一次性drop对系统造成过大的压力。
步骤:

首先使用带reuse storage子句的truncate table,将数据删除。reuse storage子句的作用是单纯降低数据段data segment的高水位线,对分配的空间不进行回收。这样truncate操作不涉及到空间回收,速度是可以接受的。
分若干次数,使用deallocate unused keep XXX的方法,将分配的空间回收。因为keep后面可以加入维持空间数量,所以可以分若干个窗口期进行回收。
大表怎么优化
限定数据的范围
读/写分离
缓存
分库分表
SQL优化技巧
选择合适的索引列规则

选择在where子句中常用的查询列
选择常用来关联表的字段
对普通的B-TREE索引,应选择具有选择性高的字段做索引字段。若字段的不同取值很少,则选择性低,适合建位映射索引(效率更高,占用空间更小)。
不要在经常被修改的字段上建索引。索引会降低update, insert, delete等操作的效率
避免在索引列上使用计算或在非基于函数的索引列上使用函数

where子句中,若索引列是函数的一部分,优化器将不使用索引而使用全表扫描
使用了!=, |, 类型转换将不使用索引
要对使用函数的列启用索引,建议建基于函数的索引;也可以将计算右移
总使用索引的第一个列
若索引是复合索引,只有在它的第一个列被where子句引用时,优化器才会选择使用该索引。

表名顺序

ORACLE解释器按 从右到左 的顺序处理FROM子句中的表名。
在FROM子句包含多个表的情况下,应选择记录数最小的表作为基础表。
若有3个以上的表,则需选择交叉表作为基础
where子句中的连接顺序
ORACLE 自下而上 的解析where子句。表之间的连接应该写在其他where条件之前,那些可以过滤掉最大数量记录的条件应写在where子句的末尾。

使用union all,而不是union
union = union all + 排序剔重
若不需要踢重,使用union all会节省时间,提高效率

使用truncate,而不是delete
delete后,数据可以恢复。
而truncate后,数据不可恢复,因此很少资源被调用,执行时间也短。

使用where子句,而不是having子句
having语句是检索出结果后对结果集过滤。一般用于对一些聚合函数的比较

使用union,而不是or
对索引列用or会造成全表扫描
也可以用in替换or,更进一步,用exist替换in

其他

优化group by语句:将不需要的记录在group by之前过滤掉
慎用is null:null值不在索引中,若用is null操作会使用不了索引
在OLTP总是用绑定变量。例如:select * from tbl where msisdn=:1
select语句中避免出现*
MySQL
MySQL的复制原理以及流程
主从复制:将主数据库中的DDL和DML操作通过二进制日志(BINLOG)传输到从数据库上,然后将这些日志重新执行(重做);从而使得从数据库的数据与主数据库保持一致。

主从复制的作用

主数据库出现问题,可以切换到从数据库。
可以进行数据库层面的读写分离。
可以在从数据库上进行日常备份。
MySQL主从复制解决的问题

数据分布:随意开始或停止复制,并在不同地理位置分布数据备份
负载均衡:降低单个服务器的压力
高可用和故障切换:帮助应用程序避免单点失败
升级测试:可以用更高版本的MySQL作为从库
MySQL主从复制工作原理

在主库上把数据更高记录到二进制日志
从库将主库的日志复制到自己的中继日志
从库读取中继日志的事件,将其重放到从库数据中
基本原理流程,3个线程以及之间的关联

主:binlog线程——记录下所有改变了数据库数据的语句,放进master上的binlog中;
从:io线程——在使用start slave 之后,负责从master上拉取 binlog 内容,放进自己的relay log中;
从:sql执行线程——执行relay log中的语句;
master在每个事务更新数据完成之前,将该操作记录串行地写入到binlog文件中。
salve开启一个I/O Thread,该线程在master打开一个普通连接,主要工作是binlog dump process。如果读取的进度已经跟上了master,就进入睡眠状态并等待master产生新的事件。I/O线程最终的目的是将这些事件写入到中继日志中。
SQL Thread会读取中继日志,并顺序执行该日志中的SQL事件,从而与主数据库中的数据保持一致。
读写分离有哪些解决方案?
使用mysql-proxy代理:
优点:直接实现读写分离和负载均衡,不用修改代码,master和slave用一样的帐号,mysql官方不建议实际生产中使用
缺点:降低性能, 不支持事务

使用AbstractRoutingDataSource+aop+annotation在dao层决定数据源。
如果采用了mybatis, 可以将读写分离放在ORM层,比如mybatis可以通过mybatis plugin拦截sql语句,所有的insert/update/delete都访问master库,所有的select 都访问salve库,这样对于dao层都是透明。 plugin实现时可以通过注解或者分析语句是读写方法来选定主从库。不过这样依然有一个问题, 也就是不支持事务, 所以我们还需要重写一下DataSourceTransactionManager, 将read-only的事务扔进读库, 其余的有读有写的扔进写库。

使用AbstractRoutingDataSource+aop+annotation在service层决定数据源,可以支持事务.
缺点:类内部方法通过this.xx()方式相互调用时,aop不会进行拦截,需进行特殊处理。

MySQL有哪些数据类型
1.字符串类型

char(n) 定长字符串,n的最大值为255, 表示存储255个字符,一般用于长度固定的字符串,比如手机号、身份证号等,效率高
varchar(n) 不定长字符串,n的最大值为 65535个字节(5.0以后的版本),一般用于长度不固定的字符串,比如用户名、昵称等,节省空间
2.数值类型

TINYINT:占用1个字节,相对于java中的byte
SMALLINT:占用2个字节,相对于java中的short
INT:占用4个字节,相对于java中的int
BIGINT:占用8个字节,相对于java中的long
FLOAT:4字节单精度浮点类型,相对于java中的float
DOUBLE:8字节双精度浮点类型,相对于java中的double
3.大数据类型

BLOB: 大二进制类型,可以存入二进制类型的数据,通过这个字段,可以将图片、音频、视频等数据以二进制的形式存入数据库。最大为4GB。
TEXT:

大文本,被声明为这种类型的字段,可以保存大量的字符数据,最大为4GB。
text属于mysql的方言,在其他数据库中为clob类型
4.日期类型

DATE:日期 2017-11-05
TIME:时间 格式 ‘HH:MM:SS’ 19:19:19
DATETIME:日期时间 2017-11-05 19:19:19 年份范围:1000~9999
TIMESTAMP:时间戳 2017-11-05 19:19:19 年份范围:1970~2037
5.逻辑型

BIT型字段只能取两个值:0或1。
MySQL存储引擎MyISAM与InnoDB区别
InnoDB支持事务,MyISAM不支持,这一点是非常之重要。事务是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以了。
MyISAM适合查询以及插入为主的应用,InnoDB适合频繁修改以及涉及到安全性较高的应用
InnoDB支持外键,MyISAM不支持
MyISAM是默认引擎,InnoDB需要指定
InnoDB不支持FULLTEXT类型的索引
InnoDB中不保存表的行数,如select count() from table时,InnoDB需要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count()语句包含where条件时MyISAM也需要扫描整个表
对于自增长的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中可以和其他字段一起建立联合索引
清空整个表时,InnoDB是一行一行的删除,效率非常慢。MyISAM则会重建表
InnoDB支持行锁(某些情况下还是锁整表,如 update table set a=1 where user like ‘%lee%’
MySQL如何查看执行计划,如何根据执行计划进行SQL优化
在SQL语句前加上explain,执行计划结果表头如下:

id: 表示查询中select操作表的顺序,按顺序从大到依次执行
select_type :该表示选择的类型,可选值有: SIMPLE(简单的),
type :该属性表示访问类型,有很多种访问类型。最常见的其中包括以下几种: ALL(全表扫描), index(索引扫描),range(范围扫描),ref (非唯一索引扫描),eq_ref(唯一索引扫描,),(const)常数引用, 访问速度依次由慢到快。其中 : range(范围)常见与 between and …, 大于 and 小于这种情况。慢SQL是否走索引,走了什么索引,也就可以通过该属性查看了。
table : 表示该语句查询的表
possible_keys :顾名思义,该属性给出了,该查询语句,可能走的索引,(如某些字段上索引的名字)这里提供的只是参考,而不是实际走的索引,也就导致会有possible_Keys不为null,key为空的现象。
key : 显示MySQL实际使用的索引,其中就包括主键索引(PRIMARY),或者自建索引的名字。
key_len : 表示索引所使用的字节数,
ref : 连接匹配条件,如果走主键索引的话,该值为: const, 全表扫描的话,为null值
rows :扫描行数,也就是说,需要扫描多少行,采能获取目标行数,一般情况下会大于返回行数。通常情况下,rows越小,效率越高, 也就有大部分SQL优化,都是在减少这个值的大小。注意: 理想情况下扫描的行数与实际返回行数理论上是一致的,但这种情况及其少,如关联查询,扫描的行数就会比返回行数大大增加)
Extra这个属性非常重要,该属性中包括执行SQL时的真实情况信息,如上面所属,使用到的是”using where”,表示使用where筛选得到的值,常用的有:“Using temporary”: 使用临时表 “using filesort”: 使用文件排序
什么是MVCC?
参考链接
英文全称为Multi-Version Concurrency Control,翻译为中文即 多版本并发控制。是乐观锁的一种实现方式。在Java编程中,如果把乐观锁看成一个接口,MVCC便是这个接口的一个实现类。

特点

MVCC其实广泛应用于数据库技术,像Oracle,PostgreSQL等也引入了该技术,即适用范围广
MVCC并没有简单的使用数据库的行锁,而是使用了行级锁,row_level_lock,而非InnoDB中的innodb_row_lock.
基本原理
MVCC的实现,通过保存数据在某个时间点的快照来实现的。这意味着一个事务无论运行多长时间,在同一个事务里能够看到数据一致的视图。根据事务开始的时间不同,同时也意味着在同一个时刻不同事务看到的相同表里的数据可能是不同的。

基本特征

每行数据都存在一个版本,每次数据更新时都更新该版本。
修改时Copy出当前版本随意修改,各个事务之间无干扰。
保存时比较版本号,如果成功(commit),则覆盖原记录;失败则放弃copy(rollback)
InnoDB存储引擎MVCC的实现策略
在每一行数据中额外保存两个隐藏的列:当前行创建时的版本号和删除时的版本号(可能为空,其实还有一列称为回滚指针,用于事务回滚,不在本文范畴)。这里的版本号并不是实际的时间值,而是系统版本号。每开始新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询每行记录的版本号进行比较。

每个事务又有自己的版本号,这样事务内执行CRUD操作时,就通过版本号的比较来达到数据版本控制的目的。

Oracle、PostgreSQL
Oracle、PostgreSQL、MySQL有什么区别?优缺点?
Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是目前世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小、微机环境。它是一种高效率、可靠性好的 适应高吞吐量的数据库解决方案。

优点

Oracle 能在所有主流平台上运行(包括 windows)完全支持所有工业标准采用完全开放策略使客户选择适合解决方案对开发商全力支持。
Oracle 并行服务器通过使组结点共享同簇工作来扩展windownt能力提供高用性和高伸缩性簇解决方案windowsNT能满足需要用户把数据库移UNIXOracle并行服务器对各种UNIX平台集群机制都有着相当高集成度。
获得最高认证级别的ISO标准认证。
Oracle 性能高 保持开放平台下TPC-D和TPC-C世界记录。
Oracle 多层次网络计算支持多种工业标准用ODBC、JDBC、OCI等网络客户连接。
Oracle 长时间开发经验完全向下兼容得广泛应用地风险低。
缺点

对硬件的要求很高。
价格比较昂贵。
管理维护麻烦一些。
操作比较复杂,需要技术含量较高。
MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 旗下公司。MySQL在 WEB 应用方面 MySQL 是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。MySQL 是一种关联数据库管理系统, 关联数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。MySQL 所使用的 SQL 语言是用于访问数据库的最常用标准化语言。其社区版的性能卓越,搭配 PHP 和 Apache 可组成良好的开发环境。

优点

体积小、速度快、总体拥有成本低,开源,提供的接口支持多种语言连接操作。
支持多种操作系统。
MySQL 的核心程序采用完全的多线程编程。线程是轻量级的进程,它可以灵活地为用户提供服务,而不过多的系统资源。用多线程和C语言实现的MySQL 能很容易充分利用CPU。
MySQL 有一个非常灵活而且安全的权限和口令系统。当客户与MySQL 服务器连接时,他们之间所有的口令传送被加密,而且MySQL 支持主机认证。
MySQL 能够提供很多不同的使用者界面,包括命令行客户端操作,网页浏览器,以及各式各样的程序语言界面,例如 C++,Perl,Java,PHP,以及Python。你可以使用事先包装好的客户端,或者干脆自己写一个合适的应用程序。MySQL可用于 Unix,Windows,以及OS/2等平台,因此它可以用在个人电脑或者是服务器上。
缺点

不支持热备份。
MySQL不支持自定义数据类型
MySQL最大的缺点是其安全系统,主要是复杂而非标准,另外只有到调用mysqladmin来重读用户权限时才发生改变。
MySQL对存储过程和触发器支持不够良好。
尽管 MySQL 理论上仍是开源产品,也有人抱怨它诞生之后更新缓慢。然而,应该注意到有一些基于 MySQL 并完整集成的数据库(如 MariaDB),在标准的 MySQL 基础上带来了额外价值。
MySQL对XML支持不够良好
PostgreSQL是一个自由的对象-关系数据库服务器(数据库管理系统),支持大部分 SQL标准并且提供了许多其他现代特性:复杂查询、外键、触发器、视图、事务完整性、MVCC。同样,PostgreSQL 可以用许多方法扩展,比如, 通过增加新的数据类型、函数、操作符、聚集函数、索引。免费使用、修改、和分发 PostgreSQL。
优点

PostgreSQL 是一个开源的,免费的,同时非常强大的关系型数据管理系统。
PostgreSQL 背后有热忱而经验丰富的社区,可以通过知识库和问答网站获取支持,全天候免费。
即使其本身功能十分强大,PostgreSQL 仍附带有许多强大的开源第三方工具来辅助系统的设计、管理和使用。
可以用预先存储的流程来程序性扩展 PostgreSQL ,一个高级的关系型数据库理应如此。
PostgreSQL 不只是一个关系型数据库,还是一个面向对象数据库——支持嵌套,及一些其他功能。
缺点

对于简单而繁重的读取操作, 超过了 PostgreSQL 的杀伤力,可能会出现比同行(如MySQL)更低的性能。
按给出的该工具的性质,从普及度来说它还缺乏足够后台支撑,尽管有大量的部署——这可能会影响能够获得支持的容易程度。
Oracle、PostgreSQL、MySQL怎么实现分页查询?
postgresql数据库分页实现方式:

SELECT select_list FROM table_expression [LIMIT { number | ALL }] [OFFSET number]
1
OFFSET说明在开始返回行之前忽略多少行。 OFFSET 0和省略OFFSET子句是一样的。 如果OFFSET和LIMIT都出现了, 那么在计算LIMIT个行之前忽略OFFSET行。

oracle数据库分页实现方式:

SELECT * FROM ( SELECT rownum AS rnum, e.* FROM ( SELECT * FROM table ) e WHERE rownum <= 10 ) WHERE rnum >= 8;
1
mysql数据库分页实现方式:

select * from table limit (pageNo-1)*pageSize, pageSize;
1
Oracle、PostgreSQL、MySQL怎么实现递归查询?
表结构:

CREATE TABLE IF NOT EXISTS `DS_CATALOG` (
`CATALOG_ID` int(3) NOT NULL COMMENT 'ID',
`CATALOG_PARENT_ID` int(3) DEFAULT NULL COMMENT '父ID',
`CATALOG_NAME` varchar(255) DEFAULT NULL COMMENT '目录名称',
`CATALOG_OWNER` varchar(255) DEFAULT NULL COMMENT '目录所有者',
`ORDER_INDEX` int(3) DEFAULT NULL COMMENT '排序索引',
PRIMARY KEY (`CATALOG_ID`)
);


MySQL:
创建自定义函数

DROP FUNCTION IF EXISTS DS_GET_CHILD_CATALOG;|
CREATE FUNCTION DS_GET_CHILD_CATALOG(rootID varchar(100))
RETURNS varchar(2000)
BEGIN
DECLARE arr varchar(2000);
DECLARE pid varchar(100);
SET arr = '$';
SET pid = rootID;
WHILE pid is not null DO
SET arr = concat(arr, ',', pid);
SELECT group_concat(CATALOG_ID) INTO pid FROM DS_CATALOG where FIND_IN_SET(CATALOG_PARENT_ID, pid);
END WHILE;
RETURN arr;
END;

查询

SELECT * FROM DS_CATALOG FIND_IN_SET(CATALOG_ID,DS_GET_CHILD_CATALOG(1))
1
Oracle:

SELECT * FROM DS_CATALOG START WITH CATALOG_ID = 1 CONNECT BY PRIOR CATALOG_ID=CATALOG_PARENT_ID
1
PostgreSQL:

WITH RECURSIVE R AS (
SELECT * FROM DS_CATALOG WHERE CATALOG_ID = 2 UNION ALL
SELECT DS_CATALOG.* FROM DS_CATALOG, R WHERE DS_CATALOG.CATALOG_PARENT_ID = R.CATALOG_ID
)
SELECT * FROM R ORDER BY CATALOG_ID;

posted @ 2022-02-27 10:28  hanease  阅读(49)  评论(0编辑  收藏  举报