面试04——整理

1.抽象类怎么模拟接口

2.项目负责

3.线程与进程

4.List list =new Arraylist(Array.aslist(“a”,“b”,“c”,“d”));

​ for(s:list){

​ s.equls(“a”){

​ s.removes()

​ }

​ }

5.io 异常关闭 应该在哪里关闭

6.public class test{

​ private int value;

​ public void get(int x){

​ this.value=x

}

public void increment(){

++this.value;

}

}

7.设备怎么管理,设备是从哪里来的。

设备怎么接受我们发送的消息。

项目文档

接口文档有软件自动生成

kafka组件有哪些

http https 区别

项目怎么部署部署流程

netty

explain

sql 索引怎么优化sql

悲观锁乐观锁

字符串反转 用revers 位与运算符

常用的容器类型

并发容器

jdk 动态代理,class文件缓存在内存,在运行时动态生成代理类进行处理的,只能基于接口进行代理。
cglib 动态代理,是通过继承代理,

还有就是jdk 只能接口进行代理,局限性
cglib 可以代理类

个人觉得局限性在与一个只能通过类进行代理类处理,但是呢cglib可以对方法,比如说一些逻辑组件处理,使用代理类处理。然后逻辑组件再用切面可以适配所有方法。我感觉很爽

所以一般我再写切面处理日志或者一些解耦的东西都是用cglib处理的

可见性

对于可见性,Java提供了volatile关键字来保证可见性。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。

另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

有序性

在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。

在Java里面,可以通过volatile关键字来保证一定的“有序性”(具体原理在下一节讲述)。另外可以通过synchronized和Lock来保证有序性,很显然,synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。另外,Java内存模型具备一些先天的“有序性”,即不需要通过任何手段就能够得到保证的有序性,这个通常也称为 happens-before 原则。如果两个操作的执行次序无法从happens-before原则推导出来,那么它们就不能保证它们的有序性,虚拟机可以随意地对它们进行重排序。

volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住.
volatile仅能使用在变量级别,synchronized则可以使用在变量,方法.
volatile仅能实现变量的修改可见性,但不具备原子特性,而synchronized则可以保证变量的修改可见性和原子性.
volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.
volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化.
synchronized很强大,既可以保证可见性,又可以保证原子性,而volatile不能保证原子性!

出于运行速率的考虑,java编译器会把经常经常访问的变量放到缓存(严格讲应该是工作内存)中,读取变量则从缓存中读。但是在多线程编程中,内存中的值和缓存中的值可能会出现不一致。volatile用于限定变量只能从内存中读取,保证对所有线程而言,值都是一致的。但是volatile不能保证原子性,也就不能保证线程安全。

select * from (select * from 表名 order by 字段 desc limit 10) 临时表 order by 字段

1. 服务雪崩

多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的扇出。如果在扇出的链路上某个微服务的调用响应式过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统雪崩,所谓的”雪崩效应”。

2. 服务熔断

熔断机制是应对雪崩效应的一种微服务链路保护机制,当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务响应正常后,恢复调用链路。

3. 服务降级

服务降级,就是当某个服务熔断之后,服务将不再被调用,此刻客户端需要执行备用的逻辑,返回一个缺省值。

synchronized

“就是因为synchronized性能低,有人就开发了ReentrantLock,大大提高了性能,其中涉及到了AQS高并发组件,用它来维护锁的状态等,这样就不需要利用操作系统来维护,减少上下文切换,做到了基本在Java级别上就能完成操作。里面还用了CAS,自旋等操作来提高性能。但是线程过…”

解释为什么jdk1.6以前synchronized是重量级锁,性能低下的原因?
为啥是重量级锁?

在这个版本之前,加锁的操作是涉及到操作系统进行互斥操作,就是会把当前线程挂起,然后操作系统进行互斥操作修改,由mutexLock来完成,之后才唤醒。操作系统来判断线程是否加锁,所以它是一个重量级操作。挂起、唤醒这两个操作进行了两次上下文切换,消耗CPU,降低性能。其实在这个版本之前已经考虑到CAS操作,但是默认是没有开启的。

