Java面试小结(二)

关于数据库方面的问题,参考博客数据库

1.DDL DCL DML DQL DTL

  • DQL(Data Query Language) - 数据查询语言 - sql的核心
    select

  • DML(Data Manipulation Language) - 数据操纵语言
    insert update delete

  • DDL(Data definition Language) - 数据定义语言
    create alter drop truncate

  • DCL(Data Control Language) - 数据控制语言
    grant[授权] revoke[取消授权]

  • DTL(Data Transaction Language) - 数据事务语言
    commit savepoint rollback

2.主键 外键 左连接 右连接

  • 主键的关键字是primary key,用于唯一标识表中的行数据,不能为空,一个主键值对应一行数据.另外,会自动在主键上创建索引,加快查询速度.

  • 外键的关键字是foreign key,用于标识两个表之间的联系,两个表必须具有相同类型的属性,在该属性上有相同的值.

  • 左连接的关键字是left join,假设有两张表,分别是A表和B表,以A表为基准表,如果A表的记录在B表不存在,也能够查询出来.

  • 右连接的关键字是right join,假设有两张表,分别是A表和B表,以B表为基准表,如果B表的记录在A表不存在,也能够查询出来.

3.DB,DBMS,RDBMS,DBS,DBA

  • DB - Database - 数据库
  • DBMS - Database Management System - 数据库管理系统 - 数据库是通过DBMS来创建和操作的.
  • RDBMS - Relational Database Management System - 关系数据库管理系统
  • DBA - Database Administrator - 数据库管理员
  • DBS - Database System - 数据库系统 = DB + DBMS + DBA

4.事务

事务是指一组相关的SQL操作,我们所有的操作都是处在事务中的,执行业务的基本单位不是sql,而是事务.

命令

  • begin - 标记事务的开始
  • commit - 提交事务
  • rollback - 回滚事务
  • save - 设置事务可以回滚到的保存点

四大特性

  • Atomicity原子性 - 事务不可再分割,要么同时成功,要么同时失败.

  • Consistency一致性 - 事务一旦结束,内存中的数据和数据库中的数据是保持一致的.

  • example:A用户和B用户进行转账1000,A账号减少1000,B用户增加1000,无论如何执行,两个账户的金额改变加起来一定是1000.

  • Isolation隔离性 - 事务之间相互独立,互不干扰

  • Durability持久性 - 事务一旦提交,数据会持久化到数据库中永久保存.

隔离级别

  • 读未提交[READ UNCOMMITED]
    事务最低的隔离级别,允许另外一个事务看到这个事务未提交的数据

  • 读已提交[READ COMMITED]
    保证一个事务修改的数据提交后才能被另外一个事务读取,即另外一个事务不能读取该事务未提交的数据.

  • 可重复读[REPEATABLE READ] - mysql默认事务隔离级别
    保证一个事务相同情况下前后两次读取的数据是一致的.

  • 串行化[SERIALIZABLE]
    事务被处理为顺序执行,有点类似于锁表,但性能很低.

事务隔离级别 脏读 不可重复读 幻读
读未提交
读已提交 ×
可重复读 × ×
串行化 × × ×

多事务并发问题

  • 脏读
    事务 A 读取了事务 B 更新的数据,然后 B 回滚操作,那么 A 读取到的数据是脏数据

  • 不可重复读
    一个事务多次读取同一条记录产生不同的结果
    事务A正在读取数据库中的记录,事务B正在修改数据库的事务并提交,事务A在事务B提交前和提交后读取到的记录结果不同,导致原来的数据变成不可重复读的数据.

  • 幻读
    一个事务修改表中的多行记录,一个事务往表里插入记录,第一个事务查询时发现仍旧有未更新的记录(新插入的),好像出现了幻觉一样

不可重复读的重点是一个事务查一个事务修改,而幻读的重点是一个事务更新,一个事务插入.

5.编程式事务/声明式事务

  • 编程式事务 - 通过编码的方式实现事务,导致事务和业务的代码耦合在一起,类似JDBC编程实现事务管理.
1
2
3
4
5
6
7
8
9
10
try { 
.....
connection.setAutoCommit(false);
.....
// 一连串SQL操作
connection.commit();
} catch(SQLException) {
// 发生错误,撤消所有变更
connection.rollback();
}
  • 声明式事务 - 建立在AOP之上,最大的优点是不需要通过编程的方式管理事务,不需要在业务逻辑代码中掺杂事务管理的代码,只需要在配置文件中做相关的申明,或通过@Transactional注解的方式就可以将事务规则应用到业务逻辑中

6.悲观锁和乐观锁

