2019尚硅谷经典Java面试题(第1季)03 Linux常用命令,git分支,Redis持久化,Mysql索引,JVM垃圾回收,Redis使用场景,消息队列工作模式
Linux常用命令,git分支,Redis持久化,Mysql索引,JVM垃圾回收,Redis使用场景,消息队列工作模式
1、Linux常用服务类相关命令
centos6
service (centos6)·注册在系统中的标准化程序
·有方便统一的管理方式(常用的方法)
- service服务名start
- service 服务名stop
- service服务名restart
- service服务名reload
- service服务名status
查看服务的方法letclinit.d/服务名
通过chkconfig命令设置自启动
- 查看服务chkconfig --list l grep xXX
- chkconfig–level 5 服务名on
service network status
Configured devices: #已配置设备
lo ens33
Currently active devices: #当前活跃设备
lo ens33 docker0 cali2bddb712697@if3
chkconfig --list
Note: This output shows SysV services only and does not include native
systemd services. SysV configuration data might be overridden by native
注:该输出结果只显示SysV服务,并不包含原生systemd服务。SysV配置数据
可能被原生systemd配置覆益。
systemd configuration.
If you want to list systemd services use 'systemctl list-unit-files'.
To see services enabled on particular target use
'systemctl list-dependencies [target]'.
要列出systemd服务,请执行'systemctl list-unit-files '.查看在具体target启用的服务请执行
"systemctl list-dependencies [target] ".
netconsole 0:off 1:off 2:off 3:off 4:off 5:off 6:off
network 0:off 1:off 2:on 3:on 4:on 5:on 6:off
chkconfig --level 5 network off #关闭
运行级别
运行级别runlevel(centos6)
开机
BIOs
/boot
init进程
运行级别
运行级对应的服务
查看默认级别:vi/etc/inittab
Linux系统有7种运行级别(runlevel):常用的是级别3和5
运行级别0:系统停机状态,系统默认运行级别不能设为0,否则不能正常启动
运行级别1:单用户工作状态 没网,root权限,用于系统维护,禁止远程登陆
运行级别2:多用户状态(没有NFS),不支持网络
运行级别3:完全的多用户状态(有NFS) 无图形界面,登陆后进入控制台命令行模式运行级别
4:系统未使用,保留
运行级别5:X11控制台,登陆后进入图形GUI模式
运行级别6:系统正常关闭并重启,默认运行级别不能设为6,否则不能正常启动
centos7
systemctl (centos7)
注册在系统中的标准化程序
.有方便统一的管理方式(常用的方法)
systemctl start服务名(xxxx.service)
systemctl restart服务名(
systemctl stop服务名(
systemctl reload服务名(
systemctl status服务名(
查看服务的方法/usr/lib/systemd/system
查看服务的命令
systemctl list-unit-files
systemctl --type service
通过systemctl命令设置自启动
自启动systemctl enable service_name
不自启动systemctl disable service_name
systemctl status firewalld
systemctl stop firewalld
systemctl list-unit-files | grep firewalld
firewalld.service disabled
systemctl disable firewalld #进制开机自启
2、git分支相关命令
创建分支
git branch<分支名>
git branch -v查看分支
切换分支
git checkout<分支名> #切换到 分支
一步完成: git checkout -b <分支名> #创建 并切换分支
合并分支
先切换到主干 git checkout master
git merge<分支名>
#在b分支上,想要把b 合并到 a
#先切换到a分支(git checkout a),然后:git merge b
删除分支
先切换到主干 git checkout master
git branch -D<分支名>
branch
英
/brɑːntʃ/
n.
树枝,分枝;分支机构,分店;(政府或机构的)部门;分科,分支;(家族的)支系;支流,岔路
v.
分岔,岔开;出枝,发出新枝
checkout
英
/ˈtʃekaʊt/
n.
检验;签出;结账台;检出
git checkout——检出,是我们的常用命令。最为常用的两种情形是创建分支和切换分支。
git工作流
master
- V1.0
- 出现bug,拉取分支:hotfix,修复bug,合并到 master。
- 临时分支:hotfix 合并到 develop
develop
- feature_game 要开发的功能,多个人开发,公用这个一个分支。
- feature_game 完成,合并到 develop分支,创建测试分支 release.2.0
测试分支 release.2.0
-
出现bug,直接修改 release.2.0 分支,没问题后,合并到 master分支。
-
测试分支 合并到 develop
新的模块开发完毕,合并到 develop,创建测试3.0,…
feature
英
/ˈfiːtʃə(r)
n.
特点,特征;五官,面貌(特征);地貌;特写,专题节目;正片; 特点,特征
v.
以……为特色,以……为主要组成;起重要作用,占重要地位;放映,上演;担任主演
3、Redis持久化类型
Redis提供了2个不同形式的持久化方式。
RDB (Redis DataBase)
AOF (Append Of File)
RDB
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。
备份是如何执行的
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
rdb的优点
·节省磁盘空间·恢复速度快
rdb的缺点
虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能。
在备份周期在一定间隔时间做一次备份,所以如果
Redis意外down掉的话,就会丢失最后一次快照后的所有修改。
AOF
AOF
以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,Redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
AOF的优点
备份机制更稳健,丢失数据概率更低。
可读的日志文本,通过操作AOF稳健,可以处理误操作。
AOF的缺点
·比起RDB占用更多的磁盘空间。·恢复备份速度要慢。
。每次读写都同步的话,有一定的性能压力。·存在个别Bug,造成恢复不能。
4. Mysql什么时候适合建索引
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。可以得到索引的本质:索引是数据结构。
你可以简单理解为"排好序的快速查找数据结构”。
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上
性能下降SQL
慢执行时间长
等待时商长
优劣势
类似大学图书馆建书目索引,提高数据检索的效率,降低数据库的IO成本
通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗
虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,
都会调整因为更新所带来的键值变化焙的索引信息
实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的
适合 不适合
哪些情况需要创建索引
- 主键自动建立唯一索引
- 频繁作为查询条件的字段应该创建索引
- 查询中与其它表关联的字段,外键关系建立索引
- 两表关联查询会更快
- 单键/组合索引的选择问题,组合索引性价比更高
- mysql 会 使用最优的索引
- 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
- 查询中统计或者分组字段
- 分组更浪费性能(分组是 先排序,后分组)
哪些情况不要创建索引
- 表记录太少
- 经常增删改的表或者字段
- Where条件里用不到的字段不创建索引
- 过滤性不好的不适合建索引
- 比如 人的性别
- 过滤性好的:比如 手机号,身份证
5、 JVM垃圾回收机制
VM垃圾回收机制,GC发生在JVM哪部分,有几种Gc,它们的算法是什么
- 堆中
分几种
CC是什么(分代收集算法)
- 次数上频繁收集Young区 Minor Gc
- 年轻代 频繁收集,轻GC
- 使用复制算法
- 次数上较少收集Old区 Full Gc
- 使用标记清除压缩算法
- 基本不动Perm区
- 永久区
minor
英
/ˈmaɪnə(r)
adj.
较小的,次要的,轻微的;(疾病)不严重的,(手术)小的;小
n.
未成年人;辅修课程;(美国大学中的)辅修生;(乐)小调,小音程;
v.
辅修
permanent
英
/ˈpɜːmənənt
adj.
永久的,永恒的;(尤指问题或困难)不断出现的,一直存在的;(员工)终生的,长期的;(家庭住址)固定的
算法
1.引用计数法
-
Python
-
只要对象有引用,就不进行垃圾回收
-
JVM的实现一般不采用这种方式
-
缺点∶
·每次对对象赋值时均要维护引用计数器,且计数器本身也有一定的消耗;- ·较难处理循环引用
2.复制算法(Copying)
- 年轻代中使用的是Minor GC,这种GC算法采用的是复制算法(Copying)
- 从根集合(GC Root)开始,通过Tracing从From中找到存活对象,拷贝到To中;From . To交换身份,下次内存分配从To开始;
- 没有标记和清除的过程,效率高没有内存碎片,
- 可以利用bump-the-pointer实现快速内存分配
- 需要双倍空间
- 谁空谁是to 区,假如 各有1个。会把 1个 复制到 另一个区域。
- 15次 GC,还没有死的时候,进入 养老区。
3.标记清除(Mark-Sweep)
- 老年代一般是由标记清除或者是标记清除与标记整理的混合实现
- 从根集合开始扫描,对存活的对象进行标记。
- 扫描整个内存空间,回收未被标记的对象,使用free-list记录可以区域。
- 两次扫描,耗时严重;
- 会产生内存碎片
- 不需要额外空间
4.标记压缩(Mark-Compact)
- 老年代一般是由标记清除或者是标记清除与标记整理的混合实现
- 标记(Mark) :
与标记-清除一样。 - 压缩(Compact) :
再次扫描,并往一端滑动存活对象。 - 在整理压缩阶段,不再对标记的对像做回收,而是通过所有存活对像都向一端移动,然后直接清除边界以外的内存
- 没有内存碎片,可以利用bump-the-pointer
- 需要移动对象的成本
5.标记清除压缩( Mark-Sweep-Compact)
- 先标记多次,进行清除一次,清除了多次后,在压缩。
- Mark-Sweep 和 Mark-Compact的结合。
和Mark-Sweep一致,当进行多次GC后才Compact。- 减少移动对象的成本
类加载
Class files
- 载器子系统Class loader
- 运行时数据区(Runtime Data Area)
- 方法区Method Area
- 堆heap
- Java栈Java stack
- 本地方法栈 Native Method Stack
- 程序计数器Program Counter Register
- 执行引擎 Execution Engine
- 本地方法接口Native Interface
- 本地方法库
三种JVM
- ① Sun公司的HotSpot ,平时用
- ② BEA公司的JRockit
- ③ IBM公司的J9 JVM
- j9vm JIT
堆中区域
堆内存中还要细分为三个区域:
-
新生区(伊甸园区 Eden Space)Young/New·
- **(伊甸园区),**student对象 第一次没有被回收,进入幸存区
-
幸存区0区
- 幸存区1区
**养老区old 老年代 **
**永久区Perm 元空间 持久代 **
- 方法区 是所有对象共享的。在堆里,叫非堆。
- 元空间 逻辑上存在,物理上 不存在。
static 静态变量、final 常量、(class类模板) 类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关
jdk1.6之前:永久代,常量池是在方法区;
jdk1.7:永久代,但是慢慢的退化了,去永久代,常量池在堆中
jdk1.8之后:无永久代,常量池在元空间
调优 和 排错
Runtime.getRuntime().maxMemory();//使用-Xmx10m 调节。最大分配内存。默认为1/4。
Runtime.getRuntime().totalMemory();//使用-Xms5m调节。初始化内存分配大小。默认1/64
-Xms1024m -Xmx1024m -XX:+PrintGCDetails ///打印GC垃圾回收
-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError //oom DUMP 堆dump在 内存溢出错误
- Jprofiler
JMM
- JavaMemory Model
8种内存交互
lock(锁定)——synchronized 和 ReentrantLock,锁的方式。主存变量加锁。
unlock(解锁)
read(读取):变量的值主存 传到 工作内存
load(载入):载入到 工作内存的变量副本。
use(使用):工作内存变量 传递给 执行引擎
assign(赋值):执行引擎 赋值 给工作内存的变量。
store(存储):工作内存变量 传输到 主内存中
write(写入):放入到主内存的变量中。
主存(主存变量) 工作内存(变量副本) 执行引擎
-
主存——》工作内存 读取
- 工作内存——》变量副本 载入 自己那块操作
-
工作内存——》主存 存储
- 主存——》主存变量 写入。 自己那块操作
-
工作内存——》执行引擎 使用
-
执行引擎——》工作内存 赋值
JMM对这八种指令的使用,制定了如下规则:
- 不许单独出现
- 改变后,要告知
- 没有 赋值的数据,不能同步到主存
- 变量必须内存中诞生
- 同一时间 只能一个 lock
- 没被lock,不能 unlock
- unlock之前,必须同步到主存。
volatile关键字的作用
1、保证可见性;
2、防止指令重排;
3、但是不保证原子性;
- 其他线程从主内存空间把值拷贝到自己的工作空间,线程修改之后的值会返回给主内存,主内存会通知其他线程,此为可见性。
transient
- 被transient修饰的变量不参与序列化和反序列化。
JIT全程Java Intime Compiler,即Java即时编译器。
6. Redis使用场景
数据类型 | 使用场景 |
---|---|
String | 比如说 ,我想知道什么时候封锁一个IP地址。Incrby命令 |
Hash | **存储用户信息【id,name,age】**Hset(key,field,value)Hset(userKey,id,101)Hset(userKey,name,admin)Hset(userKey,age,23)----修改案例----Hget(userKey,id)Hset(userKey,id,102)为什么不使用String 类型来存储Set(userKey,用信息的字符串)Get(userKey)不建议使用String 类型 |
List | 实现最新消息的排行,还可以利用List的push命令,将任务存在list集合中,同时使用另一个命令,将任务从集合中取出[pop]。Redis—list数据类型来模拟消息队列。【电商中的秒杀就可以采用这种方式来完成一个秒杀活动】 |
Set | 特殊之处:可以自动排重。比如说微博中将每个人的好友存在集合(Set)中,这样求两个人的共通好友的操作。我们只需要求交集即可。 |
Zset | 以某一个条件为权重,进行排序。京东:商品详情的时候,都会有一个综合排名,还可以按照价格进行排名。 |
-
geospatial 地理空间
-
hyperloglog 基数
- A{1,3,5,7,8,7}
- B{1,3,5,7,8]
基数(不重复的元素)= 5
-
bitmaps 位图
- 星期一 打卡成功了
7. es 和 solr
背景:它们都是基于Lucene搜索服务器基础之上开发,一款优秀的,高性能的企业级搜索服务器。【是因为他们都是基于分词技术构建的倒排索引的方式进行查询】
开发语言:java语言开发
诞生时间:
Solr :2004年诞生。
Es:2010年诞生。
Es 更新【功能越强大】
区别:
\1. 当实时建立索引的时候,solr会产生io阻塞,而es则不会,es查询性能要高于solr。
\2. 在不断动态添加数据的时候,solr的检索效率会变的低下,而es则没有什么变化。
\3. Solr利用zookeeper进行分布式管理,而es自身带有分布式系统管理功能。Solr一般都要部署到web服务器上,比如tomcat。启动tomcat的时候需要配置tomcat与solr的关联。【Solr 的本质 是一个动态web项目】
\4. Solr支持更多的格式数据[xml,json,csv等],而es仅支持json文件格式。
\5. Solr是传统搜索应用的有力解决方案,但是es更适用于新兴的实时搜索应用。
a) 单纯的对已有数据进行检索的时候,solr效率更好,高于es。
\6. Solr官网提供的功能更多,而es本身更注重于核心功能,高级功能多有第三方插件。
8、单点登录
- 在搜索页面登录了。https://searche.jd.com
- 在商品详情:https://item.jd.com 也有登录信息。
单点登录:一处登录多处使用!
前提:单点登录多使用在分布式系统中。
用户 | 认证中心 | WEB引用 |
---|---|---|
检查cookie是否有token | ||
不存在token、重定向到认证中心 | ||
提示用户登录、注册 | ||
用户填写登录信息 | 核对用户信息 | |
-用户登录成功- | ||
带token跳转原WEB应用- | 如果是带token的跳转,则写入cookie中,并继续打开业务功能页面。 | |
用户继续访问 | 检查cookie | |
存在token,提交认证中心认证 | ||
认证身份 | ||
—业务功能页面一 |
Demo:
参观动物园流程:
检票员=认证中心模块
\1. 我直接带着大家进动物园,则会被检票员拦住【看我们是否有门票】,没有[售票处买票]
登录=买票
\2. 我去买票【带着票,带着大家一起准备进入动物园】检票员check【有票】
Token=piao
\3. 我们手中有票就可以任意观赏动物的每处景点。
京东:单点登录,是将token放入到cookie中的。
案例:将浏览器的cookie禁用,则在登录京东则失败!无论如何登录不了!
9、购物车实现过程
购物车实现过程
购物车:
\1. 购物车跟用户的关系?
-
a) 一个用户必须对应一个购物车【一个用户不管买多少商品,都会存在属于自己的购物车中。】
-
b) 单点登录一定在购物车之前。
\2. 跟购物车有关的操作有哪些?
a) 添加购物车
i. 用户未登录状态
-
\1. 添加到什么地方?未登录将数据保存到什么地方?
-
a) Redis? — 京东
- 未登录,起一个 UUD
-
b) Cookie? — 自己开发项目的时候【如果浏览器禁用cookie】
- 存储在:local Storage
ii. 用户登录状态
-
\1. Redis 缓存中 【读写速度快】
- a) Hash :hset(key,field,value)
- i. Key:user:userId:cart
- ii. Hset(key,skuId,value);
-
\2. 存在数据库中【oracle,mysql】
b) 展示购物车
i. 未登录状态展示
- \1. 直接从cookie 中取得数据展示即可
ii. 登录状态
- \1. 用户一旦登录:必须显示数据库【redis】+cookie 中的购物车的数据
- a) Cookie 中有三条记录
- b) Redis中有五条记录
- c) 真正展示的时候应该是八条记录
10、消息队列在项目的使用
背景:在分布式系统中是如何处理高并发的。
由于在高并发的环境下,来不及同步处理用户发送的请求,则会导致请求发生阻塞。比如说,大量的insert,update之类的请求同时到达数据库MYSQL,直接导致无数的行锁表锁,甚至会导致请求堆积很多。从而触发 too many connections 错误。使用消息队列可以解决【异步通信】
\1. 异步
\1. 并行
\1. 排队
消息队列电商使用场景:
- 支付宝回调 支付模块,
- 通知订单模块
- 通知库存模块,都是使用mq
消息队列的弊端:
消息的不确定性:延迟队列,轮询技术来解决该问题即可!
推荐大家使用activemq!环境都是java。
- 不如:rabbitMq
RabbitMq:8种工作模式
1 “Hello World!” 简单模式
- The simplest thing that doessomething 简单模式,一个生产者,一个消费者
- 发送到默认交换器,此时要指定 routingKey 默认为 队列名
2 Work queues 工作队列模式
- 轮询分发,公平分发。
- 1、轮询模式的分发:一个消费者一条,按均分配;
- 默认的模式
- **2、公平分发:**根据消费者的消费能力进行公平分发,处理快的处理的多,处理慢的处理的少;按劳分配;
- 一定是手动应答
- 设置:Quality of Service,服务质量
3 Publish/Subscribe 发布订阅模式
-
fanout 是发布订阅模式,指定 Routing Key 没意义。
-
就好像:大家都在听一个直播间讲课。都能收到消息。
-
exchange 交换机
4 Routing 路由模式
-
路由模式direct 就是 发布订阅模式fanout,增加了 Routing Key
-
比如 注册用户,只需要微信 和 邮件 收到消息。不希望其他收到。
5 Topics 主题模式
- 增加了 模糊的路由Key
- *代表一个。#代表多个。
6 参数模式
- q1增加arguments为: X=1
- 发送消息,Headers参数为:X=1
7 RPC
8 Publisher Confirms
高可用机制
反正终归三句话:
1:要么消息共享,
2:要么消息同步
3:要么元数据共享
进入死信队列
- 消息被拒绝
- 消息过期
- 队列达到最大长度
- args.put(“x-max-length”,5;);队列对多接收5个。
分布式事务
- 两阶段提交(Two-phase Commit,2PC)
- 准备阶段
- 提交阶段
- 补偿事务(TCC)
- try Confirm Cancel
- 主动查询支付状态,对账单的形式
- MQ 事务消息 异步场景
可靠生产
//其实就是给 rabbitTemplate 扩展了方法。
//消息发送成功以后,给子生产者的消息回执,来确保生产者的可靠性
rabbitTemplate.setConfirmCallback(
new RabbitTemplate.ConfirmCallback() {
//如果没有ack成功
if (!ack) {
//应答失败,也可以存到 其他地方
System.out.println("mq应答失败");
return;
}
System.out.println("应答成功了");
//应答成功,更新数据库,状态改为1 (已发送到Mq)
}
)
//这里要插入 冗余表,状态为0,未投递成功。上面回调的方法 会改为1。
//另一个线程 定时任务,定时投递 为0的消息。
可靠消费
- 使用 手动Ack的确认机制
- 默认消费者出错,会触发重试,在错,在重试。引发死循环。
- 解决消息重试的集中方案:
- 控制重发的次数 + 死信队列
- 如果不加死信队列,重试次数到了,会扔了。
- try+catch+手动ack
- try+catch+手动ack +死信队列处理+人工干预
- 控制重发的次数 + 死信队列
- 解决消息重试的集中方案:
System.out.println("email direct模式收到了消息" + message + "===" + count++);
try {
int i = 1 / 0;
System.out.println(i);
//手动ack已经正常消费
channel.basicAck(tag, false);
} catch (Exception e) {
//如果出现异常的情况下,根据实际的情况去进行重发
//重发一次后.丢失.还是日记.存库根据自己的业务场景去决定
//参数1:消息的tag参数 2: false多条处理参数 3: requeue重发
// false不会重发,会把消息打入到死信队列
// true 的会会死循环的重发,建议如果使用true的话,不加try/catch否则就会造成死循环
channel.basicNack(tag, false, false);//单条 不重发。直接扔掉(进入死信队列)
}
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)