当然,除了操作系统维护锁的状态使当前线程挂起外,只要是synchronized,一有竞争也会引起阻塞,阻塞和唤醒操作又涉及到了上下文操作,大量消耗CPU,降低性能。

ResultMap和ResultType的

ResultMap和ResultType的功能类似,但是ResultMap更强大一点,ResultMap可以实现将查询结果映射为复杂类型的pojo。

@Transactional失效场景
声明式事务失效的场景有很多,陈某这里只是罗列一下几种常见的场景。

底层数据库引擎不支持事务
如果数据库引擎不支持事务,则Spring自然无法支持事务。

在非public修饰的方法使用
@Transactional注解使用的是AOP,在使用动态代理的时候只能针对public方法进行代理

在整个事务的方法中使用try-catch,导致异常无法抛出,自然会导致事务失效。

方法中调用同类的方法

  • 简单的说就是一个类中的A方法(未标注声明式事务)在内部调用了B方法(标注了声明式事务),这样会导致B方法中的事务失效。

原始SSM项目,重复扫描导致事务失效
在原始的SSM项目中都配置了context:component-scan并且同时扫描了service层,此时事务将会失效。

按照Spring配置文件的加载顺序来说,会先加载Springmvc的配置文件,如果在加载Springmvc配置文件的时候把service也加载了,但是此时事务还没加载,将会导致事务无法成功生效。

juc包下四大并发工具

解决方法很简单,把扫描service层的配置设置在Spring配置文件或者其他配置文件中即可juc.CountDownLatch 闭锁

一个线程在等待一组线程后再恢复执行

ConcurrentHashMap

