1.数据库如何设计可以提高性能
设计原则 :1以高性能为目标,库表的设计应该以范式为主,根据实际的业务场景使用非范式,以空间换取时间。
2.统一规范的命名方式,规范的命名提交可读性。
3.根据业务需要设计字段时,合适的字段,合适的长度。
三大范式:
1.保证字段(列)的原子性。所有字段都是不可拆分的原子值 举例:比如 某个表需要有 '地址’这个字段,我们不应该用一个字段去记录它,应该拆分成 省,市,县,详细地址4个字段去保存。
2.确保每列数据都和主键有关联,保证我们设计的库表有主键。 举例:比如一个订单表 如果把订单信息和商品信息全部记录在一张表的话,那对于商品信息,商品价格和该表订单主键就没有关联,这就违背了第二范式原则。我们应该把这个表拆分成 订单表,商品表,中间表 (记录订单和商品的对应关系)。
3.确保每列数据都和主键是直接关联而不是间接关联, 举例:在设计订单表中,我们应该将客户的编号或者ID作为外键关联到订单表中,而不可以在订单表中添加客户其他信息(名字,电话)等字段,因为这些字段是和客户编号关联,客户编号在和订单编号关联,就是间接关联,这就违背了第三范式。
2.Mysql中存储引擎的区别
Mysql常见的引擎有 InnoDB,MyISAM和 MEMORY ,其中区别提现在事务安全、存储限制、空间使用、内存使用、插入数据速度和外键的支持 。
1.事务安全 InnoDB 支持事务安全,MYISAM 和MEMORY不支持事务安全。
2.存储限制 InnoDB有64TB的存储限制,其他2种看情况而定
3.空间使用 InnoDB对空间使用程度比较高,其他对空间使用程度比较低
4.内存使用 InnoDB和MEMORy对内存使用程度较高,MyISAm使用程度比较低
5.插入数据速度:InnoDB插入数据速度比较慢,其他2中比较快
6.对外键的支持:InnoDB支持外键,其他2种不支持外键
三种引擎特点如下:
1、InnoDB存储引擎
InnoDB是事务型数据库的首选引擎,支持事务安全表(ACID),其它存储引擎都是非事务安全表,支持行锁定和外键,MySQL5.5以后默认使用InnoDB存储引擎。
InnoDB特点: 支持事务处理,支持外键,支持崩溃修复能力和并发控制。如果需要对事务的完整性要求比较高(比如银行),要求实现并发控制(比如售票),那选择InnoDB有很大的优势。
如果需要频繁的更新、删除操作的数据库,也可以选择InnoDB,因为支持事务的提交(commit)和回滚(rollback)。
2、MyISAM存储引擎
MyISAM基于ISAM存储引擎,并对其进行扩展。它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。MyISAM拥有较高的插入、查询速度,但不支持事务,不支持外键。
MyISAM特点: 插入数据快,空间和内存使用比较低。如果表主要是用于插入新记录和读出记录,那么选择MyISAM能实现处理高效率。如果应用的完整性、并发性要求比较低,也可以使用
3、MEMORY存储引擎
MEMORY存储引擎将表中的数据存储到内存中,为查询和引用其他表数据提供快速访问。
MEMORY特点: 所有的数据都在内存中,数据的处理速度快,但是安全性不高。如果需要很快的读写速度,对数据的安全性要求较低,可以选择MEMOEY。
它对表的大小有要求,不能建立太大的表。所以,这类数据库只使用在相对较小的数据库表。
高并发问题:
1.避免频繁的new对象,对于整个应用存在一个实例的情况下,我们可以使用单例模式。对于String连接操作,使用 StringBuffer或StringBuilder,对于工具类可以通过静态方法来访问。
2.使用java中效率高的类,比如ArrayList比Vector性能好。
ArrayList 和 Vector 的区别是什么?:
Vector是同步的,而ArrayList不是。然而,如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWriteArrayList。?
ArrayList比Vector快,它因为有同步,不会过载。
vector是线程安全的,arrayList不是
ArrayList更加通用,因为我们可以使用Collections工具类轻易地获取同步列表和只读列表。
3.用redis缓存 减轻对数据库的访问。
4.DB优化
``a、在数据库设计的时候就要考虑到后期的维护,数据库三范式是我们设计数据库索要遵循的原则。 ``b、索引的建立:建立索引要适当,如果一个表经常用来被查询,对于增加和修改很少被用到,我们就可以为这个表建立索引,因为对于增加和修改和删除操作时,我们对索引的维护要大大超过索引给我们带来的效率。 ``c、表字段的类型选择要恰当 包括字段的长度、类型等,要根据实际存储的数据进行选择,长度不要过长,否则会影响效率。
redis
1.Redis支持的数据类型? String Hash List Set zset
2.Redis 提供了两种持久化方式:RDB(默认) 和AOF
RDB: 是将redis某一时刻的数据持久化到磁盘中,是一种快照式的持久化方法 , 就是当在保存命令(rdbsave)之前 服务宕机的化,数据会丢失
功能核心函数rdbSave(生成RDB文件)和rdbLoad(从文件加载内存)两个函数
AOF: 以日志形式记录每一个写入或者删除操作
比较:
1、aof文件比rdb更新频率高,优先使用aof还原数据。
2、aof比rdb更安全也更大
3、rdb性能比aof好
4、如果两个都配了优先加载AOF
缓存穿透:外部使用不存在的用户数据请求接口,导致缓存不命中,然后穿透到DB依然没有数据,导致大量请求穿透缓存访问到DB,导致数据库异常。
解决:1.利用布隆过滤器将所有可能存在的数据HASH值保存在大的BITMAP中,一个一定不存在的数据就会被bitmap给拦截
2.若查询结果为空,把空结果也进行缓存,对这个设置过期时间。
缓存雪崩:即缓存一时间大面积失效或者缓存服务宕机了,导致大量请求都怼到DB上,导致数据库异常。
解决:1.给缓存的失效时间在加上一个随机值,避免大面积失效。
2.使用主从模式 尽量保证缓存的高可用。
3.增加互斥锁,控制数据库请求,重建缓存。
主从模式: 实现主从复制(Master-Slave Replication)的工作原理:Slave从节点服务启动并连接到Master之后,它将主动发送一个SYNC命令。Master服务主节点收到同步命令后将启动后台存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕后,Master将传送整个数据库文件到Slave,以完成一次完全同步。而Slave从节点服务在接收到数据库文件数据之后将其存盘并加载到内存中。此后,Master主节点继续将所有已经收集到的修改命令,和新的修改命令依次传送给Slaves,Slave将在本次执行这些数据修改命令,从而达到最终的数据同步。
HashMap:HashMap是基于哈希表(散列表),实现Map接口的双列集合,数据结构是“链表散列”,也就是数组+链表 ,key唯一的value可以重复,允许存储null 键null 值,元素无序。当我们往Hashmap中put元素时,首先根据key的hashcode重新计算hash值,根绝hash值得到这个元素在数组中的位置(下标),如果该数组在该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放入链尾.如果数组中该位置没有元素,就直接将该元素放到数组的该位置上。
设计模式:
抽象工厂设计模式: 一个颜色接口 ,实现这个接口的有 绿色 和红色,一个动物接口 ,实现接口有狗和猫,创建一个抽象工厂的抽象类 ,里面2个方法返回类型是颜色接口和动物接口,都有一个参数分别代表你是绿色还是红色的类。 在分别创建 颜色工厂类和动物工厂类继承这个抽象工厂类,颜色工厂实现创建颜色接口的方法。然后在写一个工厂创造器 提供一个方法 返回类型是抽象工厂类,根据参数返回对应的工厂对象实例,比如 颜色就new 一个颜色工厂,然后通过这个对象 调用具体的对象,比如输入红色就会new 一个红色对象。
单例模式:优点 减少内存的开销。让构造函数私有化,提供一个静态方法获取该对象。
懒汉式:线程不安全 判断该类的静态参数是否为NULL,是的话new一个对象赋值给他并返回该对象。
懒汉式:线程安全 缺点就是不支持多线程 ,实现:在静态方法上加上synchronized关键字.
饿汉式:线程安全 在初始化时就把静态变量new 出来 ,在静态方法直接返回该对象
代理模式: 解决的是无法直接访问对象,或者访问对象会出现安全问题。 比如A对象为原对象 我创建一个代理对象B ,AB都实现同一个接口C,AB都重写C接口方法,B里面可以对A的方法进行调用,并可以加上一些日志输出,或者执行前和执行后的操作。
HTTP的3次握手:
第一次 :建立连接时,客户端发送确认包syn(syn=j) ,并进入待服务器确认状态。
第二次:服务器收到syn包后,确认客户端的syn包 方式ack(ack=j+1),并发送自己的syn包(syn=k),即syn+ack包,服务器进入syn_recv状态。
第三次:客户端收到服务器的syn+ack包,向服务器发送确认包(ack=k+1),发送完毕后客户端和服务端同时进入 established(建立连接)状态
TCP和UDP区别:
1.TCP是面向连接的协议,而UDP是无连接的协议。
2.TCP是可靠信道,UDP则是不可靠信道。
3.实时性 :UDP具有较好的实时性,工作效率比TCP高
4.TCP速度比较慢,而UDP速度比较快
5.TCP面向字节流,UDP是面向报文的 。
RABBITMQ:、什么是RabbitMQ?为什么使用RabbitMQ?
RabbitMQ是一款开源的基于AMQP协议的消息中间件;可以用它来:解耦、异步、削峰。
RabbitMq几种交换机模式 :
**1.Fanout 扇形交换机 ** 广播模式:不处理路由键。你只需要简单的将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上
2.Direct 直连交换机 完全匹配模式。需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。这是一个完整的匹配
3 Topic 主题交换机 将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词
4 header 首部交换机 路由器和交换机路由的规则是通过Headers
信息来交换的,这个有点像HTTP
请求中的请求头。将一个交换机声明成首部交换机,绑定 一个队列的时候,定义一个Hash
的数据结构,消息发送的时候,会携带一组hash
数据结构的信息,当Hash
内容匹配上的时候,消息就会被写入队列。
开发流程:
1、项目启动
1)、项目组成立(公司成员、客户成员)
2)、制定项目预期目标
3)、制定项目计划周期
4)、建立好项目组成员沟通机制
2、需求调研
1)、创建调研计划、协调调研时间
2)、收集客户资料,获取客户需求
所有的资料都需要保留一份,资料中存疑的需要及时询问
3)、编写需求文档
重点描述出客户的业务流程和性能要求。
采用Word、Excel、Rose等形式。
4)、需求变更记录
5)、确定开发环境和运行环境
6)、扩展性要求
7)、与旧系统的接驳要求。
8)、估算出项目工作量
本阶段需要一套需求管理系统来进行需求的管理。
本阶段的需求文档也是用户测试的依据。
3、系统设计/详细设计
一个系统可以分为基础平台和应用模块两部分。
1)、选择基础平台,无论是采用第三方平台还是自行开发平台,都需要深入了解,查看是否符合要求。
2)、应用模块设计(针对业务流程)
3)、中间件的采用或自行开发,需要深入了解。
4)、用户界面的设计
如果用户界面设计完毕并确认,即可初步写出用户使用手册、管理员使用手册。
5)、变更记录
本阶段的系统设计是集成测试的依据。
4、程序开发
创建开发任务计划表、开发计划日程表
1)、优先编写测试用例
2)、按照编码规范编写代码
3)、按照文档注释规范注释
以上形成开发文档。
本阶段需要一套版本管理系统。
本阶段的测试用例也是单元测试的依据。
如果能做到,最好每日构建。
5、测试
本阶段需要一套Bug管理系统,形成需求、设计、开发、测试互动。
1)、编写测试计划和测试方案
2)、功能测试
单元测试、集成测试
3)、性能测试
集成测试、压力测试
如果能做到,最好能进行自动化测试。
如果能做到,做分析统计工作。
最后形成测试报告。
6、试用、培训、维护
本阶段需要解决:
1)、解决异地修改和公司修改的同步问题。
2)、用户测试中的Bug修改问题,按照级别分为
a)、程序Bug
b)、设计变更
c)、需求变更
尽量按照a b c的顺序来进行修改,尽量避免b、c级的修改。
最后形成安装手册、维护记录。
Mybatis分页: 原理
1.sql分页:sql语句的limit和count实现分页
2.拦截器分页
创建拦截器,拦截mybatis接口方法id以ByPage结束的语句,并对sql语句进行拼装
3.RowBounds分页
Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
Mybatis分页深度优化sql:
原来sql: select * from user limit 50000,10
优化sql : select * from user where id>(select id form user limit 50000,1) limit 0,10;
死锁 : 当线程A持有独占锁a,并尝试去获取独占锁b的同时,线程B持有独占锁b,并尝试获取独占锁a的情况下,就会发生AB两个线程由于互相持有对方需要的锁,而发生的阻塞现象,我们称为死锁。
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
解决:(1)按同一顺序访问对象。
(2)避免事务中的用户交互。
(3)保持事务简短并在一个批处理中。
(4)使用低隔离级别。
Spring事务 @Transactional注解
@Transactional注解可用参数:readOnly只读,timeout超时,propagation添加事务的传播行为,Isolation添加隔离级别
事务有4大特性:原子性,一致性,隔离性,持久性
事务的隔离级别:读未提交(脏读,不可重复读,幻读) ,读已提交(不可重复读,幻读),可重复读(幻读),串行化。
**事务的配置方式 **:1.编程式事务: 使用TransactionTemplate对编程式事务管理。
2.声明式事务: 声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目 标方法之后根据执行的情况提交或者回滚。实现:在方法加上@transactional注解即可
事务的传播机制:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K2FsTZoF-1621909092949)(C:\Users\Administrator\Desktop\05-21\0430b0681fc0392e0a36073b68df116.png)]
悲观锁和乐观锁区别:
悲观锁:顾名思义就是很悲观,每次去拿数据的时候都任务别人会修改数据,所以在每次拿数据的时候都会加上锁。MySQL InnoDB中使用悲观锁。
乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据.
版本号实现乐观锁:在表中的数据进行操作时(更新),先给数据表加一个版本(version)字段,每操作一次,将那条记录的版本号加1。也就是先查询出那条记录,获取出version字段,如果要对那条记录进行操作(更新),则先判断此刻version的值是否与刚刚查询出来时的version的值相等,如果相等,则说明这段期间,没有其他程序对其进行操作,则可以执行更新,将version字段的值加1;如果更新时发现此刻的version值与刚刚获取出来的version的值不相等,则说明这段期间已经有其他程序对其进行操作了,则不进行更新操作。
CAS实现乐观锁:CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作
ABA问题:比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。
线程1准备用CAS将变量的值由A替换为B,在此之前,线程2将变量的值由A替换为C,又由C替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了,尽管CAS成功,但可能存在潜藏的问题
内存异常重点排查以下几点::
1.检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
2.检查代码中是否有死循环或递归调用。
3.检查是否有大循环重复产生新对象实体。
4.检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中 数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
5.检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。
delete 、drop、truncate区别: truncate和不加where条件的delete一样都是删除表中所有数据 ,而drop是删除表。
delete是DML,删除的数据将存储在系统的回滚字段中,需要的时候数据可以回滚恢复。
truncate和drop是DDL ,删除的操作是立即生效的,不能回滚,数据不可以回滚恢复。
在速度来说 drop>truncate>delete。
DML与DDL:DML:这个是数据操作语言,sql中处理数据统称为数据操作语言,关键字有:select 、update、delete、insert。
DDL:数据定义语言,关键字:create,alter,drop,truncate等。
区别:DML是可以手动控制事务的开启,提交和回滚的 ,DDL操作是隐性提交的,不能回滚。
SpringBoot自动注入原理:
1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
2、调用List configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
3、利用工厂加载 Map<String, List> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
4、从META-INF/spring.factories位置来加载一个文件。
默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
主键索引和其他索引的区别:
1、普通索引是最基本的索引类型,没有任何限制,值可以为空,仅加速查询。普通索引是可以重复的,一个表中可以有多个普通索引。
2、主键索引是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值;索引列的所有值都只能出现一次,即必须唯一。简单来说:主键索引是加速查 询 + 列值唯一(不可以有null)+ 表中只有一个。
多线程面试题:
1.多线程有几种实现方法,都是什么?同步有几种实现方法。
继承Thread类,重写run方法
实现Runnable接口,重写run方法
通过线程池创建线程
2.sleep()和wait()有什么区别.
sleep()是Thread的方法,wait()是Object的方法
sleep()方法可以在任何地方使用 ,wait()方法只有在synchronized方法或者synchronized块中使用
3.线程的基本概念和生命周期。
线程:是进程中的一个执行控制单元,执行路径.
新建状态,就绪状态(调用start()方法),运行状态(获得CPU时间运行run()方法),阻塞状态(调用sleep()方法或者wait()方法使线程阻塞) ,死亡状态(线程执行完或者因为异常退出了run()方法,线程结束)
集合面试题
1: HashMap和Hashtable的区别。 HashMap是线程不安全的,HashTable是线程安全,HashTable的效率比HashMap高. HashMap的key和value都可以存null,HashTable不可以 2:Collection 和 Collections的区别。 Collection是集合类的顶级接口,他提供了对集合类通用的基本方法,他是很多集合类的实现接口,比如List,Set,List有ArrayList,Vector,LinkedList.Set有HashSet等。 Collections是集合类的工具类,提供一系列静态方法,用于对集合的排序,搜索等。比如有sort排序,Copy拷贝,max,min等等 3: List, Set, Map是否继承自Collection接口? List和Set是,Map不是 4:说出ArrayList,Vector, LinkedList的存储性能和特性? ArrayList和vector底层是由数组实现的,他们的查询速度较快,而vector的方法添加了synchronized,所以vector是线程安全的,arrayList是线程不安全的。arrayList的速度比vector快。 LinkedList底层是由链表实现的,所以他的增删速度比较快。
IO面试题
什么是java序列化,如何实现java序列化? 序列化:把Java对象转换为字节序列的过程。反序列化:把字节序列恢复为Java对象的过程。 用途: 对象的序列化主要有两种用途: Java的JavaBeans:Bean的状态信息通常是在设计时配置的,Bean的状态信息必须被存起来,以便当程序运行时能恢复这些状态信息,这需要将对象的状态保存到文件中,而后能够通过读入对象状态来重新构造对象,恢复程序状态。 序列化API java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。只有实现了Serializable和Externalizable接口的类的对象才能被序列化。 java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
va
什么是java序列化,如何实现java序列化?
序列化:把Java对象转换为字节序列的过程。反序列化:把字节序列恢复为Java对象的过程。
用途: 对象的序列化主要有两种用途:
Java的JavaBeans:Bean的状态信息通常是在设计时配置的,Bean的状态信息必须被存起来,以便当程序运行时能恢复这些状态信息,这需要将对象的状态保存到文件中,而后能够通过读入对象状态来重新构造对象,恢复程序状态。
序列化API
java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。只有实现了Serializable和Externalizable接口的类的对象才能被序列化。
java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。