悲观锁
总是假设最坏的情况,每次取数据都认为别人会修改,所以在每个操作上都进行上锁. Java的synchronized和reentrantlock等独占锁就是悲观锁.

乐观锁
总是假设最好的情况,每次那数据都认为别人不修改,所以不上锁,但是在更新的时候会判断一下在此期间有没有人去更新这个数据.

悲观锁和乐观锁的使用场景

  • 悲观锁适用于写的情况比较多的场景.
  • 乐观锁适用于读的情况比较多的场景.

乐观锁的实现方式

  • 版本号机制
    在数据表中加上一个版本号version字段,表示数据被修改的次数,当数据被修改时,version的值会加一.

假设现在同时有线程A和B对数据库的同一行数据进行操作,此时线程A读取版本号version=1然后进行数据更新,在线程A操作的同时线程B也读取版本号version=1并进行修改.线程A提交时先查看数据库的版本是否和自己读取的版本一致,发现是一致的,完成提交.

  • CAS算法 - compare and swap(比较与交换)
    CAS算法是一种有名的无锁算法,即在不使用锁的情况下实现多线程之间的变量同步.它涉及到三个操作数

  • 需要读写的内存位置V- 主存

  • 需要进行比较的预期值A - 主存中V的旧值

  • 需要写入的新值U

主内存中的值为V,拷贝一份主内存的值到工作内存中为A, 更新后的值为U,当线程更新值时首先判断工作内存中A的值和主存中的V进行对比,如果相同才可以提交.如果不相同就会写不进去,会自旋,重新拉取主存的值给A

7.myisam和innodb的区别

  • myisam不支持事务,innodb支持事务

  • myisam支持表锁,innodb支持行锁.

  • myisam不支持外键,innodb支持外键

  • 在索引方面myisam和innodb有很大区别

    • myisam中索引文件和数据文件是分开的独立的两个文件,innodb中索引文件和数据文件合二为一,只有一个文件.
    • myisam中的聚簇索引和非聚簇索引的方式都是一致的,叶子结点存储的是索引的值和索引文件物理行地址.innodb中的聚簇索引的叶子结点存储的是索引的值和行记录,非聚簇索引的叶子结点存储的是索引的值和主键的值

8.索引

数据结构
二叉树
二叉树的缺点 - 可能会形成链表,导致查询效率很低.

平衡二叉树
基于二叉树的缺点改进为平衡二叉树,平衡二叉树每个节点的左右子树的高度差不能超过1.

平衡二叉树的缺点

  • 平衡二叉树是每个节点只存储一个键值和数据,每个磁盘块只存储一个键值和数据.
  • 如果要存储的数据很多,二叉树的节点将会很多,高度也会极高,导致进行很多次的磁盘IO交互,查找的效率将会很低.

B树
每查找一次数据就需要从磁盘中读取一个节点,也就是我们说的一个磁盘块.读取的单位是页 - 1页的磁盘块的数据大小是16kb,每个节点可以有更多的key-value

B树单个节点存储多个键值和数据

B树的优点 - 查找数据读取磁盘的数据将会很少,数据的查找效率也会比二叉树高很多.

B树的缺点 - 因为每个节点存储的都是key值和行记录,如果行记录很多的话会导致存储的key值很少.

B+树
B+树的非叶节点仅仅保存了key值,所以每个非叶节点可以保存更多的key,而叶子节点存储所有的数据.

这又分为聚簇索引和非聚簇索引,聚簇索引的叶子结点存储的索引列值和行记录,非聚簇索引的叶子结点存储的是索引列值和主键值.

非聚簇索引在查询时首先查询到主键值再根据主键值到聚簇索引中找到行记录,这个过程称之为回表.但是不一定会回表,如果查询的列就是索引列或者是主键列,就不需要回表.

以innodb作为存储引擎的表,表中的数据都会有一个主键,即使不创建主键,系统也会创建一个隐式的主键.

9.索引失效场景/如何提高sql查询效率

  • 违反最左原则,查询语句where最左边的列一定要和创建复合索引的第一个列保持一致.
  • 在范围之后索引列会失效
  • 索引列进行模糊查询且”%”不是出现在末尾.
  • 索引列使用函数或参与计算
  • 索引列参与运算[not in / is null]
  • mysql在使用!= <>的时候无法使用索引
  • 字符串不加单引号导致索引失效
  • 在查询时使用or连接查询条件

建立索引的策略

  • 索引不是越多越好,因为索引 也是需要占用空间的,需要进行维护.
  • 推荐建立索引的列 - 主键列/唯一列;不经常改变的列;满足以上条件并且经常作为查询条件的列
  • 不建议建立索引的列 - 重复值太多的列;null值太多的列