JDK8中ConcurrentHashMap参考了JDK8 HashMap的实现,采用了数组+链表+红黑树的实现方式来设计,内部大量采用CAS操作。并发控制使⽤synchronized 和 CAS 来操作。(JDK1.6 以后 对 synchronized 锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在 JDK1.8 中还能看到 Segment 的数据结构,但
是已经简化了属性,只是为了兼容旧版本;

JDK1.8的Nod节点中value和next都用volatile修饰,保证并发的可见性。

我们知道,当前的应用都离不开数据库,随着数据库中的数据越来越多,单表突破性能上限记录时,如MySQL单表上线估计在近千万条内,当记录数继续增长时,从性能考虑,则需要进行拆分处理。而拆分分为横向拆分和纵向拆分。一般来说,采用横向拆分较多,这样的表结构是一致的,只是不同的数据存储在不同的数据库表中。其中横向拆分也分为分库和分表。

为什么决定进行分库分表

\1. 根据业务类型,和业务容量的评估,来选择和判断是否使用分库分表。
\2. 当前数据库本事具有的能力,压力的评估。
\3. 数据库的物理隔离,例如减少锁的争用、资源的消耗和隔离等。
\4. 热点表较多,并且数据量大,可能会导致锁争抢,性能下降。
\5. 数据库的高并发,数据库的读写压力过大,可能会导致数据库或系统宕机。
\6. 数据库(MySQL5.7以下)连接数过高,会增加系统压力。
\7. 单表数据量大,如SQL使用不当,会导致io随机读写比例高。查询慢(大表上的B+树太大,扫描太慢,甚至可能需要4层B+树)
\8. 备份和恢复时间比较长。

作者:阿里技术
链接:https://www.zhihu.com/question/448775613/answer/1774351830
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1 什么是分库分表?

其实就是字面意思,很好理解:

  • 分库:从单个数据库拆分成多个数据库的过程,将数据散落在多个数据库中。
  • 分表:从单张表拆分成多张表的过程,将数据散落在多张表内。

2 为什么要分库分表?

关键字:提升性能、增加可用性。

从性能上看

随着单库中的数据量越来越大、数据库的查询QPS越来越高,相应的,对数据库的读写所需要的时间也越来越多。数据库的读写性能可能会成为业务发展的瓶颈。对应的,就需要做数据库性能方面的优化。本文中我们只讨论数据库层面的优化,不讨论缓存等应用层优化的手段。

如果数据库的查询QPS过高,就需要考虑拆库,通过分库来分担单个数据库的连接压力。比如,如果查询QPS为3500,假设单库可以支撑1000个连接数的话,那么就可以考虑拆分成4个库,来分散查询连接压力。

如果单表数据量过大,当数据量超过一定量级后,无论是对于数据查询还是数据更新,在经过索引优化等纯数据库层面的传统优化手段之后,还是可能存在性能问题。这是量变产生了质变,这时候就需要去换个思路来解决问题,比如:从数据生产源头、数据处理源头来解决问题,既然数据量很大,那我们就来个分而治之,化整为零。这就产生了分表,把数据按照一定的规则拆分成多张表,来解决单表环境下无法解决的存取性能问题。

从可用性上看

单个数据库如果发生意外,很可能会丢失所有数据。尤其是云时代,很多数据库都跑在虚拟机上,如果虚拟机/宿主机发生意外,则可能造成无法挽回的损失。因此,除了传统的 Master-Slave、Master-Master 等部署层面解决可靠性问题外,我们也可以考虑从数据拆分层面解决此问题。

此处我们以数据库宕机为例:

  • 单库部署情况下,如果数据库宕机,那么故障影响就是100%,而且恢复可能耗时很长。
  • 如果我们拆分成2个库,分别部署在不同的机器上,此时其中1个库宕机,那么故障影响就是50%,还有50%的数据可以继续服务。
  • 如果我们拆分成4个库,分别部署在不同的机器上,此时其中1个库宕机,那么故障影响就是25%,还有75%的数据可以继续服务,恢复耗时也会很短。

当然,我们也不能无限制的拆库,这也是牺牲存储资源来提升性能、可用性的方式,毕竟资源总是有限的。

二 如何分库分表

1 分库?分表?还是既分库又分表?

从第一部分了解到的信息来看,分库分表方案可以分为下面3种:

imgimg

2 如何选择我们自己的切分方案?

如果需要分表,那么分多少张表合适?

由于所有的技术都是为业务服务的,那么,我们就先从数据方面回顾下业务背景。

比如,我们这个业务系统是为了解决会员的咨询诉求,通过我们的XSpace客服平台系统来服务会员,目前主要以同步的离线工单数据作为我们的数据源来构建自己的数据。

假设,每一笔离线工单都会产生对应一笔会员的咨询问题(我们简称:问题单),如果:

  • 在线渠道:每天产生 3w 笔聊天会话,假设,其中50%的会话会生成一笔离线工单,那么每天可生成 3w * 50% = 1.5w 笔工单;
  • 热线渠道:每天产生 2.5w 通电话,假设,其中80%的电话都会产生一笔工单,那么每天可生成 2.5w * 80% = 2w 笔/天;
  • 离线渠道:假设离线渠道每天直接生成 3w 笔;

合计共 1.5w + 2w + 3w = 6.5w 笔/天

考虑到以后可能要继续覆盖的新的业务场景,需要提前预留部分扩展空间,这里我们假设为每天产生 8w 笔问题单。

除问题单外,还有另外2张常用的业务表:用户操作日志表、用户提交的表单数据表。

其中,每笔问题单都会产生多条用户操作日志,根据历史统计数据来可以看到,平均每个问题单大约会产生8条操作日志,我们预留一部分空间,假设每个问题单平均产生约10条用户操作日志。

如果系统设计使用年限5年,那么问题单数据量大约 = 5年 365天/年 8w/天 = 1.46亿,那么估算出的表数量如下:

  • 问题单需要:1.46亿/500w = 29.2 张表,我们就按 32 张表来切分;
  • 操作日志需要 :32 10 = 320 张表,我们就按 32 16 = 512 张表来切分。

如果需要分库,那么分多少库合适?

分库的时候除了要考虑平时的业务峰值读写QPS外,还要考虑到诸如双11大促期间可能达到的峰值,需要提前做好预估。

根据我们的实际业务场景,问题单的数据查询来源主要来自于阿里客服小蜜首页。因此,可以根据历史QPS、RT等数据评估,假设我们只需要3500数据库连接数,如果单库可以承担最高1000个数据库连接,那么我们就可以拆分成4个库。

3 如何对数据进行切分?

根据行业惯例,通常按照 水平切分、垂直切分 两种方式进行切分,当然,有些复杂业务场景也可能选择两者结合的方式。

(1)水平切分

这是一种横向按业务维度切分的方式,比如常见的按会员维度切分,根据一定的规则把不同的会员相关的数据散落在不同的库表中。由于我们的业务场景决定都是从会员视角进行数据读写,所以,我们就选择按照水平方式进行数据库切分。

(2)垂直切分

垂直切分可以简单理解为,把一张表的不同字段拆分到不同的表中。

比如:假设有个小型电商业务,把一个订单相关的商品信息、买卖家信息、支付信息都放在一张大表里。可以考虑通过垂直切分的方式,把商品信息、买家信息、卖家信息、支付信息都单独拆分成独立的表,并通过订单号跟订单基本信息关联起来。

也有一种情况,如果一张表有10个字段,其中只有3个字段需要频繁修改,那么就可以考虑把这3个字段拆分到子表。避免在修改这3个数据时,影响到其余7个字段的查询行锁定。

sql查询数据库最后10条记录并按降序排列

SELECT TOP 10 FROM 表名 ORDER BY 排序列 DESC;SQL的执行顺序先按照你的要求排序,然后才返回查询的内容。例如有一个名为ID自动增长的列,表中有100条数据,列的值得分别是1、2、3、4………9、99、100。那么查询加了DESC你得到的是91到100条,就是最后十条,如果加ASC你得到的将会是1到10,也就是最前面的那几条。记录如果说有先后的话 必然是根据某几个字段进行排序了的你反过来排序就变成求前10条记录了呗,把desc和 asc互换一下 (默认是 asc )oracle 的写法select * from (select * from tab order by col desc ) where rownum <= 10赞同最后10条降序与最前10条升序是一样的如果还想排序,那就按他们说的用临时表。select top 10 * from table 1 order by field1 into table #tempselect * from #temp order by field1 desc //查询结果放临时表select * top 10 from table1 order by field1 asc into tabl temp //再从临时表查询select * from temp order by field1 desc

一.git提交代码步骤

1.拉取远程的代码,先pull,查看有哪些差异。 git pull

2.备份自己的文件,把所有差异还原。

3.再次pull,成功后在具体的文件中,把自己的代码粘贴复制过去,再次pull。

4.提交代码到本地 git add . git commit -m ‘修改注释’

5.推送代码到远程

备注:(1)如果是新增的文件,需要先新增,再从第2步开始。

​ (2)第2/3步骤适用于不会解决冲突的人。

二.工作中常用的git命令

1.回退历史版本

(1)git log 查看提交记录 copy 历史版本id

(2)git reset --hard 复制的历史版本id

(3)如果是取消最近一次的commit 保留本地文件修改 git reset HEAD

(4)回退并推送至远程分支 git push -f origin master

2.回退某个文件

(1)到该文件的文件夹下,打开命令面板

(2)git log 文件名.文件格式

(3)git reset 版本号 文件名.文件格式

(4)如果还想远程也回退版本 git push -f

(5)如果需要放弃本地该文件的修改 git checkout .

3.删除缓存的远程分支列表

(1)git remote prune origin

(2)git fetch -p

(3)git checkout . && git clean -xdf 抛弃本地修改

4.创建分支

在哪个分支运行的命令,就是从哪个分支为基础拉新的分支。

(1)git checkout -b dev 创建dev分支并切换到dev分支

相当于 git branch dev 与 git checkout dev 两个命令

(2)git push origin dev 把dev分支推送至远程

(3)git branch --set-upstream-to origin/dev 把本地当前的分支与远程dev分支 然后就可以用git push 推送代码到远程dev分支了

5.合并分支

切换到想要合并其他分支的分支 一般为master

(1)git checkout master

(2)git merge dev 合并dev分支到master

(3)如果合并之后 dev分支没用了 ,删除dev分支 git branch -d dev

6.添加远程分支

fork代码到私人仓库,从私有仓库拉取的代码后,需要添加远程分支

git remote add 本地远程仓库名称(自己起的有意义能识别的名称) remote-http-adress(远程仓库的克隆地址)

比如远程仓库命名为 remoteApp 仓库地址为http://remote.com,那么命令为:git remote add remoteApp http://remote.com

7.拉取远程分支代码

git pull remoteName branchName

比如git pull remoteApp master

8.查看有哪些分支

git branch -a a可以理解为all 所有

9.git pull出现合并的提示消息,按照如下图片操作,忘记在哪个博客截图的图片了,非原创。

img

10.修改分支命名

(1)如果还没有推送到远程:git branch -m oldName newName

(2)已经推送到了远程:

1)重命名远程分支对于的本地分支 git branch -m oldName newName

