1. zmysql索引结构
mysql索引底层采用的是b+树的结构,一开始mysql的索引采用的是b树的结构,当数据量达到一定程度的时候,b树存在深度过大的问题,那么磁盘io次数就会飞速上升,导致查询效率慢。b+树就很好的解决了这个问题,比如存储2000万条数据的时候,b+树的实际存储深度也就3,4层,实际上就是通过减少树的深度来减少查询时的io次数,io实际上是一个重操作。能少就少,查询才能快起来
2. 索引有哪些类型
索引主要分为4大类
- 按数据结构分:b+,hash,full-text
- 按物理存储分:主键、二级索引
- 按字段特性:主键、唯一、普通、前缀
- 按字段个数:单列、联合
比较常用的就是主键索引、前缀索引、联合索引
3. 布隆过滤器概念及原理
概念:布隆过滤器是一种用来快速判断数据是否在某个集合的数据结构,实际上就是一段位数组加上几个哈希函数组成的结构
优点:布隆过滤器不存储数据本身,并且是通过位数组的形式进行数据的位标记,运算快,节省内存空间
缺点:存在误判的可能,判断在不一定在,但是判断不在就一定不在,并且删除起来困难。存储原理如下图
同一个位置上可能存在多个变量映射到同一个位置
eg:ka经过三个哈希函数映射到了3,4,7.
但是3,4,7在上次存储kakuqo的时候已经标记成为1了,那么系统会判断ka存在,实际上ka是不存在的
3.1. 应用场景
import java.util.BitSet;public class MyBloomFilter {/*** 位数组的大小*/private static final int DEFAULT_SIZE = 2 << 24;/*** 通过这个数组可以创建 6 个不同的哈希函数*/private static final int[] SEEDS = new int[]{3, 13, 46, 71, 91, 134};/*** 位数组。数组中的元素只能是 0 或者 1*/private BitSet bits = new BitSet(DEFAULT_SIZE);/*** 存放包含 hash 函数的类的数组*/private SimpleHash[] func = new SimpleHash[SEEDS.length];/*** 初始化多个包含 hash 函数的类的数组,每个类中的 hash 函数都不一样*/public MyBloomFilter() {// 初始化多个不同的 Hash 函数for (int i = 0; i < SEEDS.length; i++) {func[i] = new SimpleHash(DEFAULT_SIZE, SEEDS[i]);}}/*** 添加元素到位数组*/public void add(Object value) {for (SimpleHash f : func) {bits.set(f.hash(value), true);}}/*** 判断指定元素是否存在于位数组*/public boolean contains(Object value) {boolean ret = true;for (SimpleHash f : func) {ret = ret && bits.get(f.hash(value));}return ret;}/*** 静态内部类。用于 hash 操作!*/public static class SimpleHash {private int cap;private int seed;public SimpleHash(int cap, int seed) {this.cap = cap;this.seed = seed;}/*** 计算 hash 值*/public int hash(Object value) {int h;return (value == null) ? 0 : Math.abs((cap - 1) & seed * ((h = value.hashCode()) ^ (h >>> 16)));}}
}
4种实现布隆过滤器的方式,可自行选择:布隆过滤器四种实现(Java,Guava,hutool,Redisson)_hutool 布隆过滤器-CSDN博客
4. 操作系统读文件流程
- 用户发起文件读取请求,操作系统处理文件系统定位文件、检查权限等
- 查文件控制块(FCB),存储了文件读写位置,文件大小、文件存储位置等信息
- 启动DMA进行数据传输,操作系统把数据源地址+目标地址给DMA,DMA把数据从硬盘读到内存缓冲区,CPU不参与数据传输,由DMA进行数据交换
- DMA根据配置采用合适的传输模式,块传输or周期盗用模式,传输完后通知cpu已完成
- 数据从内存缓冲区传到用户空间,用户完成文件读取后更新文件控制块里的读写指针
- 释放资源避免内存泄漏问题
总结:DMA技术把数据从硬件传到内存,避免cpu参与,提高数据传输效率,减少读取文件的时间
5. 为系统记录日志,侵入式和不侵入式的做法
5.1. 侵入式
AOP定义切面+连接点,在方法前后加入特定逻辑
@Aspect
@Component
public class LoggingAspect {@Before("execution(* com.example.service.*.*(..))")public void logBefore(JoinPoint joinPoint) {System.out.println("Method " + joinPoint.getSignature().getName() + " is called with arguments: " + Arrays.toString(joinPoint.getArgs()));}@After("execution(* com.example.service.*.*(..))")public void logAfter(JoinPoint joinPoint) {System.out.println("Method " + joinPoint.getSignature().getName() + " finished.");}
}
5.2. 非侵入式
5.2.1. 动态代理
通过动态代理或静态代理去包装目标对象,实现方法调用时自动记录日志,这种方式不会修改原有代码实现,只是在方法调用前后添加日志
Java 动态代理(JDK Proxy)可以用于接口类型的类,CGLIB(字节码生成库)可以用于类类型的代理。
import java.lang.reflect.*;public class LoggingHandler implements InvocationHandler {private Object target;public LoggingHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Calling method " + method.getName() + " with args: " + Arrays.toString(args));Object result = method.invoke(target, args);System.out.println("Method " + method.getName() + " executed, result: " + result);return result;}
}public class Service {public void doSomething(String msg) {System.out.println("Doing something: " + msg);}
}public class ProxyExample {public static void main(String[] args) {Service service = new Service();Service proxy = (Service) Proxy.newProxyInstance(service.getClass().getClassLoader(),service.getClass().getInterfaces(),new LoggingHandler(service));proxy.doSomething("Hello, World!");}
}
5.2.2. 拦截器模式
通过在请求进入前或方法执行后拦截请求并执行日志记录
@Component
public class LoggingInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("Request URL: " + request.getRequestURL());return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("Response Status: " + response.getStatus());}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("Request processing complete.");}
}
5.2.3. 事件监听实现日志记录
主要流程有
- 定义事件类 该类用于存储你要记录的关于方法的信息,例如方法名称、参数等
import org.springframework.context.ApplicationEvent;public class MethodExecutionEvent extends ApplicationEvent {private String methodName;public MethodExecutionEvent(Object source, String methodName) {super(source);this.methodName = methodName;}public String getMethodName() {return methodName;}
}
- 发布事件
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;@Service
public class SomeService {@Autowiredprivate ApplicationEventPublisher eventPublisher;public void someMethod(String param) {// 这里发布 MethodExecutionEvent 事件eventPublisher.publishEvent(new MethodExecutionEvent(this, "someMethod"));// 执行方法的实际逻辑System.out.println("Executing someMethod with param: " + param);}
}
- 监听事件 监听器
MethodExecutionListener
就会捕获MethodExecutionEvent
事件并执行相应的处理
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;@Component
public class MethodExecutionListener {@EventListenerpublic void onMethodExecutionEvent(MethodExecutionEvent event) {System.out.println("Method executed: " + event.getMethodName());}
}
6. @transactional注解加载A上,A调用B,此时发生异常,a和b会不会撤回事务
面经来源于牛客网