数据库面试
- 什么是事务?以及事务的ACID?
- 请描述数据库的隔离级别
- 视图和数据表的区别是什么?
- SQL优化:
- 什么是索引?以及索引的应用?
什么是事务?以及事务的ACID?
事务:事务是指严密逻辑的操作,要么都成功,要么都失败。
通俗一点说:一件事中有很多个环节,任何一个环节出现问题,整件事都作为,只有所有环节都成功时,整件事成功。
ACID指的是事务的4个特性:
原子性:执行一个事务中的每一步操作要么全部成功,那么全部失败。也就是说其中一个操作失败,那么之前执行的所有操作都失败,数据回滚,回滚到事务开始前的状态。
一致性:事务是将数据从一个状态转换为另一个状态,但是对于整体的完整性是稳定的。
隔离性:在多个线程并发操作数据库中同一张表,数据库会为每一个用户提供一个事务,并且事务是相互独立的,每个事务中的操作应当是互不干扰的。
持久性:当事务正确完成后,对数据库的影响是永久的。
请描述数据库的隔离级别
注:事务的隔离级别主要解决多线程并发操作时(多个事务,每个线程开启一个事务)时产生效果。
数据库事务的隔离级别有四个级别(从低到高):
-
读取未提交(READ_UNCOMMITTED):最低的隔离级别,该级别就好似事务之间没有隔离性,会出现脏读,幻读,不可重复读。
特点:A事务和B事务同时操作一张表,A事务在执行过程中可以读取到B事务修改但还没有提交后的效果
-
读取已提交(READ_COMMITTED):避免脏读现象,还是会出现不可重复读以及幻读现象
特点:A事务和B事务同时操作一张表,A事务在执行过程中不会读取到B事务修改但是还未提交后的效果。
-
可重复读(REPEATABLE_READ):避免脏读已经不可重复读的现象,但是还是会出现幻读现象。
特点:A事务在操作一张表的过程中,多次查询同一个数据时在本事务没有对该数据进行过任何修改的前提下,无论其他事务是否对该数据进行过修改,本事务总是会读取到重复的值。
Mysql数据库默认的隔离级别。
-
可串行化(SERIALIZABLE):最高的隔离级别,完全服从ACID特性的,可以保证避免脏读,幻读,不可重复读。
-
脏读,幻读,不可重复读:
-
脏读:A事务读取了B事务修改的表中未提交的数据,但是B事务又是失败回退了,此时A事务读取的数据就是脏数据。
例如:
场景
程序员A去数据库查询自己的工资,此时他开启了一个事务。
财务员B去数据库修改员工工资,此时他将程序员的工资从800涨到了9000。
-
不可重复读
在一个事务中首先查询了表中头条记录,在该事务经过了一系列操作(没有对表中该记录做任何修改)后,又再次读取表中该条记录时发现值变了,这就是所谓的不可重复读。
-
幻读
A事务在设定了一个查询条件的前提下检索表中数据时查询出了1条记录。按照该条件修改对应的表中记录(按理来讲应当值修改1条记录),结果确显示修改了2(或者其他数)条记录,好像产生了幻觉一样。
-
视图和数据表的区别是什么?
视图VIEW和表TABLE都是数据库对象,可以使用DDL语句操作。
创建表是CREATE TABLE ,创建视图CREATE VIEW
区别是:表有结构,有数据,占用磁盘空间真实保存数据。
视图仅对应一个查询语句的结果集。
视图的使用场景:
1:重用复杂查询,不必每次写繁琐的DQL语句,而是将该DQL定义为一个视图,直接FROM该视图即可。
2:视图可以隐藏信息,DBA可以将一个表按照查询要求定义一个视图,仅查询表中符合某些特定业务条件的部分记录,将其定义为视图,让使用者基于视图查询。来隐藏不需要看到的数据
例如:工资中的员工表涉及到员工的隐私信息,比如电话,家庭住址,工资等信息。但是由于业务需求,开发程序时要查询员工的其他信息,比如名字,年龄,但不需要查看隐私信息,此时DBA就可以专门定义一个视图,来指定仅查询名字年龄等信息,供程序使用。
SQL优化:
DQL语句的优化(SELECT语句)
DQL语句结构:
SELECT [DISTINCT] 子句
FROM 子句
[JOIN ... ON ...子句]
WHERE 子句
GROUP BY 子句
HAVING 子句
ORDER BY 子句
LIMIT 子句DQL语句的执行优先级:
1:FROM
2:JOIN...ON...子句
3:WHERE子句
4:GROUP BY 子句
5:HAVING 子句
6:SELECT 子句
7:ORDER BY 子句
8:LIMIT 子句
优化原则:
-
如果在查询语句中使用了LIKE进行模糊查询时,避免LIKE ‘%范’(以模糊项开始)。
因为模糊项开始会导致数据库启用对应字段的索引,并进行全表扫描。
可以:LIKE ‘范%’.模糊项在后面
-
尽量不要使用IN,NOT IN
上述两种也会导致进行全表扫描。
如果实在一个范围内,尽量采取BETWEEN…AND…来解决
例如:
WHERE id IN(2,3,4)
要用:WHERE id BETWEEN 2 AND 4
-
尽量避免使用OR连接条件。因为OR也会导致数据库放弃索引
WHERE id=3 OR id=5 全表扫描,放弃id的索引使用。
替换为:
SELECT xxx FROM xxx WHERE id=3
UNION
SELECT xxx FROM xxx WHERE id=5
-
避免NULL值判断,因为该操作也会导致数据库放弃索引进行全表检索。
SELECT xx FROM xxx WHERE score IS NULL
将字段指定一个默认值,比如0
SELECT xx FROM xxx WHERE score = 0
-
WHERE条件中尽量不要再判断的左侧出现函数或表达式。也会导致放弃索引全表检索
SELECT xx FROM xxx WHERE score/5=10 此时数据库会弃用score字段的索引
应当替换为:
SELECT xx FROM xxx WHERE score=10*5
-
尽量不用<>或!=
-
隐式的数据类型转换不启用索引
SELECT XXX FROM XXXX WHERE colVARCHAR=123;
注:colVARCHAR是一个VARCHAR类型的字段
-
ORDER BY的字段与WHERE条件一致,否则ORDER BY不会根据字段使用索引
例如:
SELECT xxxx FROM xxxx ORDER BY salary; 不会使用salary的索引
SELECT xxxx FROM xxxx WHERE salary>3000 ORDER BY salary;会使用salary的索引
-
不要使用SELECT *
-
多表关联查询,FROM子句中小表在前,大表在后
-
在多表关联查询时,每个表要指定别名,并且在SELECT子句中为每个字段添加表别名
-
在WHERE子句中将可以过滤最多记录的条件放在最前(最左优先原则)。
例如:
查看工资高于3000的男老师都有谁?
思考:teacher表中是工资高于3000的记录多,还是男老师的记录多
如果是男老师的记录多:
SELECT xxx FROM teacher WHERE salary>3000 AND gender=‘男’
如果是工资大于3000的记录多:
SELECT xxx FROM teacher WHERE gender=‘男’ AND salary>3000
-
能在WHERE中过滤的就不要放到HAVING中过滤。HAVING应当仅过滤有聚合函数作为条件的
DML中的优化
批量插入数据时:
INSERT INTO T VALUES(1,‘张三’);
INSERT INTO T VALUES(2,‘李四’);
INSERT INTO T VALUES(3,‘王五’);
应当换做:
INSERT INTO T VALUES(1,‘张三’),(2,‘李四’),(3,‘王五’);
在大量数据操作时,尽量减少事务commit的次数。
什么是索引?以及索引的应用?
索引也是数据库对象,是检索数据时排序数据的结构。就好像新字典是一张表,前面的汉语拼音查询,偏旁部首查询就是索引。用于提高检索效率的手段。
什么使用使用索引:
- 经常检索的字段
- 经常作为过滤条件的字段
- 经常作为排序的字段
- 作为表连接条件的字段(外键字段)
- 主键字段
什么时候不使用索引:
- 永远不要在小表上添加索引
- 经常进行DML操作的表不建议加索引(因为索引失效速度太快)
- 向TEXT类型,IMAGE类型的字段不加索引
- 列中值的取值范围不大的不建议加,比如表中的性别字段。
数据库事务隔离级别练习中的SQL脚本
CREATE DATABASE testmysqldb;
USE testmysqldb;
CREATE TABLE teacher(id INT AUTO_INCREMENT PRIMARY KEY ,name VARCHAR(30),age INT(2),title VARCHAR(30),manager INT,salary INT(6),comm INT(6),gender CHAR(1),subject_id INT
);
INSERT INTO teacher (name, age, title, manager, salary, comm, gender, subject_id) VALUES ('宋柠檬',33,'特级讲师',1,3000,NULL,'女',2);
NT,salary INT(6),comm INT(6),gender CHAR(1),subject_id INT
);
INSERT INTO teacher (name, age, title, manager, salary, comm, gender, subject_id) VALUES ('宋柠檬',33,'特级讲师',1,3000,NULL,'女',2);