​ 2)删除远程分支 git push --delete origin oldName

3)上传新命名的本地分支 git push origin newName

4)把修改过后的本地分支与远程分支关联 git branch --set-upstream-to origin/newName

CAS

、对于资源竞争较少的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。

2、对于资源竞争严重的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。以

死锁的条件

1、互斥条件:一个资源每次只能被一个进程使用;

img

2、请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放;

3、不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺;

4、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系;

死锁概念及产生原理

​ 概念: 多个并发进程因争夺系统资源而产生相互等待的现象。
​ 原理: 当一组进程中的每个进程都在等待某个事件发生,而只有这组进程中的其他进程才能触发该事件,这就称这组进程发生了死锁。
​ 本质原因:
​ 1)、系统资源有限。
​ 2)、进程推进顺序不合理。

死锁产生的4个必要条件
1、互斥: 某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。
2、占有且等待: 一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。
3、不可抢占: 别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。
4、循环等待: 存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。
当以上四个条件均满足,必然会造成死锁,发生死锁的进程无法进行下去,它们所持有的资源也无法释放。这样会导致CPU的吞吐量下降。所以死锁情况是会浪费系统资源和影响计算机的使用性能的。那么,解决死锁问题就是相当有必要的了。

避免死锁的几种方式:
设置加锁顺序

