# 数据库的分类
非关系数据库:mysql、oracle、sqlserver 等
关系型数据库:redis、memcache、mongodb、hahdoop 等
redis:键值对数据库
mongodb:文档数据库
# 数据库三范式
范式就是规范,就是在关系型数据库设计表时要遵循的规范
要想满足第二范式就必须满足第一范式,要想满足第三范式就必须满足第二范式
第一范式:要求属性具有原子性,不可再分解
第二范式:每一行必须被唯一标识(主键)
第三范式:任何字段不能由其他字段派生出来,要求字段没有冗余(外键)
# 事务四个基本特性(ACID)
事务是并发控制的单位,是用户定义的一个操作序列,这些操作要么都做要么都不做,是一个不可分割的工作单位
原子性(A)
一个事务要么完整执行,要么就不执行
一致性(C)
底层数据存储的完整性
隔离性(I)
事务必须在不干扰其他进程或事务的前提下独立完成
持久性(D)
某个事务的执行过程中,对数据所做的所有改动都必须再事务成功结束前保存至某种物理存储设备
# mysql 的内连接、左连接、右连接有什么区别
- 内连接:把匹配的关联数据显示出来
- 左连接:左边的表全部显示出来,右边的表显示出符合条件的数据
- 右连接:与左连接正好相反
# 事务的隔离级别
脏读:A 查询 B 修改后问提交的数据,当 B 回滚时,A 查询的数据是无效的
不可重复读:A 在第一次查询用户甲的信息,B 将用户甲的信息修改并提交;A 再次读取用户甲的信息,A 两次获取的信息不同则称为 “不可重复读”
幻读:A 查询用户数量时,当 B 新增或删除用户时,A 再次获取用户数量时,两次数量不一致,则称为 “幻读”
注意:“不可重复读” 与 “幻读” 的区别在于,不可重复读强调的是数据信息的改变,幻读强调的是数量上的改变
隔离级别 脏读 不可重复读 幻读 读未提交 (Read Uncommitted) × × × 读已提交 (Read Committed) √ × × 可重复读 (Repeated Read) √ √ × 串行化 (Serializable) √ √ √ 以上四种隔离级别,串行化的级别最高,读未提交的级别最低,级别越高,效率越低
MySQL 支持以上四种隔离级别,默认的隔离级别是可重复度
Oracle 数据库只支持串行化和读已提交 ,默认是读已提交;
# Mysql 最大的默认连接数
100
为什么需要最大连接数?
特定服务器上面数据库最多只能支持一定数目同时连接
# Mysql 分页?Oracle 分页
为什么需要分页?
当有很多数据,一个页面不可能显示为所有数据,需要进行分段显示
mysql 使用了 limit 关键字来限制查询条数
oracle 使用了 rownum 三层嵌套循环
# 对 JDBC 的理解
他就是 Java 与数据库建立连接的桥梁或插件,用 Java 代码就能操作数据库的增删查改、存储过程、事务等
# 写一个简单的 JDBC 程序
# 操作步骤
- 加载驱动 (com.mysql.jdbc.Driver)
- 获取参数 (DriverManager.getConnection (url,username,password))
- 设置参数 (Statement PrepareStatement)
- 执行 (execute)
# 例子
Class.forName("com.mysql.cj.jdbc.Driver"); | |
String url = "jdbc:mysql://localhost:3306/user?serverTimezone=GMT"; | |
String username = "root"; | |
String password = "123456"; | |
Connection connection = DriverManager.getConnection(url,username,password); | |
String sql = "select * from user"; | |
PreparedStatement preparedStatement = connection.prepareStatement(sql); | |
ResultSet resultSet = preparedStatement.executeQuery(); | |
while (resultSet.next()){ | |
System.out.println(resultSet.getObject(1)+" "+resultSet.getObject(2)); | |
} | |
connection.close(); | |
preparedStatement.close(); | |
resultSet.close(); |
# PreparedStatement 相比于 statement 的好处
- PreparedStatement 是预编译的,比 statement 快
- 代码的可读性和可维护性高
- 可以防 SQL 注入
# 数据库连接池的作用
- 限定数据库的连接个数,进行统一的连接管理
- 节约资源
- 加快响应速度
# MySQL 索引是怎么实现的?
索引满足某种特定查找算法的数据结构,而这些数据结构会以某种方式指向数据,从而实现高效查找数据。
对于 mysql 的索引,不同的数据引擎实现有所不同,但目前主流的数据引擎的索引都是 B + 树实现的,B + 树的搜索效率可以达到二分法的性能,找到数据区域后就找到完整的数据结构了,所有索引的性能也是更好的。
# B + 索引和 hash 索引的区别
- B + 索引:数据有序,范围查询
- hash 索引:等值查询效率高,不能排序,不能进行范围查询
# 聚集索引和非聚集索引的区别
- 聚集索引:数据按索引顺序存储,中子节点存储真实的物理数据
- 非聚集索引:存储指向真正数据行的指针
# 索引的优缺点,什么时候使用索引,什么时候不能使用索引
- 索引最大的好处是提高查询速度
- 缺点是更新数据时效率低,需要同时更新索引
- 对数据频繁查询时建立索引,对于频繁更改数据时不建议使用索引
# 索引最左前缀问题
如果对三个字段建立联合索引,如果第二个字段没有使用索引,则第三个字段也是用不到索引
# 索引的底层实现(B + 树,为何不采用红黑树,B 树)
- 红黑树:增加、删除,红黑树会进行频繁的调整,来保证红黑树的性质,浪费时间
- B 树:查询性能不稳定,查询结果高度不致,每个节点保存指向真实数据的指针,相对于 B + 树每一层每屋存储的元素更多,显得更高一点
- B + 树:相对于另外两种树,显得更矮更宽,查询层次更浅
# 怎么验证 MySQL 的索引是否满足需求?
使用 explain 查询 sql 是如何执行查询语句的,从而分析你的索引是否满足要求
# SQL 优化
尽量命中索引,避免全表扫描
- 联合索引注意最左前缀法则
- 索引上尽量不要进行任何操作(计算、函数、类型转化等),否则会导致全表扫描
- 尽量使用索引覆盖(只访问索引的查询),减少 "select *"
- like 以通配符开始,索引会失效,导致全表扫描,如 name like "% abc"
- 字符串查询需要使用单引号
- 少用 in 或 or,使用时不一定命中索引,mysql 内部会进行评估,看是否使用索引
- 如果直到查询结果只有一条或者只要最大 / 最小一条,建议 limit 1
- 尽量避免 where 字句中使用 or 连接条件(可使用 union all)
- limit 优化:order by + 索引 + limit
- 子查询变成 left join
- 避免嵌套查询
# 索引分类及索引失效条件
- 普通索引:最基本的索引,没有任何限制
- 唯一索引:与普通索引类似,不同的是索引列的值必须唯一,但允许有空值
- 主键索引:它是一种特殊的唯一索引,不允许有空值
- 全文索引:针对较大的数据,生成全文索引很耗时耗空间
- 组合索引:为了更多的提高 mysql 效率可建立组合索引,遵循最左前缀原则
失效条件:
- 条件是 or,如果还想让 or 条件生效,给 or 每个字段加个索引
- like 查询,以 % 开始
- 内部函数
- 对索引列进行计算
- is null 不会用,is not null 会用
# MySQL 中有哪几种锁?
表级锁:开销小,加锁快,不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
行级锁:开销大,加锁慢,会出现死锁;锁定粒度最小,发生锁冲突的概率最小,并发度最高。
页面锁:开销和加锁时间介于表锁和行锁,会出现死锁;锁定粒度介于表锁和行锁之间,并发度以一般。
# MySQL 有哪些不同的表格
MyISAM
Heap
Merge
INNODB
ISAM
# 简述在 MySQL 数据库中 MyISAM 和 InnoDB 的区别
MyISAM
不支持事物,但是每次查询都是原子的;
支持表级锁,即每次操作都是对整个表加锁;
存储表的总行数;
一个 MYISAM 表中有三个文件:索引文件、表结构文件、数据文件;
采用非聚集索引,索引文件的数据域存储指向数据文件的指针。辅索引与主索引基本一致,但是辅索引不用保证唯一性;
INNODB
支持 ACID 的事物,支持事物的四种隔离级别;
支持行锁及外键约束,因此可以支持写并发;
不存储行总数;
一个 INNODB 引擎存储一个文件空间(共享表空间,表大小不受操作系统控制,一个表可能分配在多个文件里),也可能有多个(设置独立表空间,表大小受操作系统文件大小限制,一般为 2G);
主键索引采用聚集索引(索引的数据域存储数据文件本身),辅索引的数据域存储主键的值;因此从辅索引查找数据,需要先通过辅索引找到主键值,在访问辅索引;最好使用自增主键,防止插入数据时,为维持 B + 树结构,文件的大调整;
# CHAR 和 VARCHAR 的区别?
char 和 varchar 类型在存储和检索方面有所不同;
char 列长度固定为创建表时声明的长度,长度值范围是 1 到 255,当 char 值被存储时,他们被用空格填充到特定长度,检索 char 值时需删除尾随空格;
# 如果一个表有一列定义为 TIMESTAMP,将发生什么?
当每行被更改时,时间戳字段将获取放弃时间戳;
# 列设置为 AUTO INCREMENT 时,如果在表中达到最大值,会发生什么情况?
他会停止递增,任何进一步的插入都将产生错误;
# 怎样才能找出最后一次插入时分配了哪个自动增量?
LAST_INSERT_ID 将返回有 Auto_increment 分配的最后一个值,并且不需要指定表名称;
# BLOB 和 TEXT 有什么区别?
BLOB 是一个二进制对象,可以容纳可变数量的数据。TEXt 是一个不区分大小写的 BLOB;
BLOB 和 TEXT 类型之间的唯一区别在于 BLOB 值进行排序和比较时区分大小写,对 TEXT 值不区分大小写;
# Mysql 如何优化 DISTINCT?
DISTINCT 在所有列上转化为 GROUP BY,并于 ORDER BY 自居结合使用;
# 锁的优化策略
读写分离
分段加锁
减少锁持有的时间
多个线程尽量以相同的顺序去获取资源
不能将锁的粒度过去细化,不然可能会出现线程的加锁和释放次数过多,反而效率不如一把加一把大锁;
# 说一下乐观锁和悲观锁?
- 乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但在提交和更新的时候会判断以下在此期间别人有没有去更新这个数据
- 悲观锁:每次去拿数据的时候都人会别人会修改,所以在每次拿数据的时候都会上锁,这样别人想要拿这个数据的时候就会阻止,直到这个锁被释放
数据库的乐观锁需要自己实现,在表里面添加一个 version 字段,每次修改成功值为 1,这样每次修改的时候对比一下,自己拥有的 version 和数据库现在的 version 是否一致,如果不一致就不修改,这样就实现了乐观锁。
# 介绍一下排他锁和共享锁
数据库的增删查改操作默认都是排他锁,而查询不会加任何锁,都是行级锁
- 共享锁:对某一资源加共享锁,自身可以读取该资源,其他人也可以读取该资源(也可以继续加共享锁,即共享锁可以多个共存),但无法修改,想要修改必须等所有共享锁都释放完之后。语法为:select * from table lock in share mode
- 排他锁:对某一资源加排他锁,自身可以进行增删查改,其他人无法进行任何操作。语法为:select * from table for update
# mysql 问题排查都有哪些手段?
- 使用 show processlist 命令查询当前所有连接信息
- 使用 explain 命令查询 sql 语句执行计划
- 开启慢查询日志,查询慢查询的 sql
# 如何做 mysql 的性能优化?
- 为搜索字段创建索引
- 避免使用 select *,列出需要查询的字段
- 垂直分段分割
- 选择正确的存储引擎