为什么可以提高查询效率 - 解释B+树的查询过程

10.sql优化

  • 避免在where子句中使用!=或者<>操作符,否则将进行全表扫描.
  • 避免在where子句中使用or来连接条件
  • 避免使用is null
  • 避免使用not in
  • 避免使用索引列进行计算
  • 避免对索引列使用函数
  • 遵循最左原则
  • 避免使用select /,用具体的字段列表替代 /
  • 避免使用order by,因为order by效率很低.
  • 针对于limit可以使用where语句配合使用

11.truncate和delete,drop区别

用法和区别

  • drop table 表名 - 删除内容和定义,并释放空间,执行drop语句会把表结构一起删除.
  • truncate table 表名 - 删除内容,释放空间但不删除定义(保留表的数据结构).
  • delete from 表名 where 列名 = 值 - 与truncate相似,delete也只删除内容,释放空间但不删除定义.

区别

  • 执行速度一般是drop>truncate>delete

  • delete语句是DML操作,可以回滚.

  • truncate和drop是DDL,操作立即失效,无法回滚.

  • truncate会重置表的自增值,delete不会.

  • truncate不能操作视图,其他的都可以

12.三大范式

  • 1NF - 保证列的原子性,列不可再分割

  • 2NF - 基于1NF,不存在非关键列部分依赖于关键列 [非关键列部分依赖于关键列的一部分]

1
2
场景:有一张表sc(sid,same,cid,cname);
这里的主键应该是复合主键(sid,cid),其中非关键列sname就只依赖于sid,也就是说非关键列部分依赖于关键列,不符合2NF
  • 3NF - 基于2NF,不存在非关键列传递依赖于关键列
1
2
场景:由一张表student(sid,sname,sage,schoolid,schoolname);
这里的主键是sid,但是存在非关键列schoolname依赖于schoolid,schoolid依赖于sid,所以说是非关键列不是直接依赖于关键列,而是传递依赖于关键列.

13.单行函数

字符串函数

  • substring(str,pos,len) - 返回字符串str的位置pos起len个字符.
  • substring(str,pos) - 返回字符串str的位置pos起后面的子串
  • replace(str,from_str,to_str) - 用字符串to_str替换字符串str中的子串from_str并返回
  • char_length(str) - 不管是汉字还是数字或者字母都算是一个字符
  • lenght(str) - 汉字占3个其他占1个

数字函数

  • floor(n) - 返回不大于n的最大整数值
  • ceiling(n) - 返回不小于n的最小整数值
  • round(n[,d]) - 返回n的四舍五入值,保留d位小数(d的默认值为0)
  • truncate(n,d) - 保留数字n的d位小数并返回 - 直接截取

日期函数

  • select now(); - 返回当前系统的日期

  • date_format(date,format) - 把日期转换成指定模板的字符串

1
2
3
4
5
6
7
8
%Y - 四位数的年份
%y - 两位数的年份
%d - 月份中的天数(00...31)
%m - 数字(01...12)
%H - 24小时制小时(00...23)
%h - 12小时制小时(01...12)
%i - 分钟(00...59)
%s - 秒钟(00...59)
1
mysql> select start_date,date_format(start_date,'%Y-%m-%d %H:%i:%s') from s_emp;
  • 字符串转日期,mysql中只要字符串满足默认支持的格式默认会转为date类型.
    例如2021/10/12 2019-09-08 20181106
    如果不是默认支持的格式 - str_to_date(‘05/11/2023’,’%d%m%Y’)

14.group by / order by / 多行函数 / 组函数

group by/order by

语法select 语句 where 语句 group by 语句 [having 组函数判断] order by 语句
order by默认是升序,asc是升序,desc是降序.

多行函数/组函数/聚合函数

count / avg / sum / min / max

15.视图的定义和优点

视图是由数据库中的一个表或者多个表导出的虚拟表,是一种虚拟存在的表,方便用户对数据的操作.

视图的作用

  • 保证数据的安全性
  • 封装一些比较繁琐的关联查询,在以后再次用到的时候就可以直接查询视图
  • 简化查询数据

关于视图

1
2
3
4
-- 创建视图
create view 视图名 as select 语句
-- 删除视图
drop view 视图名

简单视图就是单张表的视图,可以对简单视图进行DML操作,同样会对原表产生影响.复杂视图就是多图关联查询导出的视图,不能对复杂视图进行DML操作.

16.存储过程的定义语法和优点

存储过程是为了完成一些特定的功能,提前将sql预编译好,存储在mysql-server端系统的执行计划表中,第一次去调用存储过程的时候会对sql进行预编译并且进行保存,第二次再去调用的时候,省去了sql语句的编译过程.