设置加锁时限

死锁检测

设置加锁顺序(线程按照一定的顺序加锁):
死锁发生在多个线程需要相同的锁,但是获得不同的顺序。

假如一个线程需要锁,那么他必须按照一定得顺序获得锁。
例如加锁顺序是A->B->C,现在想要线程C想要获取锁,那么他必须等到线程A和线程B获取锁之后才能轮到他获取。(排队执行,获取锁)

缺点:
按照顺序加锁是一种有效的死锁预防机制。但是,这种方式需要你事先知道所有可能会用到的锁,并知道他们之间获取锁的顺序是什么样的。

设置加锁时限:(超时重试)
在获取锁的时候尝试加一个获取锁的时限,超过时限不需要再获取锁,放弃操作(对锁的请求。)。

若一个线程在一定的时间里没有成功的获取到锁,则会进行回退并释放之前获取到的锁,然后等待一段时间后进行重试。在这段等待时间中其他线程有机会尝试获取相同的锁,这样就能保证在没有获取锁的时候继续执行比的事情。

缺点:
但是由于存在锁的超时,通过设置时限并不能确定出现了死锁,每种方法总是有缺陷的。有时为了执行某个任务。某个线程花了很长的时间去执行任务,如果在其他线程看来,可能这个时间已经超过了等待的时限,可能出现了死锁。

在大量线程去操作相同的资源的时候,这个情况又是一个不可避免的事情,比如说,现在只有两个线程,一个线程执行的时候,超过了等待的时间,下一个线程会尝试获取相同的锁,避免出现死锁。但是这时候不是两个线程了,可能是几百个线程同时去执行,大的基数让事件出现的概率变大,假如线程还是等待那么长时间,但是多个线程的等待时间就有可能重叠,因此又会出现竞争超时,由于他们的超时发生时间正好赶在了一起,而超时等待的时间又是一致的,那么他们下一次又会竞争,等待,这就又出现了死锁。

