面试整理

一、mysql数据库的原理
答:mysql是基于客户端与服务器的数据库管理系统,是由mysqld服务进程和一些工具程序组成,mysql客户端可以连接到该进程,并向其发送sql,mysqld负责解释及执行这些语句
二、mysql架构
答:主要分三层,分别是连接层,服务层,存储层
三、如何高效的设计数据库表结构
1.设计规范和标准化

规范和标准化是数据库设计的基础。在设计数据库表结构时,应该遵循一系列标准和规范,这些标准和规范包括但不限于:

– 表名和字段名应该易于理解和表达含义;

– 表和字段应该使用小写字母和下划线命名;

– 表中应该有一个唯一标识符字段;

– 表中应该有一个主键字段;

– 表间应该使用外键字段进行关联;

– 应该尽可能使用数据库约束(主键、唯一键、外键、默认值、非空值、检查约束等)来保证数据的完整性和准确性;

– 不使用特殊字符或关键字作为表名或字段名;

2.避免过度设计

过度设计是数据库设计中一种常见的问题,它会导致数据库结构复杂,难以维护和理解。因此,数据库设计师必须避免过度设计。

在设计数据库时,应该始终记住以下原则:

– 最小化表的数量;

– 最小化表中字段的数量;

– 最小化字段中的数据类型;

– 最小化索引的数量。

3.选择合适的数据类型

选择合适的数据类型对于提高数据库性能非常重要。当选择数据类型时,应该考虑以下因素:

– 数据类型应该尽可能小。使用较小的数据类型可以节省存储空间并提高查询的速度。例如,如果需要存储年份,使用INT更大只需要4个字节。

– 应该使用正确的数据类型。当涉及到日期和时间时,使用日期和时间类型,而不是字符串类型。当涉及到金额时,使用DECIMAL而不是FLOAT类型,因为DECIMAL能够提供更高的精度。

– 应该使用统一的数据类型。同一表中,应该使用相同的数据类型来存储相同类型的数据。

4.使用索引

索引对于提高数据库查询速度非常重要。当使用索引时,应该考虑以下因素:

– 应该只在需要时才使用索引。使用索引会增加数据插入和更新的时间,因此应该仅在查询过程中需要它们时使用。

– 应该仅对查询频繁的列使用索引。

– 应该仅为小型表使用索引。大型表的索引会占用大量存储空间并且降低查询性能。

– 应该使用联合索引来提高性能。

5.正规化数据表

正规化是减少数据冗余和维护数据完整性的过程。在设计数据库表结构时,应该遵循正规化的规则。

正规化的目标是将数据划分为多个相互关联的表,每个表仅包含其实体的唯一信息。不同的表之间使用主键和外键进行关联。

在进行正规化时,应该遵循以下原则:

– 消除重复的数据;

– 将表拆分成相对较小的表;

– 最小化对重复数据的更新操作;

– 确保每个表只包含唯一数据。

6.使用存储过程和触发器

存储过程和触发器旨在为数据库提供额外的安全性和功能。存储过程是一组SQL语句的,可以在需要时进行查询。触发器是与表相关联的一些逻辑,只要表中的一些操作发生,就会自动启动。

使用存储过程和触发器可以提高数据库的性能和可维护性。

7.备份和恢复

备份和恢复是数据库设计中最重要的部分之一。备份和恢复可以在数据库出现故障时帮助恢复数据库。

使用备份和恢复时,应该考虑以下因素:

– 应该定期备份数据库,以确保在系统故障时能够快速进行恢复;

– 应该进行备份测试,以确保备份是可用的;

– 应该在服务器上多个位置存储备份,以确保不会丢失所有备份;

– 应该测试恢复过程,以确保在需要时可以正确恢复数据库。

四、高性能数据表如何设计
范式是关系数据库理论的基础,也是我们在设计数据库结构过程中所要遵循的规则和指导方法。数据库的设计范式是数据库设计所需要满足的规范。只有理解数据库的设计范式,才能设计出高效率、优雅的数据库,否则可能会设计出低效的库表结构。

目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,还又称完美范式)。

满足最低要求的叫第一范式,简称 1NF。在第一范式基础上进一步满足一些要求的为第二范式,简称 2NF。其余依此类推。各种范式呈递次规范,越高的范式数据库冗余越小。通常所用到的只是前三个范式,即:第一范式(1NF),第二范式(2NF),第三范式(3NF)。

第一范式
第一范式无重复的列,表中的每一列都是拆分的基本数据项,即列不能够再拆分成其他几列,强调的是列的原子性.。

如果在实际场景中,一个联系人有家庭电话和公司电话,那么以“姓名、性别、电话”为表头的表结构就没有达到 1NF。要符合 1NF 我们只需把电话列拆分,让表头变为姓名、性别、家庭电话、公司电话即可。

第二范式
第二范式属性完全依赖于主键,首先要满足它符合 1NF,另外还需要包含两部分内容:

• 表必须有一个主键;
• 没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。即要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性。
第三范式
第三范式属性不传递依赖于其他非主属性,首先需要满足 2NF,另外非主键列必须直接依赖于主键,不能存在传递依赖。即不能存在:非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况。

五、sql优化的方法
避免使用select *、用union all 代替union、小表驱动大表、批量操作、多用limit、in中值太多、增量查询、高效的分页、用链接查询代替子查询、join数量不宜过多、join时需要注意、控制索引的数量、选择合理的字段类型、提升group by的效率、索引优化。

六、servlet的生命周期
Servlet的生命周期分为5个阶段:加载、创建、初始化、处理客户请求、卸载。
1.加载:容器通过类加载器使用Servlet类对应的文件来加载Servlet
2.创建:通过 调用Servlet的构造函数来创建一个Servlet实例
3.初始化:通过调用Servlet的init()方法来完成初始化工作, 这个方法是在Servlet已经被创建,但在向客户端提供服务之前调用。
4.处理客户请求:Servlet创建后就可以处理请求,当有新的客户端请求时,Web容器都会 创建一个新的线程来处理该请求。接着调用Servlet的
Service()方法来响应客户端请求(Service方法会根据请求的method属性来调用doGet()和doPost())
5.卸载: 容器在卸载Servlet之前需要调用destroy()方法,让Servlet释放其占用的资源。

七、msyql锁
MySQL中有多种锁类型,包括乐观锁、悲观锁、全局锁、表级锁、页级锁、行级锁、共享锁、排它锁、意向共享锁、意向排它锁、间隙锁、临建锁和记录锁,下面分别介绍一下各种锁:

  1. 乐观锁(Optimistic Locking):假设并发操作时不会发生冲突,只在提交事务时检查数据是否被其他事务修改过。常用于读多写少的场景。
  2. 悲观锁(Pessimistic Locking):假设并发操作时会发生冲突,因此在操作期间持有锁来避免冲突。常用于写多读少的场景。
  3. 全局锁(Global Lock):对整个数据库实例加锁,限制除了超级用户外的所有查询和修改操作。一般用于备份、恢复等操作。
  4. 表级锁(Table Lock):对整个表加锁,其他连接无法修改或读取该表的数据,但可以对其他表进行操作。
  5. 页级锁(Page Lock):对数据页(通常是连续的几个行)加锁,控制并发事务对该页的访问。适用于数据较大且并发量较高的场景。
  6. 行级锁(Row Lock):对单个行加锁,只锁定需要修改的数据行,其他行可以被同时修改或读取。并发性高,但锁管理较复杂。
  7. 共享锁(Shared Lock):也称为读锁,多个事务可以同时持有共享锁并读取数据,但不能修改数据。适用于同时读取同一数据的场景。
  8. 排它锁(Exclusive Lock):也称为写锁,事务持有排它锁时,其他事务无法同时持有共享锁或排它锁,用于保护数据的写操作。
  9. 意向共享锁(Intention Shared Lock):表级锁的辅助锁,表示事务要在某个表或页级锁上获取共享锁。
  10. 意向排它锁(Intention Exclusive Lock):表级锁的辅助锁,表示事务要在某个表或页级锁上获取排它锁。
  11. 间隙锁(Gap Lock):锁定一个范围的键,但不包括这些键的实际值。用于防止其他事务在范围内插入数据。
  12. 临建锁(Metadata Lock):锁定数据库对象的元数据,如表结构,用于保证数据定义的一致性。
  13. 记录锁(Record Lock):行级锁的特定类型,锁定单个行,确保其他事务无法同时修改或读取该行。

这些锁类型在不同的场景中可以组合使用,根据具体需求选择适当的锁策略来保证数据的一致性和并发性。同时,需要注意锁的粒度、锁冲突、死锁等问题,合理设计和管理锁可以提高数据库的并发性能。

八、四种常见的线程池:
CachedThreadPool:可缓存的线程池,该线程池中没有核心线程,非核心线程的数量为Integer.max_value,就是无限大,当有需要时创建线程来执行任务,没有需要时回收线程,适用于耗时少,任务量大的情况。

SecudleThreadPool:周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程,非核心线程的大小也为无限大。适用于执行周期性的任务。

SingleThreadPool:只有一条线程来执行任务,适用于有顺序的任务的应用场景。

FixedThreadPool:定长的线程池,有核心线程,核心线程的即为最大的线程数量,没有非核心线程

sbringboot项目中用@EnableAsync开启线程池,用@Async注解标识需要异步的程序

九、线程的生命周期
线程生命周期,总共有五种状态:

1) 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();

2) 就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;

3) 运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

4) 阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态;

5) 根据阻塞产生的原因不同,阻塞状态又可以分为三种:

a) 等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;

b) 同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

c) 其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

6) 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

你可能感兴趣的:(程序员)