文章目录
前言
吐槽(这里可直接跳过):五天九面,面的都麻木了,虽然感觉面试挺累,所幸收获满满,也清楚知道了自身欠缺的一些问题。下面呢是关于一些面试中自己遇到的一些面试问题,略做小记,分享给大家。希望大家都能成功的面上自己心仪的公司,頑張って!一、数据库
1.你所知道的SQL优化?
1、查询语句中不要使用 *
2、尽量减少子查询,使用关联查询(left join,right join,inner join)替代
3、减少使用IN或者NOT IN ,使用exists,not exists或者关联查询语句替代
4、or 的查询尽量用 union或者union all 代替(在确认没有重复数据或者不用剔除重复数据时,union all会更好)
5、合理的增加冗余的字段(减少表的联接查询)
6、增加中间表进行优化(这个主要是在统计报表的场景,后台开定时任务将数据先统计好,尽量不要在查询的时候去统计)
7、建表的时候能使用数字类型的字段就使用数字类型(type,status…),数字类型的字段作为条件查询比字符串的快
8、那些可以过滤掉最大数量记录的条件必须写在WHERE子句的最末尾
2.mysql与oracle区别
- 连接字符串在Oracle中用|| ,SqlServer中用+,MySQL中用concat(‘a’,‘b’,‘c’)
- mysql的group by 语句可以select 没有被分组的字段,如select id,name,age from A group by age 这样但是在orcale和sqlserver中是会报错的。
- oracle很早就完全支持事务。mysql在innodb存储引擎的行级锁的情况下才支持事务。
- oracle默认不自动提交,需要用户手动提交。mysql默认是自动提交。
- mysql分页用limit,oracle分页用rownum
3.MySQL如何控制高并发下安全修改同一条数据
1.加锁,
加锁demo
2.进行手动commit
二、事务
1.什么是事务
是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。对数据库的操作要么统一执行成功,要么统一执行失败。
2.spring中的事务传播
3.@Transactional源码的理解
Spring中@Transactional的使用及事务源码分析
三、redis
1.五大数据类型
- string(字符串)
- hash(哈希)
- list(列表)
- set(集合)
- zset(sorted set:有序集合)
2.redis是使用什么策略清理内存
1.过期删除策略
- 定时删除
- 惰性删除
- 定期删除
2.内存淘汰策略
- 设置Redis最大内存
- 设置内存淘汰方式
Redis过期删除策略是采用惰性删除和定期删除这两种方式组合进行的,惰性删除能够保证过期的数据我们在获取时一定获取不到,而定期删除设置合适的频率,则可以保证无效的数据及时得到释放,而不会一直占用内存数据。
3.突然停电,redis中缓存数据是否会丢失,有没有什么处理
不会丢失,通常来说,当机器关闭的时候,内存中的数据都会清空,都不会存储,那么下次开机的时候,不会出现之前的数据。那么为了防止这种情况突然发生,我们通常都会对缓存做持久化操作。
Redis提供了2中不同形式的持久化类型:
1.rdb(redis database)
原理:将redis在内存的数据库中的数据定时dump到硬盘上,实现rdb持久化。
优缺点:
rdb是比较磁盘空间的,他只是保持更新整个redis缓存中所有的数据的一个完整性。
相当于在一定时间间隔内对redis缓存数据的一个持久化,
采取一种临时文件替换上一层保存的文件,比较节省空间的。
每次fork redis中全数据的时候,会因数据量的增加而增加性能的消耗。
周期进行备份,但是如果突然停止,会丢失最后一次快照的修改。临时文件会丢失。
2.aof(append of file)
原理:将redis的操作日志以追加的形式写入到文件中。
优缺点:
备份机制更加成熟稳健,对每一条操作都会有所记录,那么就会大大的降低丢失率。
这是个日志形式的备份,那么我们就可以直接读持久化文件。可以对其人为的操作。
粒度细致,并且只会增加数据,不会修改数据,那么这个时候会占用更多的磁盘空间。
那么粒度细,也会造成一定的性能压力,每次变更都会新增日志记录。
官方也做出了一些声明:存在个别bug,会造成不能恢复现象。
四、AOP(面向切面编程)
1.AOP是什么,怎么实现的
- AOP面向切面编程,是对业务流程中某一类共性功能的横向抽取。比如事务处理,消息通知。
- Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且 要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。
- 如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
2.举例项目上具体aop的使用
Spring AOP实现日志管理
- 前置通知(可以作为切点,记录用户操作的开始时间等)
- 后置通知(可以作为切点,记录在方法执行之后返回的数据等)
/**
* Spring AOP实现日志管理
* 前置通知 (在方法执行之前返回)用于拦截Controller层记录用户的操作的开始时间
* @param joinPoint 切点
* @throws InterruptedException
*/
@Before("controllerAspect()")
public void doBefore(JoinPoint joinPoint) {
// 线程绑定变量(该数据只有当前请求的线程可见)
Date beginTime = new Date();
THREAD_LOCAL_BEGIN_TIME.set(beginTime);
}
/**
* Spring AOP实现日志管理
* 后置通知(在方法执行之后并返回数据) 用于拦截Controller层无异常的操作
* @param joinPoint 切点
*/
@AfterReturning("controllerAspect()")
public void after(JoinPoint joinPoint) {
try {
String username = "";
String description = getControllerMethodInfo(joinPoint).get("description").toString();
int type = (int) getControllerMethodInfo(joinPoint).get("type");
Map<String, String[]> logParams = request.getParameterMap();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated() || authentication.getName() == null
|| (authentication instanceof AnonymousAuthenticationToken)) {
return;
}
username = authentication.getName();
String device = "", isMobile = "PC端";
UserAgent ua = UserAgentUtil.parse(request.getHeader("user-agent"));
if (ua != null) {
if (ua.isMobile()) {
isMobile = "移动端";
}
device = ua.getBrowser().toString() + " " + ua.getVersion() + " | " + ua.getPlatform().toString()
+ " " + ua.getOs().toString() + " | " + isMobile;
}
if (esRecord) {
EsLog esLog = new EsLog();
// 请求用户
esLog.setUsername(username);
// 日志标题
esLog.setName(description);
// 日志类型
esLog.setLogType(type);
// 日志请求url
esLog.setRequestUrl(request.getRequestURI());
// 请求方式
esLog.setRequestType(request.getMethod());
// 请求参数
esLog.setMapToParams(logParams);
ipInfoUtil.getInfo(request, ObjectUtil.mapToStringAll(request.getParameterMap()));
// 请求IP
esLog.setIp(ipInfoUtil.getIpAddr(request));
// IP地址
esLog.setIpInfo(ipInfoUtil.getIpCity(request));
// 设备信息
esLog.setDevice(device);
// 请求开始时间
long beginTime = THREAD_LOCAL_BEGIN_TIME.get().getTime();
long endTime = System.currentTimeMillis();
// 请求耗时
Long logElapsedTime = endTime - beginTime;
esLog.setCostTime(logElapsedTime.intValue());
// 调用线程保存至ES
ThreadPoolUtil.getPool().execute(new SaveEsSystemLogThread(esLog, esLogService));
} else {
Log log = new Log();
// 请求用户
log.setUsername(username);
// 日志标题
log.setName(description);
// 日志类型
log.setLogType(type);
// 日志请求url
log.setRequestUrl(request.getRequestURI());
// 请求方式
log.setRequestType(request.getMethod());
// 请求参数
log.setMapToParams(logParams);
ipInfoUtil.getInfo(request, ObjectUtil.mapToStringAll(request.getParameterMap()));
// 请求IP
log.setIp(ipInfoUtil.getIpAddr(request));
// IP地址
log.setIpInfo(ipInfoUtil.getIpCity(request));
// 设备信息
log.setDevice(device);
// 请求开始时间
long beginTime = THREAD_LOCAL_BEGIN_TIME.get().getTime();
long endTime = System.currentTimeMillis();
// 请求耗时
Long logElapsedTime = endTime - beginTime;
log.setCostTime(logElapsedTime.intValue());
// 调用线程保存至ES
ThreadPoolUtil.getPool().execute(new SaveSystemLogThread(log, logService));
}
THREAD_LOCAL_BEGIN_TIME.remove();
} catch (Exception e) {
log.error("AOP后置通知异常", e);
}
}
总结个人感受:
简单说说这次找工作给自己最深的体验:主要是心态,五天九面,前面五次全挂,音讯全无,确实挺受打击的。我爱面试,面也没用,没用也面。中毒惹~哈哈哈哈! 最后,祝大家都能找到心仪的工作!