死锁检测:
当一个线程获取锁的时候,会在相应的数据结构中记录下来,相同下,如果有线程请求锁,也会在相应的结构中记录下来。当一个线程请求失败时,需要遍历一下这个数据结构检查是否有死锁产生。

例如:线程A请求锁住一个方法1,但是现在这个方法是线程B所有的,这时候线程A可以检查一下线程B是否已经请求了线程A当前所持有的锁,像是一个环,线程A拥有锁1,请求锁2,线程B拥有锁2,请求锁1。
当遍历这个存储结构的时候,如果发现了死锁,一个可行的办法就是释放所有的锁,回退,并且等待一段时间后再次尝试。

缺点:
这个这个方法和上面的超时重试的策略是一样的。但是在大量线程的时候问题还是会出现和设置加锁时限相同的问题。每次线程之间发生竞争。

预防死锁

还有一种解决方法是设置线程优先级,这样其中几个线程回退,其余的线程继续保持着他们获取的锁,也可以尝试随机设置优先级,这样保证线程的执行。

数据的锁定分为两种,第一种叫作悲观锁,第二种叫作乐观锁。

1、悲观锁,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住。【数据锁定:数据将暂时不会得到修改】

2、乐观锁,认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让用户返回错误的信息。让用户决定如何去做。

乐观锁

理解:

  1. 乐观锁是一种思想,具体实现是,表中有一个版本字段,第一次读的时候,获取到这个字段。处理完业务逻辑开始更新的时候,需要再次查看该字段的值是否和第一次的一样。如果一样更新,反之拒绝。

之所以叫乐观,因为这个模式没有从数据库加锁。

  1. 悲观锁是读取的时候为后面的更新加锁,之后再来的读操作都会等待。这种是数据库锁

乐观锁优点程序实现,不会存在死锁等问题。他的适用场景也相对乐观。阻止不了除了程序之外的数据库操作。

悲观锁是数据库实现,他阻止一切数据库操作。

再来说更新数据丢失,所有的读锁都是为了保持数据一致性。乐观锁如果有人在你之前更新了,你的更新应当是被拒绝的,可以让用户从新操作。悲观锁则会等待前一个更新完成。这也是区别。具体业务具体分析

MySQL的四种事务隔离级别

本文实验的测试环境:Windows 10+cmd+MySQL5.6.36+InnoDB

一、事务的基本要素(ACID)

1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。

**2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。

**

3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。

4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

二、事务的并发问题

1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据

2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。

3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

**1、事务隔离级别为读提交时,写数据只会锁住相应的行****

2*、事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果****检索条件****没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。*

3、事务隔离级别为串行化时,读写数据都会锁住整张表

4*、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。*

Java并发编程:volatile关键字解析

如何保证Redis和数据库双写一致性的问题?

Redis在国内各大公司都很热门,比如新浪、阿里、腾讯、百度、美团、小米等。Redis也是大厂面试最爱问的,尤其是Redis客户端、Redis高级功能、Redis持久化和开发运维常用问题探讨、Redis复制的原理和优化策略、Redis分布式解决方案等。

关于Redis的这8问,你能答上来几个?

1、为什么使用Redis

项目中使用Redis,主要考虑性能和并发。如果仅仅是分布式锁这些,完全可以用中间件ZooKeeper等代替。

性能:

如下图所示,在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用Redis做一个缓冲操作,让请求先访问到Redis,而不是直接访问数据库。

根据交互效果的不同,响应时间没有固定标准。在理想状态下,我们的页面跳转需要在瞬间解决,对于页内操作则需要在刹那间解决。

并发:

如下图所示,在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用Redis做一个缓冲操作,让请求先访问到Redis,而不是直接访问数据库。

2、使用Redis有什么缺点?

缓存和数据库双写一致性问题
缓存雪崩问题
缓存击穿问题
缓存的并发竞争问题

3、单线程的Redis为什么这么快?

你知道Redis是单线程工作模型吗?

纯内存操作
单线程操作,避免了频繁的上下文切换
采用了非阻塞I/O多路复用机制

4、Redis的数据类型及使用场景

(这5种类型你用到过几个?)

