redis缓存问题
- 缓存穿透——使用不存在的key进行大量高并发的查询,导致无法命中缓存从而穿透查询数据库,使数据库压力过大,可能导致数据库宕机。一般因为恶意攻击或bug导致。解决方法,缓存空值并设置较短的过期时间、使用布隆过滤器(BloomFilter)或者压缩filter提前拦截不合法参数等
- 缓存并发——高并发场景下,热点key过期,导致同一时间大量请求查询进入数据库,并写回缓存,导致数据库负载过大。解决方式:本地锁、分布式锁、软过期
- 缓存雪崩——服务器重启或者大量缓存集中在某一个时间内失效。解决方案:设置随机的过期时间。如果是redis挂了,可以通过——1.事发前:实现Redis的高可用(主从架构+Sentinel 或者Redis Cluster) 2.事发中:设置本地缓存(ehcache)+限流(hystrix),避免数据库挂掉 3.事发后:redis持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。
设计模式
23种,分类:
-
1.创建型模式,是对对象创建过程的各种问题和解决方案的总结,这其中包括 工厂模式,单例模式,构建器模式,原型模式。
-
2.结构型模式,是针对软件设计结构的总结,它关注于类,对象继承,组合方式的实践经验。常见的结构型模式有 桥接模式,适配器模式,装饰器模式,代理模式,组合模式,外观模式,享元模式等。
-
3.行为型模式,是从类或者对象之间的交互,职责划分等角度总结的模式。比较常见的行为型模式有 策略模式,解释器模式,命令模式,观察者模式,迭代器模式,模板方法模式,访问者模式。
spring中设计模式的典型用例:
-
BeanFactory
和ApplicationContext
应用了工厂模式; -
在
Bean
的创建中,Spring 为不同的scope
定义的对象,提供了单例和原型等模式的实现; -
AOP 模块则是使用了 代理模式, 装饰器模式,适配器模式等;
-
各种事件监听器,则是使用了观察者模式;
-
Spring JdbcTemplte
等则是应用了模板模式。
synchronized
synchronized
的底层是由一对 monitorenter
/monitorexit
指令实现的,Monitor
对象是同步的基本实现单元。
Java6之前,Monitor的实现完全依靠操作系统内部的互斥锁实现,需要进行用户态到内核态的切换,属于无差别的重量级锁。之后的jdk版本对此做了优化,引入了偏斜锁、轻量级锁、重量级锁等。
所谓锁的升级,降级,实际上是 JVM 对 synchronized
优化的一种策略,JVM 会检测不同的竞争状态,然后自动切换到合适的锁实现,这种切换就是锁的升级,降级。默认使用偏斜锁。
扩展:
- 用户态(User Mode) : 用户态运行的进程可以直接读取用户程序的数据,拥有较低的权限。当应用程序需要执行某些需要特殊权限的操作,例如读写磁盘、网络通信等,就需要向操作系统发起系统调用请求,进入内核态。
- 内核态(Kernel Mode) :内核态运行的进程几乎可以访问计算机的任何资源,包括系统的内存空间、设备、驱动程序等,不受限制,拥有非常高的权限。当操作系统接收到进程的系统调用请求时,就会从用户态切换到内核态,执行相应的系统调用,并将结果返回给进程,最后再从内核态切换回用户态。
- 自旋锁:竞争锁失败的线程,并不会真实的在操作系统层面被挂起,JVM 会让线程做几个空循环(基于预测在短时间内能够获取到锁),经过若干次循环后,如果可以获取到锁,那么会进入到临界区,如果还不能获取到锁,才会真实在操作系统层面被挂起。自旋锁适用于锁竞争不是很激烈,且占用锁时间非常短的代码,会带来性能的提升,因为自旋的消耗会小于线程被挂起带来的消耗。
Mysql索引
B 树也称 B-树,全称为 多路平衡查找树 ,B+ 树是 B 树的一种变体。B 树和 B+树中的 B 是 Balanced
(平衡)的意思。mysql就是采用B+Tree 作为索引结构。
B 树& B+树两者有何异同呢?
- B 树的所有节点既存放键(key) 也存放 数据(data),而 B+树只有叶子节点存放 key 和 data,其他内节点只存放 key。
- B 树的叶子节点都是独立的;B+树的叶子节点有一条引用链指向与它相邻的叶子节点。
- B 树的检索的过程相当于对范围内的每个节点的关键字做二分查找,可能还没有到达叶子节点,检索就结束了。而 B+树的检索效率就很稳定了,任何查找都是从根节点到叶子节点的过程,叶子节点的顺序检索很明显。
MyISAM和InnoDB的索引结构实现方式
MyISAM 引擎中,B+Tree 叶节点的 data 域存放的是数据记录的地址。在索引检索的时候,首先按照 B+Tree 搜索算法搜索索引,如果指定的 Key 存在,则取出其 data 域的值,然后以 data 域的值为地址读取相应的数据记录。这被称为“非聚簇索引(非聚集索引)”。
InnoDB 引擎中,其数据文件本身就是索引文件。相比 MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按 B+Tree 组织的一个索引结构,树的叶节点 data 域保存了完整的数据记录。这个索引的 key 是数据表的主键,因此 InnoDB 表数据文件本身就是主索引。这被称为“聚簇索引(聚集索引)”,而其余的索引都作为 辅助索引 ,辅助索引的 data 域存储相应记录主键的值而不是地址,这也是和 MyISAM 不同的地方。在根据主索引搜索时,直接找到 key 所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。 因此,在设计表的时候,不建议使用过长的字段作为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。
总结:数据查询需要经历几次 IO 是由树的高度来决定的,而树的高度又由磁盘块,数据项的大小决定的。磁盘块越大,数据项越小那么树的高度就越低。这也是为什么索引字段要尽可能小的原因。
java8新特性
-
Interface——新 interface 的方法可以用
default
或static
修饰,这样就可以有方法体,实现类也不必重写此方法。 -
functional interface——函数式接口,有且只有一个抽象方法,但可以有多个非抽象方法的接口。一般使用
@FunctionalInterface
注解。 -
Lambda 表达式——使用 Lambda 表达式可以使代码变的更加简洁紧凑。让 java 也能支持简单的函数式编程。
-
Stream流——它的源数据可以是
Collection
、Array
等。由于它的方法参数都是函数式接口类型,所以一般和 Lambda 配合使用。分为stream 串行流和parallelStream 并行流(可多线程执行)。 -
Optional——建议使用
Optional
解决 NPE(java.lang.NullPointerException
)问题,它就是为 NPE 而生的,其中可以包含空值或非空值。 -
Date-Time API——
LocalDate
、LocalTime
、LocalDateTime、
ZonedDateTime
线程并发
基础:继承Thread、实现Runnable,重写run方法,调用start方法启动线程
进阶:线程池——使用ThreadPoolExecutor创建,也可以通过Executors工具类创建,它会持续监听是否有新的任务需要执行,所以需要使用shutdwon()或shutdwonNow()显示的终止它。不过阿里巴巴开发手册规范不推荐使用Executors创建线程池,会有oom风险,因为涉及无界队列等问题。
带返回值:Callable 和 Future——创建线程池,传参callable调用submit()方法,返回Future,通过future.get()方法获取结果,get()方法可以设置超时时间Timeouts。此外,Executors
支持通过invokeAll和invokeAny一次性批量提交多个callable任务,区别是invokeAll等待全部callable执行完成返回结果,而invokeAny会在某个callable第一个执行完成立即返回结果。
任务调度:ScheduledExecutor,支持通过schedule()——延时执行、scheduleAtFixedRate()——固定周期循环执行、scheduleWithFixedDelay——固定延迟循环执行。scheduleWithFixedDelay()
和 scheduleAtFixedRate()
最大的区别就是, scheduleWithFixedDelay()
需要等到前一个任务执行完成后才开始计延时,再触发下一个任务。