一、基礎
1.1 作業系統
1.1.1 基本知識
略
1.1.2 常見Linux指令
1.2 Java基礎
1.2.1 Hashmap
JDK1.7中
使用一個Entry數組來存儲資料,用key的hashcode取模來決定key會被放到數組裡的位置,如果hashcode取模後的結果相同,那麼這些key會被定位到Entry數組的同一個格子裡,這些key會形成一個連結清單;這樣資料周遊時間就過長。
1.7中hashmap使用的是頭插法。
JDK1.8中
使用一個Node數組來存儲資料,但是這個Node可能是連結清單結構,也可能是紅黑樹結構;如果插入的元素key的hashcode值相同,那麼這些key也會被定位到Node數組的同一個格子裡,如果不超過8個使用連結清單存儲,超過8個,會将連結清單轉換為紅黑樹。
1.8中hashmap使用的是使用尾插法.
1.2.2 ConcurrentHashmap
主要是支援安全的多線程的讀寫
1.2.3 Spring Bean加載過程
1.2.4 多線程CAS
CAS(Compare and swap 比較并交換) 一種CPU指令級的操作,保證資料一緻性。記憶體值V、預期值A(備份值)、新值B V=A 更新值。在多線程情況下保證值的一緻性。
1.2.5 equals(值相等) ==(引用相等)
java對象分為基礎類型(8大基礎類型 int short float long double byte char)與引用類型
- 基礎類型 equals ==相同均為值相等
- 引用類型(string) equals源代碼中可以看到:
JDK自帶的equals有兩種,針對Object對象及String對象
1、String中的equals
==判斷是否相等,相等直接傳回true->再判斷是否為string,否直接傳回false->true,繼續判斷對象length->循環判斷char是否相等
2、Object中的equals
直接return (this == obj),一般業務對象比較要改造equals方法
1.3 網絡
1.3.1 Https
- https單向加密(數字信封)
核心為公鑰加密,私鑰解密
- https雙向身份驗證
在單向身份驗證基礎上增加伺服器端對用戶端的證書驗證
1.3.2 TCP
【問題1】為什麼需要三次握手?
相對UDP,TCP是可靠的通訊協定,是全雙工通信。TCP三向交握的關鍵在于,序列号seq的交換确認,因為對于用戶端和服務端來說,雙方序列号的确認是可靠傳輸的關鍵。1、2步握手隻能确定發送方發和收正常,并不能确定接收方也是發和收正常,增加了第3次握手,才能保證接收方也是發和收都正常。
【問題2】為什麼連接配接的時候是三次握手,關閉的時候卻是四次揮手?
由于伺服器端回複已經響應完畢,此時用戶端并不是立刻就收完了,是以伺服器處于半關閉狀态,等用戶端完全處理完,收到通知後才完全關閉,固為4次。
【問題3】怎麼簡單描述三次握手,四次揮手?**
就像是C與S發微信一樣
三次握手:C與S
C:我要給你發資料了
S:好的,我準備好了,你發吧
C:好的,收到
四次揮手:C與S
C:我的資料發完了
S:好的,我聽到了,我看看收完了沒
S:好的,已經收完了,你關閉吧
C:好的,已經關閉了
1.3.3 Http2.0
- 優點
相對http1.x性能提升(二進制分幀、多路複用、頭部壓縮)
- 目前很多平台已開始使用http2.0
如 zhihu taobao google github juejin 等,可以打開F12看到協定這列為"h2"
1.3.4 Websocket
一般用戶端向伺服器發送請求後伺服器會回應響應。但伺服器不會主動向用戶端發送請求。響應式的方式可以解決此類問題。當然傳統的方式也可以達到相同的效果比如:輪詢、http長連接配接。
1.3.5 Http Code
1.4 JVM
1.4.1 jvm記憶體模型
為了屏蔽各種硬體和作業系統對記憶體通路的差異,java定義了JVM記憶體模型
主記憶體->工作記憶體->線程
java記憶體模型和java運作時資料區域的關系:主記憶體對應着java堆和方法區,工作記憶體對應着java棧。
1.4.2 jvm運作時記憶體
1.4.3 GC
- GC種類
- GC三種收集方法:
标記清除、标記整理、複制算法
标記清除:先标記,标記完畢之後再清除,效率不高,會産生碎片
标記整理:标記完畢之後,讓所有存活的對象向一端移動
複制算法:分為 8:1 的 Eden 區和 survivor 區,就是上面談到的 YGC
- GC收集器
CMS G1
1.4.4 類加載過程
1.4.5 常見問題
全局變量與局部變量在記憶體中的差別
- 局部變量存儲在棧中
- 全局變量(java中無全局變量概念,java中叫成員變量)
成員變量均存儲在方法區中,J_VM隻是定義了方法這個概念,并沒有定義它的具體組成_
1、jdk1.7方法區(習慣上把永久代叫方法區)
2、jdk1.8方法區(由中繼資料區+堆組成),其中字元串常量池被放在堆中
_
jdk1.7的永久代在jdk1.8中去掉并換成中繼資料區
1.5 多線程
1.5.1 資料共享
1、多線程如何共享資料
- 線程代碼相同,即runnable中的代碼一緻,這樣可以直接共享
/**
* 賣票處理
* @author yang
*/
public class SellTicket {
//賣票系統,多個視窗的處理邏輯是相同的
public static void main(String[] args) {
Ticket t = new Ticket();
new Thread(t).start();
new Thread(t).start();
}
}
/**
* 将屬性和處理邏輯,封裝在一個類中
* @author yang
*/
class Ticket implements Runnable{
private int ticket = 10;
public synchronized void run() {
while(ticket>0){
ticket--;
System.out.println("目前票數為:"+ticket);
}
}
}
作者:那時年少輕狂
連結:https://www.imooc.com/article/17112?block_id=tuijian_wz
來源:慕課網
本文原創釋出于慕課網 ,轉載請注明出處,謝謝合作
- 線程代碼不相同,即runnable中的代碼不一緻,Runnable1 Runnable2,利用一個對象,把runnable中的方法封裝到這個對象中去,資料也在這個對象中
public class MultiThreadShareData {
public static void main(String[] args) {
ShareData data = new ShareData();
new Thread(new MyRunnable1(data)).start();
new Thread(new MyRunnable2(data)).start();
}
}
class MyRunnable1 implements Runnable {
private ShareData data;
public MyRunnable1(ShareData data) {
this.data = data;
}
public void run() {
data.decrement();
}
}
class MyRunnable2 implements Runnable {
private ShareData data;
public MyRunnable2(ShareData data) {
this.data = data;
}
public void run() {
data.increment();
}
}
class ShareData {
private int j = 10;
public synchronized void increment() {
j++;
System.out.println("線程:" + Thread.currentThread().getName() + "加操作之後,j = " + j);
}
public synchronized void decrement() {
j--;
System.out.println("線程:" + Thread.currentThread().getName() + "加操作之後,j = " + j);
}
}
作者:那時年少輕狂
連結:https://www.imooc.com/article/17112?block_id=tuijian_wz
來源:慕課網
本文原創釋出于慕課網 ,轉載請注明出處,謝謝合作
2、父子線程如果共享資料
通過 interitableThreadLocal實作共享
1.5.2 synchronized與lock差別
總結:建議使用synchronized,在jdk1.5之前lock優于synchronized,但在jdk1.5之後對synchronized進行了優化,後面在性能方面基本與lock一樣且使用簡單(有作者說"synchronized是親生的,jdk還是會一直優化他不會讓lock優于它")。
1.5.3 線程池(實作與原理)
ThreadPoolExecutor
1.6 設計模式
1.6.1 概述
可以發現,設計模式好像都是類似的。越看越感覺都着不多。其實都是類似面向接口程式設計的一種展現,隻不過側重點不一樣或者說要展現的結果不一樣。
1.6.2 使用場景
- 問題一:應對可能變化的對象實作方案:間接建立模式:工廠模式
-
問題二:為請求指定相應的操作(類似請假審批,不同時長對應不同職位的審批人)
方案:程式根據請求動态選擇操作
模式:責任鍊模式
1.6.3 具體說明
1、政策模式
政策模式說明
一個行為型模式,包含多個行為或職責的業務,通過政策模式簡化
public class StrategyContext {
Strategy strategy;
public StrategyContext(Strategy strategy) {
this.strategy = strategy;
}
/**
*
*/
public int context(int a, int b) {
return strategy.operator(a,b);
}
}
政策模式的核心為StrategyContext上下文類,持有strategy對象,在context完成操作。
測試類
public class StrategyContextTest {
public static void main(String[] args) {
Strategy strategy;
strategy = new OperationAdd();
StrategyContext strategyContext = new StrategyContext(strategy);
strategy.operator(5,2);
}
}
政策模式實踐
- 如何使用政策模式解決大量使用if else 或大量switch問題
政策模式+反射
政策模式後好像使用都還是要用if else來決定調用哪個類,是以在引入政策模式後,在上下文類還要增加反射。
public class StrategyContext {
Strategy strategy;
public StrategyContext(String type) throws Exception {
Class clazz = Class.forName(type);
this.strategy = (Strategy) clazz.newInstance();
}
/**
*
*/
public int context(int a, int b) {
return strategy.operator(a,b);
}
當然這裡的type可以用個枚舉來解決。感覺代價非常大是不是沒必要,不過代碼的可讀性還是增強了。
p.s. 在架構裡政策模式中的Context一般不會直接出現,類似spring中直接在使用時就通過注解給設定了
_
2、裝飾器模式
描述:原接口Shape不變,方法數量不變,在方法實作中增加修飾
場景:
場景一:一個類功能簡單,滿足不了我們的需求
場景二:給原方法增加日志功能,不改變原方法,新的實作類去實作此功能,帶入的對象為接口對應
特點
- 原接口Shape不動,增加新的裝飾類ShapeDecorator
- 原方法名不變,隻是增加或修飾此方法體
- ColorShapeDecorator裝飾類持有原對象,隻是增加了修飾
public class ColorShapeDecorator extends ShapeDecorator {
public ColorShapeDecorator(Shape shape) {
super(shape);
}
@Override
public void draw() {
setColor();
shape.draw();
}
private void setColor() {
//設定畫圖顔色
}
}
3、擴充卡模式
描述:原接口不變,增加方法數量
場景:
場景一:原接口不變,在基礎上增加新的方法。
場景二:接口的抽象方法很多,不想一一實作,使用擴充卡模式繼承原實作類,再實作此接口
- 擴充卡模式适合需要增加一個新接口的需求,在原接口與實作類基礎上需要增加新的接口及方法。類似原接口隻能method01方法,需求是增加method02方法,同時不再使用之前接口類。
新接口
public interface Targetable {
/**
*
*/
public void method01();
/**
*
*/
public void method02();
}
原接口實作類
public class Source {
public void method01() {
// TODO implement here
}
}
擴充卡類,用于實作新接口。繼承原實作類,同時實作新接口。
public class Adapter extends Source implements Targetable {
/**
*
*/
public void method02() {
// TODO implement here
}
}
測試類
public class AdapterTest {
public static void main(String[] args) {
Targetable targetable = new Adapter();
targetable.method01();
targetable.method02();
}
}
4、代理模式
一個類代表另一個類的功能
場景:
場景一:不改變原方法,對原方法增加耗時的計算
場景二:rpc遠端調用,client端進行動态代理類似耗時計算一樣,使用者不用關心client的具體實作
分類
- 靜态代理模式
- 動态代理模式
說明
- 靜态代理模式
/**
* 與擴充卡模式的差別,擴充卡模式主要改變所考慮對象的接口,
* 而代理模式不能改變所代理類的接口。與裝飾器模式的差別,
* 裝飾器模式是為了增強功能,代理模式是為了加以控制
*/
public class ProxySigntureService implements SigntureService {
private SigntureService signatureService;
/**
* Default constructor
*/
public ProxySigntureService(SigntureService signatureService) {
this.signatureService = signatureService;
}
public void sign() {
//控制對這個對象的通路
// 實作電子簽名
}
}
- 動态代理模式
public class DynamicProxySignatureService implements InvocationHandler {
private Object obj;
public DynamicProxySignatureService(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxyObj, Method method, Object[] objects)
throws Throwable {
return method.invoke(obj,objects);
}
}
參考文章:https://blog.csdn.net/liujiahan629629/article/details/19428485
5、單例模式
保證被建立一次,節省系統開銷。
1)單例實作方式
- 餓漢式
- 懶漢式
- 懶漢式+Synchronized
- 雙重校驗
- 靜态内部類
- 枚舉(推薦方式)
2)實作代碼
- 餓漢式
package com.hanko.designpattern.singleton;
/**
* 餓漢式 (餓怕了,擔心沒有吃,是以在使用之前就new出來)
*優點:實作簡單,安全可靠
*缺點:在不需要時,就已執行個體化了
* @author hanko
* @version 1.0
* @date 2020/9/14 18:50
*/
public class HungrySingleton {
//特點一 靜态私有變量 直接初始化
private static HungrySingleton instance = new HungrySingleton();
//特點二 構造函數私有
private HungrySingleton(){
}
public static HungrySingleton getInstance(){
return instance;
}
public void doSomething(){
//具體需要實作的功能
}
}
- 懶漢式
package com.hanko.designpattern.singleton;
/**
* 懶漢式(非常懶,是以在要使用時再去new)
*優點:簡單
*缺點:存線上程安全問題
* @author hanko
* @version 1.0
* @date 2020/9/14 18:50
*/
public class SluggardSingleton {
//特點一 靜态私有變量,先不初始化
private static SluggardSingleton instance;
//特點二 構造函數私有
private SluggardSingleton(){
}
//特點三 null判斷,沒有執行個體化就new
public static SluggardSingleton getInstance(){
if(instance == null){
instance = new SluggardSingleton();
}
return instance;
}
public void doSomething(){
//具體需要實作的功能
}
}
- 懶漢式+Synchronized
package com.hanko.designpattern.singleton;
/**
* 懶漢式(非常懶,是以在要使用時再去new)
*優點:簡單
*缺點:存線上程安全問題
* @author hanko
* @version 1.0
* @date 2020/9/14 18:50
*/
public class SluggardSingleton {
//特點一 靜态私有變量,先不初始化
private static SluggardSingleton instance;
//特點二 構造函數私有
private SluggardSingleton(){
}
//特點三 null判斷,沒有執行個體化就new
public static synchronized SluggardSingleton getInstance(){
if(instance == null){
instance = new SluggardSingleton();
}
return instance;
}
public void doSomething(){
//具體需要實作的功能
}
}
- 雙重校驗
package com.hanko.designpattern.singleton;
/**
* 雙重校驗
*對懶漢式單例模式做了線程安全處理增加鎖機制
* volatile變量級
* synchronized 類級
* @author hanko
* @version 1.0
* @date 2020/9/15 9:53
*/
public class DoubleCheckSingleton {
//特點一 靜态私有變量,增加volatile變量級鎖
private static volatile DoubleCheckSingleton instance;
//特點二 構造函數私有
private DoubleCheckSingleton(){
}
//特點三 雙重null判斷 synchronized類級鎖
public static DoubleCheckSingleton getInstance(){
if (instance == null){
synchronized(DoubleCheckSingleton.class){
if (instance == null){
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
- 靜态内部類
package com.hanko.designpattern.singleton;
/**
* 内部靜态類方式
*優點:靜态内部類不會在InnerStaticSingleton類加載時加載,
* 而在調用getInstance()方法時才加載
*缺點:存在反射攻擊或者反序列化攻擊
* @author hanko
* @version 1.0
* @date 2020/9/15 10:03
*/
public class InnerStaticSingleton {
//特點一:構造函數私有
private InnerStaticSingleton(){
}
//特點二:靜态内部類
private static class InnerSingleton{
private static InnerSingleton instance = new InnerSingleton();
}
public InnerSingleton getInstance(){
return InnerSingleton.instance;
}
public void doSomething(){
//do Something
}
}
- 枚舉(推薦方式)
package com.hanko.designpattern.singleton;
/**
* 枚舉實作單例簡單安全
*
* @author hanko
* @version 1.0
* @date 2020/9/14 19:01
*/
public enum EnumSingleton {
INS;
private Singleton singleton;
EnumSingleton() {
singleton = new Singleton();
}
public void doSomething(){
singleton...
//具體需要實作的功能
}
}
EnumSingleton.INS.doSomething();
6、工廠模式
(簡單工廠、抽象工廠):解耦代碼。
簡單工廠:用來生産同一等級結構中的任意産品,對于增加新的産品,無能為力。
工廠方法:用來生産同一等級結構中的固定産品,支援增加任意産品。
抽象工廠:用來生産不同産品族的全部産品,對于增加新的産品,無能為力;支援增加産品族。
參考文章:https://zhuanlan.zhihu.com/p/248497545
7、觀察者模式
定義了對象之間的一對多的依賴,這樣一來,當一個對象改變時,它的所有的依賴者都會收到通知并自動更新。
8、外觀模式
提供一個統一的接口,用來通路子系統中的一群接口,外觀定義了一個高層的接口,讓子系統更容易使用。
9、狀态模式
允許對象在内部狀态改變時改變它的行為,對象看起來好像修改了它的類。與政策模式類似,政策模式側重點在一個事的不同實作方式抽離出來,而狀态模式是一個事的不同狀态抽離出來(開始、進行中、結束),每次狀态完成自己的業務邏輯。
總結:
- 擴充卡模式(原功能不變,增加新功能)、裝飾器模式(裝飾原功能)、代理模式(控制原功能)
- 政策模式側重點在一個事的不同實作方式抽離出來,而狀态模式是一個事的不同狀态抽離出來(開始、進行中、結束),每次狀态完成自己的業務邏輯。
二、資料存儲
2.1 緩存
2.1.1 緩存類型
- 本地緩存
- 分布式緩存
- 多級緩存(本地+分布式)
2.1.2 淘汰政策
- FIFO淘汰最早資料
- LRU最近最少使用(存的最久、用的最少)
使用LinkedHashMap實作LRU
1)LinkedHashMap被get過的元素會自動放在尾項
2)LinkedHashMap連結清單被删除不用補位
- LFU
2.1.3 緩存實作
- LRUCache(通過LinkedHashMap本地實作)
- Memcache
- Redis
- MongoDB
2.1.4 緩存穿透、擊穿、雪崩
1、緩存穿透 (緩存、DB均無,穿透)
- 簡述
緩存穿透是指查詢一個一定不存在的資料,由于緩存是不命中時被動寫的,并且出于容錯考慮,如果從存儲層查不到資料則不寫入緩存,這将導緻這個不存在的資料每次請求都要到存儲層去查詢,失去了緩存的意義。在流量大時,可能DB就挂掉了,要是有人利用不存在的key頻繁攻擊我們的應用,這就是漏洞。
- 解決方案
有很多種方法可以有效地解決緩存穿透問題,最常見的則是采用布隆過濾器,将所有可能存在的資料哈希到一個足夠大的bitmap中,一個一定不存在的資料會被 這個bitmap攔截掉,進而避免了對底層存儲系統的查詢壓力。另外也有一個更為簡單粗暴的方法(我們采用的就是這種),如果一個查詢傳回的資料為空(不管是數 據不存在,還是系統故障),我們仍然把這個空結果進行緩存,但它的過期時間會很短,最長不超過五分鐘。
2、緩存擊穿(緩存無、DB有,擊穿)
- 簡述
對于一些設定了過期時間的key,如果這些key可能會在某些時間點被超高并發地通路,是一種非常“熱點”的資料。這個時候,需要考慮一個問題:緩存被“擊穿”的問題,這個和緩存雪崩的差別在于這裡針對某一key緩存,前者則是很多key。
緩存在某個時間點過期的時候,恰好在這個時間點對這個Key有大量的并發請求過來,這些請求發現緩存過期一般都會從後端DB加載資料并回設到緩存,這個時候大并發的請求可能會瞬間把後端DB壓垮。
- 解決方案
1)使用互斥鎖(mutex key)
業界比較常用的做法,是使用mutex。簡單地來說,就是在緩存失效的時候(判斷拿出來的值為空),不是立即去load db,而是先使用緩存工具的某些帶成功操作傳回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一個mutex key,當操作傳回成功時,再進行load db的操作并回設緩存;否則,就重試整個get緩存的方法。
SETNX,是「SET if Not eXists」的縮寫,也就是隻有不存在的時候才設定,可以利用它來實作鎖的效果。在redis2.6.1之前版本未實作setnx的過期時間,是以這裡給出兩種版本代碼參考:
2) "提前"使用互斥鎖(mutex key):
在value内部設定1個逾時值(timeout1), timeout1比實際的memcache timeout(timeout2)小。當從cache讀取到timeout1發現它已經過期時候,馬上延長timeout1并重新設定到cache。然後再從資料庫加載資料并設定到cache中。僞代碼如下:
3) "永遠不過期":
這裡的“永遠不過期”包含兩層意思: (1) 從redis上看,确實沒有設定過期時間,這就保證了,不會出現熱點key過期問題,也就是“實體”不過期。(2) 從功能上看,如果不過期,那不就成靜态的了嗎?是以我們把過期時間存在key對應的value裡,如果發現要過期了,通過一個背景的異步線程進行緩存的建構,也就是“邏輯”過期 從實戰看,這種方法對于性能非常友好,唯一不足的就是建構緩存時候,其餘線程(非建構緩存的線程)可能通路的是老資料,但是對于一般的網際網路功能來說這個還是可以忍受。
3、緩存雪崩(一批過期的key)
- 簡述
緩存雪崩是指在我們設定緩存時采用了相同的過期時間,導緻緩存在某一時刻同時失效,請求全部轉發到DB,DB瞬時壓力過重雪崩。
- 解決方案
緩存失效時的雪崩效應對底層系統的沖擊非常可怕。大多數系統設計者考慮用加鎖或者隊列的方式保證緩存的單線程(程序)寫,進而避免失效時大量的并發請求落到底層存儲系統上。一個簡單方案就是将緩存失效時間分散開(過期時間錯開),比如我們可以在原有的失效時間基礎上增加一個随機值,比如1-5分鐘随機,這樣每一個緩存的過期時間的重複率就會降低,就很難引發集體失效的事件。
2.2 Redis
1、支援資料類型
String Hash List Set ZSet
- 大多數我們會把資料序列化成json也就是string存儲
- zset可以實作排行榜
2、分布式鎖
1)Redis實作分布式鎖面臨的主要問題
就是能否保證分布式鎖的原子性,主要展現在以下兩點:
- 未釋放
- 誤釋放
2)詳細說明
a、未釋放
- 過期未釋放
b、誤釋放
- 業務未完成,由于過時被釋放
- 被其它線程釋放
3)實作方案
- 手動實作(setnx-expire) 如果不存在寫入,如果存在就不寫
- 手動實作更新版(set ex px nx)
- Redisson
- Springboot LockRegistry(推薦)
p.s. 手動實作方案需程式解決以上兩問題,使用第三方的用戶端或元件一般自帶解決以上問題
3、單線程的redis為什麼快
- 記憶體讀寫
- 單線程無線程切換
- 采用了非阻塞I/O多路複用機制
2.3 MySQL
2.31. MySql調優
先引用一張圖檔
目錄圖
1、SQL語句優化
- 開啟慢查詢功能
vim /etc/my.cnf
[mysqld]
slow-query-log = on # 開啟慢查詢功能
slow_query_log_file = /data/slow-query.log # 慢查詢日志存放路徑與名稱
long_query_time = 5 # 查詢時間超過5s的查詢語句
log-queries-not-using-indexes = on # 列出沒有使用索引的查詢語句
1)檢視所有日志狀态: show variables like '%quer%';
2)檢視慢查詢狀态:show variables like 'show%'
- 分析SQL語句
MySql内部函數explain(查詢sql的執行計劃),explain傳回各列的含義
table:顯示這一行的資料是關于哪張表的
type:這是重要的列,顯示連接配接使用了何種類型。
從最好到最差的連接配接類型為const、eq_reg、ref、range、index 和ALL
possible_keys:顯示可能應用在這張表中的索引。如果為空,沒有可能的索引。
key:實際使用的索引。如果為NULL,則沒有使用索引。
keyjen:使用的索引的長度。在不損失精确性的情況下,長度越短越好
ref:顯示索引的哪一列被使用了,如果可能的話,是一個常數
rows: MYSQL認為必須檢查的用來傳回請求資料的行數
- 子查詢優化
子查詢盡量不用或改成join
- group by 優化
group by 盡量使用索引字段
- limit 優化
給查詢語句增加limit
2、索引優化
- 重複索引
- 備援索引
- 檢查重複及備援索引的工具
- 删除不用的索引
3、資料庫結構優化
- 選擇合适的資料類型
- 表的範式化
- 表的反範式化的使用
- 表的垂直拆分(列拆分)
- 表的水行拆分(行拆分)
4、配置優化
- 作業系統配置
1)緩存池大小
2)打開檔案限制
- MySQL配置
1)innodb緩沖池記憶體占用大小
2)innodb_buffer_pool_instances 緩沖池個數
3)innodb_log_buffer_size 緩沖的大小
4)innodb IO配置
5、伺服器硬體優化
- CPU 多核
- 硬碟 raid0 raid1 raid5增加硬碟IO速度
2.3.2 MySql基礎問題
InnoDB myisam差別
- innodb支援事務、myisam不支援事務
- innodb不支援全文索引、myisam支援全文索引,查詢性能較快
- innodb行級鎖、myisam表級鎖
三、搜尋引擎
3.1 反向索引
何為反向索引?首先要了解索引表:由關鍵詞為key,關鍵詞位置屬性為value組成的一張表。由于該表不是由key來确定value值,而是由value的屬性值來确定key的位置,是以稱為反向索引,帶有反向索引的檔案稱為倒排檔案。通俗的講反向索引就好比書的目錄,通過目錄咱們可以準确的找到相應的資料。下面對lucene反向索引的結構與算法進行介紹。使用場景
反向索引服務于es查詢操作,對資料的聚合,排序則需要使用正排索引,下面我們介紹正排索引。
3.2 ES
Elasticsearch
3.3 熱點分析(詞頻統計)
方案一、基于ElasticSearch方式
方案二、基于Spark方式
方案三、基于Python方式
方案一、基于ElasticSearch方式
詳見文章,裡面列舉了各種ElasticSearch的實作樣例。主要是通過ES的fielddata做聚合或調取termvector兩種方式實作,當然調用方式比較多http requset、highclient、springboot data repository、elasticsearch resttemplate等調用方式
https://zhuanlan.zhihu.com/p/315888125
方案二、基于Spark方式
Spark是基于記憶體的分布式計算元件,Spark官方提供了JavaWordCount的demo,詳見以下文章
https://zhuanlan.zhihu.com/p/329967589
方案三、基于Python方式
如果你隻能實作簡單的資料詞頻統計,python是最的方式,幾行代碼就可以搞定
text = "http requset highclient springboot"
data = text.lower().split()
words = {}
for word in data:
if word not in words:
words[word] = 1
else:
words[word] = words[word] + 1
result = sorted(words.items(), reverse=True)
print(result)
參考文章 :https://zhuanlan.zhihu.com/p/333358727
四、大資料
- spark
- hive
- hadoop
五、安全
- SQL注入
- XSS注入(跨站腳本攻擊)
- CSRF(跨域攻擊)
六、服務架構
6.1 微服務架構
- 第一代微服務架構
springcloud dubbo
- 第二代微服務架構
service mesh
未來的微服務架構與技術棧
6.2 SpringCloud
網關 zuul/gateway
服務注冊發現 eureka/nacos/consul
配置中心 configserver/nacos/apollo
熔斷降級 hystrix/sentinel
監控 springbootadmin
鍊路器 sleuth+zipkin
6.3 Dubbo
- 支援哪些協定
http dubbo rmi hessian webservice
- dubbo的幾種角色
- 注冊中心
zookeeper
- dubbo有哪幾種配置方式
xml 注解 properties API
- 序列化
hessian dubbo fastjson java自帶序列化
七、性能調優
7.1 Java項目性能調優
7.1.1 jstat jconsole
7.1.2 Xrebel
7.1.3 Arthas
7.1.3.1 常用指令
- cpu占用過高
thread
- 死鎖
thread -b
- 記憶體洩漏
dashboard
- 方法耗時及跟蹤
trace
- 看源代碼
jad
- 檢視函數的參數/傳回值/異常資訊
watch com.Welfare batchQuerySku "{params}"
- 發現異常并檢視異常
tt -t com.UserServiceImpl check //記錄方法調用資訊
tt -i 1001 //上面指令發現異常後,檢視異常
tt -i 1001 -p //重新調用,重制異常
- 檢視、更新類成員變量值
ognl '@com.Arthas@hashSet' //檢視
ognl '@[email protected]("test")' //更新
7.1.3.2 項目實戰
- 針對web項目,可以跟蹤servlet類
trace org.springframework.web.servlet.DispatcherServlet *
- 跟蹤後通過watch檢視出入參(發現執行的controller類)
watch org.springframework.web.method.support.InvocableHandlerMethod doInvoke "{params,returnObj}"
- 記錄方法調用資訊
tt -t com.wdbyte.arthas.service.UserServiceImpl mysql
- 通過tt -t 檢視到index,然後通過index重複調用
tt -i 1001 -p
- 最後通過tt -i 與 trace重複配合,定位出性能消耗
八、部署、內建
- Jekins
- Sonarquber
- Docker
- Podman
- K8s
九、項目過程
項目中的問題及解決方案
9.1 架構方面
1、傳統架構改造成微服務架構
- 微服務拆分時,資料庫問題
問題說明:微服務拆分後,對應的資料庫也會進行拆分。也就是原本很簡單功能一個查詢就解決,拆分後就得跨多個微服務查詢。如果其中一張表資料較少可以代碼循環的方式解決,如果每個微服務的資料都比較多就比較麻煩。
方案一:使用視圖,通過視圖把兩個庫的中表進行關聯。當然這樣也就違避微服務的解耦
方案二:臨時表或緩存,通過臨時表或緩存建構臨時關聯資料,使用完就清理
方案三:資料備援,犧牲空間換時間。類似索引,犧牲索引空間換查詢速度。
建議使用方案三
- 微服務拆分粒度
微服務粒度問題,多次讨論拆分架構。拆分是一個疊代的過程,别試着一步到位拆成很細,一開始千萬别拆太細
十、項目實戰
10.1 排行榜
- 方案一:mysql order by
網際網路使用者資料過大,排行榜性能較差
- 方案二:redis
redis zset 資料結構為 id、score、member。下面是具體實作的步驟
1)插入資料
zadd game 90 tom
zadd game 92 sam
zadd game 95 hanko
2)擷取範圍内的排名
zrange game 0 -1 withscores //開始到集合結束的從小到大清單,-1代表結束
zrevrange game 0 -1 withscores //開始到集合結束的從大到小清單
3)擷取某使用者的排名
zrank game hanko
zrevrank game hanko
10.2 秒殺系統
10.2.1 秒殺系統特點
- 秒殺時大量使用者會在同一時間同時進行搶購,網站瞬時通路流量激增。
- 秒殺一般是通路請求數量遠遠大于庫存數量,隻有少部分使用者能夠秒殺成功。
- 秒殺業務流程比較簡單,一般就是下訂單減庫存。
10.2.2 秒殺系統設計理念
限流: 鑒于隻有少部分使用者能夠秒殺成功,是以要限制大部分流量,隻允許少部分流量進入服務後端。
削峰:對于秒殺系統瞬時會有大量使用者湧入,是以在搶購一開始會有很高的瞬間峰值。高峰值流量是壓垮系統很重要的原因,是以如何把瞬間的高流量變成一段時間平穩的流量也是設計秒殺系統很重要的思路。實作削峰的常用的方法有利用緩存和消息中間件等技術。
p.s. 削峰主要是把并行請求變為串行請求
異步處理:秒殺系統是一個高并發系統,采用異步處理模式可以極大地提高系統并發量,其實異步處理就是削峰的一種實作方式。
記憶體緩存:秒殺系統最大的瓶頸一般都是資料庫讀寫,由于資料庫讀寫屬于磁盤IO,性能很低,如果能夠把部分資料或業務邏輯轉移到記憶體緩存,效率會有極大地提升。
10.2.3 具體方案
- 前端
- 背景
背景技術選型
10.3 搶紅包
十一、團隊管理
目标(目标清晰)、計劃、職責(職責明确)、制度(賞罰分明)
1、木桶法則
注重團隊中的薄弱環節
一隻沿口不齊的木桶,它盛水的多少,不在于木桶上那塊最長的木闆,而在于木桶上最短的那塊木闆。要使木桶多盛水(提高水桶的整體效應),需要的不是去增加最長的那塊木闆長度,而是下工夫依次補齊木桶上最短的那些木闆,這就是管理上有名的“木桶”法則。企業管理也是如此,要提高企業的效益,就必須狠抓薄弱環節,否則機關的整體工作就會受到影響。人們常說“取長補短”,即取長的目的是為了補短,隻取長而不補短,就很難提高工作的整體效應。項目管理者聯盟
2、羊群效應
提升自己的判斷力,不盲目跟風
羊群效應是指人們經常會收到多數人的影響,而跟從大衆的思想或行為,也被稱為“從衆效應”。羊群是一種很散亂的組織,平時在一起也是盲目地左沖右撞,一旦有一頭羊動起來,其他的養也會不假思索地一哄而上,全然不顧前面可能有狼或者不遠處有更好的草。是以,就是比喻人都有一種從衆心理,從衆心理很容易導緻盲從,而盲從往往會使人陷入騙局或遭到失敗。
3、“熱爐”法則:
規章制度面前人人平等
“熱爐”法則不僅形象地闡述了規章制度的權威性,而且活靈活現地描述了懲處所需掌握的原則:(1)熱爐火紅,不用手去摸也知道爐子是熱的,是會灼傷人的,這就是懲處的警告性原則。上司者要經常對下屬進行規章制度教育,警告或勸戒不要觸犯規章制度,否則會受到懲處。(2)每當碰到熱爐,肯定會被火灼傷,這就是規章制度的權威性。也就是說隻要觸犯機關的規章制度,就一定會受到懲處。(3)當你碰到熱爐時,立即就被灼傷,這就是懲處的即時性原則。懲處必須在錯誤行為發生後立即進行,決不拖泥帶水,決不能有時間差,以達到及時改正錯誤行為的目的。(4)不管是誰碰到熱爐,都會被灼傷,這就是規章制度的公平性原則。
4、“金魚缸”法則:
增加管理的透明度
金魚缸是玻璃做的,透明度很高,不論從哪個角度觀察,裡面的情況都一清二楚,這就是管理上的“金魚缸”法則。“金魚缸”法則運用到管理中,就是要求上司者必須增加規章制度和各項工作的透明度。各項規章制度和工作有了透明度,上司者的行為就會置于員工的監督之下,就會有效地防止上司者濫用權力,進而強化上司者的自我限制機制。同時,員工在履行監督義務的同時,自身的主人翁意識和責任感得到極大的提升,而敬業、愛崗和創新的精神也必将得到升華。
5、“南風”法則
真誠溫暖員工
也稱“溫暖”法則,源于法國作家拉封丹寫過的一則寓言:北風和南風比威力,看誰能把行人身上的大衣脫掉。北風首先吹得人寒冷刺骨,結果行人為了抵禦北風的侵襲,便把大衣裹得緊緊的。南風則徐徐吹動,頓時風和日麗,行人覺得溫暖如春,随之開始解開紐扣,繼而脫掉大衣,最終南風獲得了勝利。這則寓言形象地說明一個道理:溫暖勝于嚴寒、柔性勝于剛性。上司者在管理中運用“南風”法則,就是要尊重和關心員工,以員工為本,多點“人情味”,少點官架子,盡力解決員工日常生活中的實際困難,使員工真正感覺到上司者給予的溫暖,進而激發他們工作的積極性。
6、“刺猬”法則:
保持适當的距離更有利于管理
“刺猬”法則講的是:兩隻困倦的刺猬,由于寒冷而擁在一起。可因為各自身上都長着刺,刺得對方怎麼也睡不舒服。于是它們離開了一段距離,但又冷得受不了,于是湊到一起。幾經折騰,兩隻刺猬終于找到了一個合适的距離,既能互相獲得對方的溫暖又不緻于被紮。“刺猬”法則就是管理和人際交往中的“心理距離效應”。心理學研究認為:上司者要搞好工作,就應該與員工保持親密關系,這樣做可以獲得他們的尊重。與員工保持一定的心理距離,不僅可以避免員工之間的嫉妒和緊張,而且可以減少他們的恭維、奉承、行賄等行為,防止與員工稱兄道弟、吃喝不分,并在工作中喪失原則。事實上,霧裡看花,水中望月,給人的是“距離美”的感覺,管理上也是如此。一個原本很受員工敬佩的上司者,往往由于與員工“親密無間”,就會使自己的缺點顯露無遺,結果在不知不覺中喪失了嚴肅性,不利于對其更進一步的管理。另外,“刺猬”法則還啟示我們,彼此間的親密協作是必不可少的,員工之間、管理者與員工之間、管理者之間,盡管每個人都有其特點和個性,但各自為戰在工作中卻是不可取的,“獨木難成林”、衆人劃槳開大船就是這個道理。線務局的工作千頭萬緒,各位局上司、中層幹部、管理人員,各區域局、各部室都要各司其職、各負其責、立足本崗、發揮作用,同時也要注意分工不分家、補台不包辦、到位不越位,切實形成合力、發揮團隊作用。
7、“青蛙原理”
時刻保持危機意識
關于“問題管理”有個著名的“青蛙原理”,說的是如果把一隻青蛙扔進沸水中,青蛙肯定會馬上跳出來。但是如果把一隻青蛙放入冷水中逐漸加溫,青蛙則會在不知不覺中喪失跳出去的能力,直至被熱水燙死。這個原理是用來形容企業中存在的兩種性質的問題,即顯性問題和隐性問題。人們對顯性問題的反應就如同青蛙對沸水的反應一樣,會馬上采取相應的措施,及時地将其扼殺在萌芽狀态;而隐性問題由于自身的隐匿性,不易被發現,往往是等到發現時,已經對企業釀成了嚴重的損失。這就啟示我們,很多線路障礙都是一些不起眼的小問題日積月累的結果,有客觀的,但是也有主觀的,跟我們的部分線務員在巡回或随工配合中的麻痹大意有關,聽任一些小問題長期自由發展,最終釀成了影響線路通暢的大禍。“冰凍三尺,非一日之寒”,是以我們要時刻關注潛在的問題,而不是等小問題變大了、危機降臨了再臨時抱佛腳。
8、鲶魚效應
競争是提高效率的法寶
“鲶魚效應”來自一個古老的傳說:一個小漁村的漁民靠到深海捕捉沙丁魚(一種比較懶的魚)為生。但由于捕魚點距離陸地比較遠,漁民捕的魚運回漁村時,往往死掉大半,很難賣出好價錢。隻有一個漁翁,他運回陸地的魚,都是活的,總能賣出好價錢,但是他從來不讓人看他的魚艙。直到他死後,好奇的村民才發現,原來他的魚艙裡總是放着一條鲶魚。由于鲶魚是以捕食沙丁魚為生,是以鲶魚在魚艙裡會不停地追逐沙丁魚,結果一些老弱的沙丁魚被吃掉,但其他的沙丁魚由于總在不停遊動,是以都活着到岸。而其他漁船所捕的沙丁魚靜止不動,結果一大半都會死掉。這個傳說告訴我們一個淺顯的道理:“生于憂患、死于安樂”,如果一個企業缺少活力與競争意識,沒有生存的壓力,就如同“沙丁魚”一樣,在“魚艙”裡混吃混喝,必然會被日益殘酷的市場競争所淘汰。一個員工也是如此,長期安于現狀、不思進取,必然會成為時代的棄兒。
上司者要成為“鲶魚”,有句俗話叫“兵熊熊一個,将熊熊一窩”。一家公司或一個部門,如果上司缺乏激情,要想手下的人有激情,那是白日做夢。最常見的情況是,上司工作不在狀态,員工必然上行下效,人浮于事,缺乏創新和主動性,日複一日,年複一年,必然成了一潭死水。反之則是“強将手下無弱兵”。如果上司者本身是一條充滿活力的“鲶魚”,那麼,通過整頓紀律,規範制度,改造流程,合理配置崗位和人、财、物,就能将那些無能的“沙丁魚”吃掉、趕走,使有能耐的“沙丁魚”得到正面的激勵,進而使整個機構呈現欣欣向榮的景象。是以,作為上司者,如果自己的公司沒有激情,首先不要怪員工,而是要去反思自己是不是有激情。隻有自己先成為“鲶魚”,才能影響員工,才能使整個組織的活力都被調動起來,進而使集體的力量更加強大,克敵制勝。
9、“走動式”管理
這種管理方式屬于最典型的柔性管理,目的很明确,就是要求企業的管理層要經常深入到基層和員工群衆中去,體察民意、了解實情,與員工打成一片,進而增強上司層的親和力和企業的凝聚力,激發員工的自豪感、自信心,起到上下一心、團結一緻、共同進步的理想效果。“走動式”管理啟示我們:一個整天忙忙碌碌、足不出戶的上司決不是好上司,而事無巨細、事必躬親的上司也不是好上司,隻有削掉“椅子背兒”,從辦公室中解放出來、深入基層與員工群衆中去,才能取得事半功倍的效果。
10、破窗效應
及時矯正和補救正在發生的問題
一個房子如果窗戶破了,沒有人去修補,隔不久,其它的窗戶也會莫名其妙的被人打破 ; 一面牆,如果出現一些塗鴉沒有清洗掉,很快的,牆上就布滿了亂七八糟,不堪入目的東西。一個很幹淨的地方,人會不好意思丢垃圾,但是一旦地上有垃圾出現之後,人就會毫不猶疑的抛,絲毫不覺羞愧 。這真是很奇怪的現象
11、馬雲的管理之道
“目标清晰(目标量化+計劃),職責明确,賞罰分明(制度),超越伯樂。”
十二、消息隊列
12.1 RabbitMQ
12.1.1 消息模式
- 點對點模式 Direct 綁定一個queue
- 扇形模式 Fanout 綁定多個queue
- 主題模式 Topic 綁定多個queue,同時增加topic 通配符 * #
12.1.2 有序性
單個queue有序
12.1.3 ACK
- basicAck 手動确認
- basicReject 重新放回隊列
12.1.4 事務
- TransactionMQProducer
12.2 RocketMQ
12.2.1 消息模式
- 叢集消費模式(預設模式) 同一group+topic 多個消費者,通過負載均衡政策消費
- 廣播模式 topic通配符
_RocketMq在topic基礎上可以增加tag進一步篩選
12.2.2 有序性
同一個Queue中有序,原理就是使用MessageQueueSelector(兩種實作随機與哈希)
- 生産者
rocketMQTemplate.syncSendOrderly
設定相同的hashKey使消息發送至同一個queue中,保證消息有序
- 消費者
consumeMode設定成ConsumeMode.ORDERLY
@Component
@RocketMQMessageListener(topic = "topic-lcf1", selectorExpression = "tag1", consumerGroup = "luchunfeng1",consumeMode = ConsumeMode.ORDERLY)
public class Consumer implements RocketMQListener<String> {
private static final Logger logger = LoggerFactory.getLogger(Consumer.class);
@Override
public void onMessage(String s) {
logger.info(s);
}
}
12.2.3 ACK
//CONSUME_SUCCESS 消費成功
//RECONSUME_LATER 消費失敗,需要稍後重新消費
ConsumeConcurrentlyStatus.RECONSUME_LATER
12.2.4 事務
- RabbitTransactionManager
12.2.5 ACL
ACL是access control list的簡稱,俗稱通路控制清單。通路控制,基本上會涉及到使用者、資源、權限、角色等
12.3 Kafka
12.3.1 消息模式
- 點對點模式
- 釋出/訂閱模式
12.3.2 有序性
單個partition有序,生産者發送消息指定partition
12.3.3 ACK
生産者到kafka伺服器
- ack=1,producer隻要收到一個分區副本成功寫入的通知就認為推送消息成功了。
- ack=0,producer發送一次就不再發送了,不管是否發送成功。
- ack=-1,producer隻有收到分區内所有副本的成功寫入的通知才認為推送消息成功了。
消費者手動送出
consumer.commitSync()
12.3.4 事務
- 使用者隻需要在 Producer 的配置中配置 transactional.id,通過 initTransactions() 初始化事務狀态資訊,再通過 beginTransaction() 辨別一個事務的開始,然後通過 commitTransaction() 或 abortTransaction() 對事務進行 commit 或 abort
十三、技術棧
微服務:SpringCloud/Dubbo、Gateway、Eureka/Nacos/Consul、 ConfigServer/Apollo、Hystrix/Sentinel
分布式:SLB、LVS、KeepAlived、LCN/Seata、ELK/ES等; 緩存:Java本地緩存、Memcached、Redis、MongoDB;
消息:RabbitMQ、RocketMQ、Kafka等
存儲:Minio/Fastdfs、 MySQL(MybatisPlus)、Oracle、DB2、SQL Server等
測試:Jmeter、Loadrunner、JUnit、MockServer、Mockjs
監控:Arthas、XRebel、Jconsole、Jvisualvm、MAT
自動化:Jenkins、Nexus、GIT、Sonaqube、Maven、Gradle;
項目管理:TAPD(靈活)、Redmine、禅道、Jira、Worktile(OKR)等;
設計:常用設計模式、UML、PowerDesinger
前端:JavaScript、Jquery、Ajax、Nodejs、Vue 等;
背景:SpringSecurityOauth2/Shiro、okHttp、Swagger/Knife4j 、對稱/非對稱加密(SM2)等;
大資料:熟悉 Spark、Hive、Hadoop(hdfs、hbase);
容器:Nginx/Tomcat/Jetty、Docker/Podman、WebLogic、Websphere等;
其它:Python、Activiti 、以太坊
十四、其它
可用性(SLA)、可靠性()
- 可用性
可用性指系統在給定時間内可以正常工作的機率,通常用SLA(服務等級協定,service level agreement)名額來表示。
這是這段時間的總體的可用性名額。
- 可靠性
可靠性相關的幾個名額如下:
MTBF(Mean Time Between Failure)
即平均無故障時間,是指從新的産品在規定的工作環境條件下開始工作到出現第一個故障的時間的平均值。
MTBF越長表示可靠性越高,正确工作能力越強 。
MTTR(Mean Time To Repair)
即平均修複時間,是指可修複産品的平均修複時間,就是從出現故障到修複中間的這段時間。
MTTR越短表示易恢複性越好。
MTTF(Mean Time To Failure)
即平均失效時間。系統平均能夠正常運作多長時間,才發生一次故障。
系統的可靠性越高,平均無故障時間越長。