String:一般做一些复杂的计数功能的缓存;

Hash:单点登录;

List:做简单的消息队列的功能;

Set:做全局去重的功能;

SortedSet:做排行榜应用,取TOPN操作;延时任务;做范围查找。

5、Redis过期策略和内存淘汰机制?

正解:Redis采用的是定期删除+惰性删除策略。

为什么不用定时删除策略?
定期删除+惰性删除是如何工作的呢?
采用定期删除+惰性删除就没其他问题了么?

6、Redis和数据库双写一致性问题

(最终一致性和强一致性)

如果对数据有强一致性要求,不能放缓存。

7、如何应对缓存穿透和缓存雪崩问题

缓存穿透:即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。

缓存雪崩:即缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。

中小型的公司一般遇不到这些问题,但是大并发的项目,流量有几百万左右,这两个问题一定要深刻考虑。

8、如何解决Redis并发竞争Key问题?

这个问题大致就是,同时有多个子系统去set一个key。不太推荐使用redis的事务机制。

(1)如果对这个key操作,不要求顺序

这种情况下,准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可。

(2)如果对这个key操作,要求顺序

假设有一个key1,系统A需要将key1设置为valueA,系统B需要将key1设置为valueB,系统C需要将key1设置为valueC.

期望按照key1的value值按照 valueA–>valueB–>valueC的顺序变化。这种时候我们在数据写入数据库的时候,需要保存一个时间戳。假设时间戳如下

系统A key 1 {valueA 3:00}
系统B key 1 {valueB 3:05}
系统C key 1 {valueC 3:10}

那么,假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。

其他方法,比如利用队列,将set方法变成串行访问也可以。总之,灵活变通。

烟哥彩蛋

在面试中如果碰到下列问题,如何应用上本篇的知识呢?先明确一点,我推荐的是Redis Cluster。
OK,开始举例说明

问题1:懂Redis事务么?
正常版:Redis事务是一些列redis命令的集合,blabla…
高调版: 我们在生产上采用的是Redis Cluster集群架构,不同的key是有可能分配在不同的Redis节点上的,在这种情况下Redis的事务机制是不生效的。其次,Redis事务不支持回滚操作,简直是鸡肋!所以基本不用!

问题2:Redis的多数据库机制,了解多少?
正常版:Redis支持多个数据库,并且每个数据库的数据是隔离的不能共享,单机下的redis可以支持16个数据库(db0 ~ db15)
高调版: 在Redis Cluster集群架构下只有一个数据库空间,即db0。因此,我们没有使用Redis的多数据库功能!

问题3:Redis集群机制中,你觉得有什么不足的地方吗?
正常版: 不知道
高调版: 假设我有一个key,对应的value是Hash类型的。如果Hash对象非常大,是不支持映射到不同节点的!只能映射到集群中的一个节点上!还有就是做批量操作比较麻烦!

问题4:懂Redis的批量操作么?
正常版: 懂一点。比如mset、mget操作等,blabla
高调版: 我们在生产上采用的是Redis Cluster集群架构,不同的key会划分到不同的slot中,因此直接使用mset或者mget等操作是行不通的。

问题5:那在Redis集群模式下,如何进行批量操作?
正常版:不知道
高调版:这个问题其实可以写一篇文章了,改天写。这里说一种有一个很简单的答法,足够面试用。即:
如果执行的key数量比较少,就不用mget了,就用串行get操作。如果真的需要执行的key很多,就使用Hashtag保证这些key映射到同一台Redis节点上。简单来说语法如下

对于key为{foo}.student1、{foo}.student2,{foo}student3,这类key一定是在同一个redis节点上。因为key中“{}”之间的字符串就是当前key的hash tags, 只有key中{ }中的部分才被用来做hash,因此计算出来的redis节点一定是同一个!

ps:如果你用的是Proxy分片集群架构,例如Codis这种,会将mget/mset的多个key拆分成多个命令发往不同得Redis实例,这里不多说。我推荐答的还是Redis Cluster。