优点

  • 增强SQL语言的功能和灵活性 - 存储过程可以用控制语句编写,有很强的灵活性,可以完成复杂的判断和较复杂的运算.
  • 标准组件式编程 - 存储过程被创建后,可以在程序中被多次调用,而不必重新编写该存储过程的SQL语句.
  • 执行速度比较快 - 因为存储过程是预编译的,速度比批处理的执行速度快

17.存储过程和函数的区别

存储过程的实例

1
2
3
4
5
6
7
8
9
10
11
12
13
-- 删除存储过程
drop procedure sal_pro;
-- 创建存储过程
delimiter //
create procedure sal_pro()
begin
-- 封装sql语句
select avg(salary) from s_emp;
end //
delimiter //

-- 调用存储过程
call sal_pro();

函数的区别

1
2
3
4
5
6
7
8
9
10
11
12
-- 删除函数
drop function my_date;
-- 创建函数
delimiter //
create function my_date(dt date)returns varchar(20)
begin
return date_format(dt,'%Y年%m月%日');
end //
delimiter ;

-- 调用函数
select my_date('2010-09-09')

存储过程和函数的区别

  • 定义函数的时候必须制定returns返回类型,定义存储过程的时候不需要使用returns返回类型.
  • 函数体中必须要有return语句+返回结果,但是存储过程可以没有return语句
  • 函数的调用使用select关键字,存储过程的调用使用call关键字
  • 存储过程更加侧重于封装以及预编译sql语句,提高sql语句的复用性.
  • 存储过程是用in来接受参数,out来返回结果.

18.触发器

在mysql中,当我们执行一些操作的时候[比如dml操作],一旦事件被触发就会执行一段程序.

触发器的本质上就是一个特殊的存储过程.

before触发器[前置触发器] - 在触发条件之前执行

after触发器[后置触发器] - 在触发条件之后执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- 删除account表中的任意一条数据的时候[触发时间],并且把这条数据放到备份表中acc_copy
-- 删除触发器
drop trigger acc_tri;
-- 创建触发器
delimiter //
create trigger acc_tri
after delete on account
for each row
begin
-- old.列名 - 刚刚删除的那条数据
insert into acc_copy values(old.id,old.name,old.balance);
end //
delimiter;

-- 触发器是遇到触发的事件才会执行的,不是我们手动去调用的.
delete from account where id = 1;

19.如何自己使用mysql进行分页操作

limit (pageNow-1)*pageSize,pageSize

limit缺点 - 如果偏移量很大,性能就会降低

20.数据库的数据类型

数值类型

tinyint 1 Bytes
smallint 2 Bytes
mediumint 3 Bytes
int或者integer 4 Bytes
bigint 8 Bytes
float 4 Bytes
double 8 Bytes
decimal 对DECIMAL(M,D) ,如果M>D,为M+2否则为D+2

日期和时间类型

类型 大小 ( bytes) 范围 格式 用途
DATE 3 1000-01-01/9999-12-31 YYYY-MM-DD 日期值
TIME 3 ‘-838:59:59’/‘838:59:59’ HH:MM:SS 时间值或持续时间
YEAR 1 1901/2155 YYYY 年份值
DATETIME 8 1000-01-01 00:00:00/9999-12-31 23:59:59 YYYY-MM-DD HH:MM:SS 混合日期和时间值
TIMESTAMP 4 1970-01-01 00:00:00/2038结束时间是第 2147483647 秒,北京时间 2038-1-19 11:14:07,格林尼治时间 2038年1月19日 凌晨 03:14:07 YYYYMMDD HHMMSS 混合日期和时间值,时间戳

字符串类型

类型 大小 用途
CHAR 0-255 bytes 定长字符串
VARCHAR 0-65535 bytes 变长字符串
TINYBLOB 0-255 bytes 不超过 255 个字符的二进制字符串
TINYTEXT 0-255 bytes 短文本字符串
BLOB(图片存储方式可以blog) 0-65 535 bytes 二进制形式的长文本数据
TEXT 0-65 535 bytes 长文本数据
MEDIUMBLOB 0-16 777 215 bytes 二进制形式的中等长度文本数据
MEDIUMTEXT 0-16 777 215 bytes 中等长度文本数据
LONGBLOB 0-4 294 967 295 bytes 二进制形式的极大文本数据
LONGTEXT 0-4 294 967 295 bytes 极大文本数据

21.约束的类型

  • not null - 非空约束
  • unique - 唯一约束
  • primary key - 主键约束
  • foreign key - 外键约束
  • check - 检查约束