如何保证缓存与数据库的双写一致性
先删除缓存,再更新数据库。如果数据库更新失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致。因为读的时候缓存没有,所以去读了数据库中的旧数据,然后更新到缓存中。单删。
写请求先删除缓存,再去更新数据库,(异步等待段时间)再删除缓存(成功表示有脏数据出现)。缓存双删。
这种方案读取快速,但会出现短时间的脏数据。
写请求先修改缓存为指定值,再去更新数据库,再更新缓存。读请求过来后,先读缓存,判断是指定值后进入循环状态,等待写请求更新缓存。如果循环超时就去数据库读取数据,更新缓存。
这种方案保证了读写的一致性,但是读请求会等待写操作的完成,降低了吞吐量。
JDK动态代理为什么要实现接口
因为JDK动态代理类已经继承了Proxy这个类,所以只能通过接口来与被代理类建立联系(两个类建立起联系,一是继承的关系【jdk已经不能通过这个方式了,因为java仅支持单继承】,另一种就是实现同一个接口【JDK动态代理选这种】),所以必须要求被代理类也得实现一个接口,这样的话代理类与被代理类就能通过这个接口建立联系了。
cglib和jdk代理的区别
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类 (2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。
JVM卸载类的条件
-
该类所有的实例已经被回收
-
加载该类的ClassLoder已经被回收
-
该类对应的java.lang.Class对象没有任何对方被引用
A方法没有@Transactional,里面调用了B方法有@Transactional,B方法的@Transactional有效吗?为什么?
事务的传播特性。
合并两个有序的数组
最优解:从后往前处理,不需要开辟额外空间。
IO
HashMap造成循环的情况
主要就是transfer()方法的时候,假设两个线程全都执行了while()方法循环的前面,假设一个线程执行扩容结束。此时的newTable有值。第二个线程在执行e.next = newTable[i]就会产生循环。
ArrayList的扩容机制
添加第一个元素是,初始化一个长度为10的数组,以后每次添加一个元素,如果个数大于数组的长度,则进行扩容,默认扩容1.5倍。
redis的分布式锁
- 互斥性,同一时刻,只能有一个客户端持有锁。
- 防止死锁发生,如果持有锁的客户端崩溃没有主动释放锁,也要保持可以正常释放锁。
- 加锁和释放锁必须是同一个客户端。
- 只有redis还有节点存活,就可以进行正常的加锁解锁操作。
MYSQL可重复读 RR级别
MVCC,对每一个事务分配一个versionId,代表第几个版本,如果另一个是事务对已经查出来的数据进行修改,就会修改最新的事务id,A事务再次查询的时候发现id不匹配,就会去undolog日志中找到匹配的值。
单链表是否为环
快慢指针。
http访问的全流程
通过我们输入的网址URL在应用层进行DNS进行域名解析,找到与它相对应的IP地址。并将请求的数据放到HTTP数据中去。前提是看是否有缓存,如果有缓存,可以直接拿取数据,如果没有缓存,那就需要进行请求。在传输层利用TCP协议进行可靠的传输,加上TCP首部封装。在网络层加上IP首部的封装,经过网络层传输到服务器。 然后在 依次向上(解封)找到应用层,得到数据。然后在从服务器返回到客户端,加载整个页面。
项目难点前端界面。
项目难点数据库的优化,文章的时间和类型进行分表。
亮点数据库与redis的数据一致性问题。
教育项目的亮点,以及分布式开发,前后端分离。
难点用到了许多的第三方api。
静态代理的缺点
- 每一个代理类都需要手动创建,如果有100个需要代理的类就需要手动创建100个类
- 如果被代理的类需要扩展方法,那么每一个代理类也需要扩展,增加耦合性。
去除链表中倒数N个节点
双指针,快指针先走n步,如果到了next直接删除第一个节点。快慢指针一起走,直到快指针到最后,然后修改慢指针的next节点为next.next节点即可。
TCP的拥塞控制
- 慢开始
- 拥塞避免
- 快重传
- 快恢复
linux的常用命令
cd 进入文件夹
ls 列出所有文件
pwd 当前目录
grep "xxxxx" 如果该行数据有关键字则显示出来通常和ps命令一起用
find /xxx xxx 查找文件下的文件
cp 复制命令
mv 移动命令
mkdir 创建文件夹
rm 删除命令
ps 把当前正在运行的所有进程显示出来,可以和grep连用找出自己想要的
kill 强制结束指定进程
tar 压缩解压的命令
cat 查看文件内容命令
vim 编辑文本内容
top 监控进程,查看内存不足的原因
LRU 算法
最近最少未使用先被淘汰。
可以用双向链表来模拟,头节点就是最远使用的,尾节点就是最近使用的。
ActiveMQ消息发送失败怎么办
ActiveMQ有两种通信方式,点到点形式和发布订阅模式。
如果是点到点模式的话,如果消息发送不成功,此消息默认会保存到ActiveMQ服务端直到有消费者将其消费,所以此消息是不会丢失的。
如果是发布订阅模式的通信方式,默认情况只通知一次,如果接受不到此消息就没有了,这种场景使用于对消息发送率要求不高的情况,如果要求消息必须送达不可以丢失的话,需要配置持久订阅。每个订阅端定义一个id,在订阅是向ActiveMQ注册,发布消息和接受消息时需要配置发送模式为持久化,此时如果客户端接受不到消息,消息会持久化到服务端,直到客户端正常接收后为止。
如何防止消息重复发送
解决方法:增加消息状态表。
通俗来说就是一个账本,用来记录消息的处理状态,每次处理消息之前,都去状态表中查询一次,如果已经有相同的消息存在,那么不处理,可以防止重复发送。
持久化消息非常慢
持久化消息是同步发送,非持久化消息是异步发送。开启事务后是异步发送的。
MQ宕机了怎么办
持久化机制。
为什么String类型不可变
- 可以保证线程安全,同一个字符串可以被多个线程共享
- 所以在它创建的时候HashCode就被缓存了,这就使得字符串很适合作为Map中的键。
- 字符串常量池。多个变量指向同一个地址,如果string可变那么所有的变量指向地址都需要改变。
MVCC解决不了幻读
前面先开启的事务进行了delete操作,那么后面的事务就会产生幻读。
git
git reverse -hard 回滚 git branch xx 开启分支 git checkout xx 切换分支 git merge xx 产生冲突手动修改掉删除
可达性分析用来存储的数据机构
OopMap这个结构,存放了GCRoot以及对象的偏移地址,通过解释器和JIN编译器创建,放在内存中。
解决redis的热点key方案
- 通过java读取mysql数据存储到本地内存从内存读取。
- 可以对热点key加随机数让他分配到每一个redis的节点上,不会全部命中一台机器上。
如何打破双亲委派机制
- 线程类类加载器,如果自己不加载就交给线程类加载器加载。
- 自定义类加载器,自定义的类加载器回去加载自己的webapp下的class下的文件,不会交给父类。解决不同的版本对应的问题。
如果当前正在运行本地方法则程序计数器是def值
native方法而言,它的方法体并不是由Java字节码构成的,自然无法应用上述的“Java字节码地址”的概念。所以JVM规范规定,如果当前执行的方法是native的,那么pc寄存器的值未定义——是什么值都可以。
redis的hash和HashMap的扩容区别
Redis的字典rehash和JDK中hashmap等rehash有什么不同? 这个问题比较宽泛,我个人的理解有以下两点.
hashmap rehash的时候的 另外一张table是临时创建的. 而 redis 是时刻保持两张表的引用的. 只是在需要rehash的时候才分配足够的空间. hashmap rehash是一次性的,集中的完成rehash过程, 而redis是渐进式hash. hashmap的rehash过程想必大家都是了解的, 那么这么稍微说一下redis的渐进式hash.
首先, rehash是要将原来表中的所有数据重新hash一遍,存放到新的表格中, 以进行扩容.
而redis是一个单线程的高性能的服务, 如果一个hash表中有几亿条数据, rehash 花费的时间将比较长, 而在此期间, redis是无法对外提供服务的, 这是不可接受的.
因此, redis实现了渐进式hash. 过程如下:
假如当前数据在ht[0]中, 那么首先为ht[1]分配足够的空间. 在字典中维护一个变量, rehashindex = 0. 用来指示当前rehash的进度. 在rehash期间, 每次对 字典进行 增删改查操作, 在完成实际操作之后, 都会进行 一次rehash操作, 将 ht[0] 在rehashindex 位置上的值rehash到ht[1]上. 将 rehashindex 递增一位. 随着不断的执行, 原来的 ht[0] 上的数值总会全部rehash完成, 此时结束rehash过程. 在上面的过程中有两个问题没有提到:
假如这个服务器很空余呢? 中间几小时都没有请求进来, 那么同时保持两个 table, 岂不是很浪费内存? 解决办法是: 在redis的定时函数里, 也加入帮助rehash的操作, 这样子如果服务器空闲, 就会比较快的完成rehash.
在保持两个table期间, 该哈希表怎么对外提供服务呢? 解决办法: 对于添加操作, 直接添加到ht[1]上, 因此这样才能保证ht[0]的数量只会减少不会增加,才能保证rehash过程可以完结. 而删除,修改, 查询等操作会在ht[0]上进行, 如果得不到结果, 会去ht[1]再执行一遍.
渐进式hash带来的好处是显而易见的, 他采用了分而治之的思想, 将rehash操作分散到每一个对该native方法而言,它的方法体并不是由Java字节码构成的,自然无法应用上述的“Java字节码地址”的概念。所以JVM规范规定,如果当前执行的方法是native的,那么pc寄存器的值未定义——是什么值都可以。
mysql视图
一种虚拟存在的表,行和列的数据来自于视图中的查询所用的表,并且是使用视图的时候动态生成的,只保存sql逻辑,不保存结果。
- 使用视图,可以定制用户数据,聚焦特定的数据。
解释:在实际过程中,公司有不同角色的工作人员,我们以销售公司为例的话,采购人员,可以需要一些与其有关的数据,而与他无关的数据,对他没有任何意义,我们可以根据这一实际情况,专门为采购人员创建一个视图,以后他在查询数据时,只需select * from view_caigou 就可以啦。 2. 使用视图,可以简化数据操作。
解释:我们在使用查询时,在很多时候我们要使用聚合函数,同时还要显示其它字段的信息,可能还会需要关联到其它表,这时写的语句可能会很长,如果这个动作频繁发生的话,我们可以创建视图,这以后,我们只需要select * from view1就可以啦,这样很方便。 3. 使用视图,基表中的数据就有了一定的安全性
因为视图是虚拟的,物理上是不存在的,只是存储了数据的集合,我们可以将基表中重要的字段信息,可以不通过视图给用户,视图是动态的数据的集合,数据是随着基表的更新而更新。同时,用户对视图,不可以随意的更改和删除,可以保证数据的安全性。
- 性能差
sql server必须把视图查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,即使是视图的一个简单查询,sql server也要把它变成一个复杂的结合体,需要花费一定的时间。
- 修改限制
当用户试图修改试图的某些信息时,数据库必须把它转化为对基本表的某些信息的修改,对于简单的试图来说,这是很方便的,但是,对于比较复杂的试图,可
redis缓存整个页面
页面缓存是应对高并发的一个比较常见的方案,当请求页面的时候,会先查询redis缓存中是否存在,若存在则直接从缓存中返回页面,否则会通过代码逻辑去渲染页面,并将渲染后的页面缓存到redis中,然后返回。
Group by底层实现
mysql中group by实现方式有三种,松散索引,紧凑索引,临时文件(文件排序)。
在学习SQL优化时,我们都知道可以对group by进行优化的方式就是对group by引用的字段建立索引。当group by引用多个字段时,我们建立的相应的索引也应包含多个字段。
对group by操作优化的原理就是让mysql利用索引,而避免进行建立临时表,进而进行文件排序(group by的第三种实现方式)。
对于group by引用的多个字段,需满足于所建立索引的最左前缀索引,否则进行group by操作时,无法利用索引。在利用索引时,group by可根据索引,即可对数据分组,此时完全不用去访问表的数据值(索引健对应的数据)。这种实现方式就是利用松散索引。
当group by引用的字段无法构成所建索引的最左前缀索引时,也就是说group by不能利用索引时。如何where语句(如果有的话)弥补了这种差距,比如:group by引用的字段为(c2,c3),而索引为(c1,c2,c3)。此时如果where语句限定了c1=a(某一个值),那么此时mysql的执行过程为先根据where语句进行一次选择,
对选出来的结果集,可以利用索引。这种方式,从整体上来说,group by并没有利用索引,但是从过程来说,在选出的结果中利用了索引,这种方式就是紧凑索引。
这种方式,mysql的执行计划为using where,use index。而松散索引的执行计划为using index for group by。
如果mysql如论如何都不能利用索引时,此时mysql将读取所有的数据建立临时表,对文件进行排序,完成分组操作。
关于group的作用
满足于我们针对某些字段进行分组,然后在组内对多行数据进行处理(计算行数,计算max,min等等)这样的需求。group by返回的数据是有序的。
如果我们对数据进行分组后,要输出(select)其所有(多)行的数据,此时是无法实现的。根据版本的不同,可能会输出第一行数据。