问题6:你们有对Redis做读写分离么?
正常版:没有做,至于原因额。。。额。。。额。。没办法了,硬着头皮扯~
高调版:不做读写分离。我们用的是Redis Cluster的架构,是属于分片集群的架构。而Redis本身在内存上操作,不会涉及IO吞吐,即使读写分离也不会提升太多性能,Redis在生产上的主要问题是考虑容量,单机最多10-20G,key太多降低Redis性能.因此采用分片集群结构,已经能保证了我们的性能。其次,用上了读写分离后,还要考虑主从一致性,主从延迟等问题,徒增业务复杂度。

list遍历并删除

public void testDel() {
List list = Lists.newArrayList();
list.add(1);
list.add(2);
list.add(2);
list.add(2);
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Integer integer = iterator.next();
if (integer == 2)
iterator.remove();
}
}

LeetCode(3):无重复字符的最长子串

“SpringCloud和Dubbo都是当下流行的RPC框架,各自都集成了服务发现和治理组件。SpringCloud用Eureka,Dubbo用Zookeeper。”

单一应用架构,当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的 数据访问框架(ORM)是关键。
垂直应用架构,当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的 Web框架(MVC)是关键。
分布式服务架构,当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的 分布式服务框架(RPC)是关键。
流动计算架构当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的 资源调度和治理中心(SOA)是关键。

  • 理解为啥说“http协议一般会使用JSON报文,消耗会更大”?可以解释一下吗?

  • 数据底层是二进制流,直来直往快一些,无论是json还是xml都需要转换,会影响速率

  • json和xml这些都是字符了,而且里面的很多内容都是多余的,这种格式很直白但是效率不高,不如直接基于字节的协议

  • 1、dubbo由于是二进制的传输,占用带宽会更少

    2、springCloud是http协议传输,带宽会比较多,同时使用http协议一般会使用JSON报文,消耗会更大

    3、dubbo的开发难度较大,原因是dubbo的jar包依赖问题很多大型工程无法解决

    4、springcloud的接口协议约定比较自由且松散,需要有强有力的行政措施来限制接口无序升级

    5、dubbo的注册中心可以选择zk,redis等,springcloud的注册中心用eureka或者Consul

    1、dubbo由于是二进制的传输,占用带宽会更少

    2、springCloud是http协议传输,带宽会比较多,同时使用http协议一般会使用JSON报文,消耗会更大

    3、dubbo的开发难度较大,原因是dubbo的jar包依赖问题很多大型工程无法解决

    4、springcloud的接口协议约定比较自由且松散,需要有强有力的行政措施来限制接口无序升级

    5、dubbo的注册中心可以选择zk,redis等,springcloud的注册中心用eureka或者Consul
    ————————————————
    版权声明:本文为CSDN博主「程序大视界」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/xuri24/article/details/89283802

我搭建的kafka集群宕机

我搭建的kafka集群中包含3个节点,当我先随机杀掉两个节点时,第3个节点成为leader,整个集群仍然可用,当我再杀掉唯一的节点时,kafka集群不可用。

但是我发现应用程序的日志一直在重连最后宕机的leader,并没有重连之前两个宕机的节点。

然后,我恢复了先杀掉的两个kafka节点,此时kafka集群可用,但我发现kafka客户端仍然没有重连恢复的节点,而是一直保持与最后死掉leader的重连。

但是其他节点恢复后成为新的leader了,客户端也没有重连,也就造成了全部节点宕机进行恢复时,必须恢复最后宕机的leader,否则,kafka集群虽然已经可用,但是应用程序仍然无法正常使用。

不知道这个问题怎么解决。

Linux简介及****Ubuntu安装

Linux,免费开源,多用户多任务系统。基于Linux有多个版本的衍生。RedHat、Ubuntu、Debian

安装VMware或VirtualBox虚拟机。具体安装步骤,找百度。

再安装Ubuntu。具体安装步骤,找百度。

安装完后,可以看到Linux系统的目录结构,见链接http://www.cnblogs.com/laov/p/3409875.html

常用指令

ls   显示文件或目录

-l 列出文件详细信息l(list)

-a 列出当前目录下所有文件及目录,包括

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值