大數據技術之_31_Java 面試題_01_JavaSE 面試題 + SSM 面試題 + Java 高級面試題 + Java 項目面試題

第1章 JavaSE 面試題1、自增變量2、編程題:寫一個 Singleton 示例3、類初始化和實例初始化4、方法的參數傳遞機制5、遞歸與迭代6、成員變量與局部變量第2章 SSM 面試題1、Spring Bean 的作用域之間有什麼區別?2、Spring 支持的常用數據庫事務傳播屬性和事務隔離級別3、SpringMVC 中如何解決 POST 請求中文亂碼問題4、SpringMVC 中如何解決 GET 請求中文亂碼問題5、簡單的談一下 SpringMVC 的工作流程6、MyBatis 中當實體類中的屬性名和數據庫表中的字段名不一樣怎麼辦?第3章 Java 高級面試題1、Linux 常用服務類相關命令2、git 分支相關命令和實際應用3、redis 持久化4、Mysql 什麼時候建索引5、JVM 垃圾回收機制第5章 Java 項目面試題1、redis 在項目中的使用場景2、Elasticsearch 與 solr 的區別3、單點登錄實現過程4、購物車實現過程5、消息隊列在項目中的使用


第1章 JavaSE 面試題

1、自增變量

    public static void main(String[] args) {
        int i = 1;
        i = i++; // i=1
        int j = i++; // j=1  i=2
        int k = i + ++i * i++; // 2 + 3 * 3 = 11
        System.out.println("i=" + i); // i=4
        System.out.println("j=" + j); // j=1
        System.out.println("k=" + k); // 11
    }

(1)


(2)

(3)

小總結

2、編程題:寫一個 Singleton 示例

  • Singleton:在 Java 中即指單例設計模式,它是軟件開發中最常用的設計模式之一。
  • 單:唯一
  • 例:實例
  • 單例設計模式,即某個類在整個系統中只能有一個實例對象可被獲取和使用的代碼模式。
  • 例如:代表 JVM 運行環境的 Runtime 類。

要點:
1、某個類只能有一個實例;
  構造器私有化
2、它必須自行創建這個實例;
  含有一個該類的靜態變量來保存這個唯一的實例
3、它必須自行向整個系統提供這個實例;
  對外提供獲取該實例對象的方式:
  (1)直接暴露
  (2)用靜態變量的get方法獲取

幾種常見形式:
餓漢式:在類初始化時直接創建對象,不存在線程安全問題
  直接實例化餓漢式(簡潔直觀)
  枚舉式(最簡潔)
  靜態代碼塊餓漢式(適合複雜實例化)
示例代碼如下:

package com.atguigu.singleton;

/*
 * 餓漢式:
 *     在類初始化時直接創建實例對象,不管你是否需要這個對象都會創建
 * 
 * (1)構造器私有化
 * (2)自行創建,並且用靜態變量保存
 * (3)向外提供這個實例
 * (4)強調這是一個單例,我們可以用 final 修飾
 */

public class Singleton1 {
    public static final Singleton1 INSTANCE = new Singleton1();

    private Singleton1() {
    }
}

示例代碼如下:

package com.atguigu.singleton;

/*
 * 枚舉類型:表示該類型的對象是有限的幾個
 * 我們可以限定爲一個,就成了單例
 */

public enum Singleton2 {
    INSTANCE
}

示例代碼如下:

package com.atguigu.singleton;

import java.io.IOException;
import java.util.Properties;

public class Singleton3 {
    public static final Singleton3 INSTANCE;

    private String info;

    static {
        try {
            Properties properties = new Properties();
            properties.load(Singleton3.class.getClassLoader().getResourceAsStream("single.properties"));

            INSTANCE = new Singleton3(properties.getProperty("info"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private Singleton3(String info) {
        this.info = info;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "Singleton3 [info=" + info + "]";
    }
}

懶漢式:延遲創建對象
  線程不安全(適用於單線程)
  線程安全(適用於多線程)
  靜態內部類形式(適用於多線程)
示例代碼如下:

package com.atguigu.singleton;

/*
 * 懶漢式:
 *     延遲創建這個實例對象
 *  線程不安全(適用於單線程)
 * 
 * (1)構造器私有化
 * (2)用一個靜態變量保存這個唯一的實例
 * (3)提供一個靜態方法,獲取這個實例對象
 */

public class Singleton4 {
    private static Singleton4 instance;

    private Singleton4() {
    }

    public static Singleton4 getInstance() {
        if (instance == null) {
            instance = new Singleton4();
        }
        return instance;
    }
}

示例代碼如下:

package com.atguigu.singleton;

/*
 * 懶漢式:
 *     延遲創建這個實例對象
 *  線程安全(適用於多線程)
 * 
 * (1)構造器私有化
 * (2)用一個靜態變量保存這個唯一的實例
 * (3)提供一個靜態方法,獲取這個實例對象
 */

public class Singleton5 {
    private static Singleton5 instance;

    private Singleton5() {
    }

    public static Singleton5 getInstance() {
        if (instance == null) { // 優化性能
            synchronized (Singleton5.class) { // 線程安全
                if (instance == null) {
                    instance = new Singleton5();
                }
            }
        }
        return instance;
    }
}

示例代碼如下:

package com.atguigu.singleton;

/* 懶漢式:
 *     延遲創建這個實例對象
 *     靜態內部類形式(適用於多線程)
 * 
 *     在內部類被加載和初始化時,才創建 INSTANCE 實例對象
 *     靜態內部類不會自動隨着外部類的加載和初始化而初始化,它是要單獨去加載和初始化的。(是最懶的)
 *     因爲是在內部類加載和初始化時,創建的,因此是線程安全的
 */

public class Singleton6 {
    private Singleton6() {
    }

    private static class Inner {
        private static final Singleton6 INSTANCE = new Singleton6();
    }

    public static Singleton6 getInstance() {
        return Inner.INSTANCE;
    }
}

3、類初始化和實例初始化

考點
  類初始化過程
  實例初始化過程
  方法的重寫

類初始化過程

一個類要創建實例需要先加載並初始化該類
    main 方法所在的類需要先加載和初始化

一個子類要初始化需要先初始化父類

一個類初始化就是執行 <clinit>() 方法
    <clinit>() 方法由靜態類變量顯示賦值代碼和靜態代碼塊組成
    類變量顯示賦值代碼 和 靜態代碼塊代碼 從上到下順序執行
    <clinit>() 方法只執行一次

實例初始化過程

實例初始化就是執行 <init>() 方法
    <init>() 方法可能重載有多個,有幾個構造器就有幾個 <init> 方法
    <init>() 方法由非靜態實例變量顯示賦值代碼和非靜態代碼塊、對應構造器代碼組成

    非靜態實例變量顯示賦值代碼和非靜態代碼塊代碼從上到下順序執行,而對應構造器的代碼最後執行

    每次創建實例對象,調用對應構造器,執行的就是對應的 <init> 方法

    <init> 方法的首行是 super() 或 super(實參列表),即對應父類的 <init> 方法

方法的重寫

哪些方法不可以被重寫
    final 方法
    靜態方法
    private 等子類中不可見方法

對象的多態性
    子類如果重寫了父類的方法,通過子類對象調用的一定是子類重寫過的代碼
    非靜態方法默認的調用對象是 this
    this 對象在構造器或者說 <init> 方法中就是正在創建的對象

Override 和 Overload 的區別?

Override 重寫的要求?
    方法名
    形參列表
    返回值類型
    拋出的異常列表
    修飾符

瞭解《JVM 虛擬機規範》中關於 <clinit> 和 <init> 方法的說明、invokespecial 指令

示例代碼如下:

package com.atguigu.init;

/*
 * 父類的初始化 <clinit>:
 * (1)父類的靜態方法 j = method();
 * (2)父類的靜態代碼塊
 * 
 * 
 * 
 * -----------------------------------------
 *  父類的實例化 <init>:
 * (1)super()(最前)
 * (2)i = test();
 * (3)父類的非靜態代碼塊
 * (4)父類的無參構造(最後)
 * 
 * 非靜態方法前面其實有一個默認的對象 this
 * this 在構造器(或 <init>)它表示的是正在創建的對象,
 * 因爲這裏是在創建 Son 對象,所以 test() 執行的是子類重寫的代碼(面向對象+多態)
 * 
 * 這裏 i=test() 執行的是子類重寫的 test() 方法
 */

public class Father {
    private int i = test();
    private static int j = method();

    static {
        System.out.print("(1)");
    }

    Father() {
        System.out.print("(2)");
    }

    {
        System.out.print("(3)");
    }

    public int test() {
        System.out.print("(4)");
        return 1;
    }

    public static int method() {
        System.out.print("(5)");
        return 1;
    }
}

示例代碼如下:

package com.atguigu.init;

/*
 * 子類的初始化 <clinit>:
 * (1)子類的靜態方法 j = method();
 * (2)子類的靜態代碼塊
 * 
 * 先初始化父類的靜態方法和靜態代碼塊:(5)(1)
 * 再初始化子類的靜態方法和靜態代碼塊:(10)(6)
 * -----------------------------------------
 * 子類的實例化  <init>:
 * (1)super()(最前)            (9)(3)(2)
 * (2)i = test();                (9)
 * (3)子類的非靜態代碼塊        (8)
 * (4)子類的無參構造(最後)    (7)
 * 
 * 因爲創建了兩個 Son 對象,因此實例化方法 <init> 執行了兩次
 * 即又出現了一次 (9)(3)(2)(9)(8)(7)
 */

public class Son extends Father {
    private int i = test();
    private static int j = method();

    static {
        System.out.print("(6)");
    }

    Son() {
        // super(); // 寫或不寫都在,在子類構造器中一定會調用父類的構造器
        System.out.print("(7)");
    }

    {
        System.out.print("(8)");
    }

    public int test() {
        System.out.print("(9)");
        return 1;
    }

    public static int method() {
        System.out.print("(10)");
        return 1;
    }

    public static void main(String[] args) {
        Son s1 = new Son();
        System.out.println();
        Son s2 = new Son();
    }
}

4、方法的參數傳遞機制

考點
  方法的參數傳遞機制
  String、包裝類等對象的不可變性

方法的參數傳遞機制

形參是基本數據類型時
    傳遞數據值

形參是引用數據類型時
    傳遞地址值,本質也是值
    特殊的類型:String、包裝類等對象不可變性

示例代碼如下:

package com.atguigu.passvalue;

import java.util.Arrays;

public class Exam4 {
    public static void main(String[] args) {
        int i = 1;
        String str = "hello"// 在常量池中(jdk1.8 中,常量池在元空間中;jdk1.7 中,常量池在堆中)
        Integer num = 200// 值大於 127,對象在堆中
        int[] arr = { 12345 }; // 堆中
        MyData my = new MyData(); // 堆中

        change(i, str, num, arr, my);

        System.out.println("i = " + i); // i = 1
        System.out.println("str = " + str); // str = hello
        System.out.println("num = " + num); // num = 200
        System.out.println("arr = " + Arrays.toString(arr)); // arr = [2, 2, 3, 4, 5]
        System.out.println("my.a = " + my.a); // my.a = 11
    }

    public static void change(int j, String s, Integer n, int[] a, MyData m) // 方法在棧中,執行完畢後銷燬
        j += 1// j = 1
        s += "world"// 產生新的對象
        n += 1// 產生新的對象
        a[0] += 1// a[0] = 2
        m.a += 1// 11
    }
}

class MyData {
    int a = 10;
}

5、遞歸與迭代

編程題:有 n 步臺階,一次只能上 1 步或 2 步,共有多少種走法?

遞歸(理解上直觀,效率上可能會差些(針對某些情況下))


示例代碼如下:
package com.atguigu.step;

import org.junit.Test;

public class TestStep {
    @Test
    public void test() {
        long start = System.currentTimeMillis();
        System.out.println(f(40)); // 165580141
        long end = System.currentTimeMillis();
        System.out.println(end - start); // 308ms
    }

    // 實現 f(n):求 n 步臺階,一共有幾種走法
    public int f(int n) {
        if (n < 1) {
            throw new IllegalArgumentException(n + "不能小於1");
        }
        if (n == 1 || n == 2) {
            return n;
        }
        return f(n - 2) + f(n - 1);
    }
}

循環迭代


示例代碼如下:
package com.atguigu.step;

import org.junit.Test;

public class TestStep2 {
    @Test
    public void test() {
        long start = System.currentTimeMillis();
        System.out.println(loop(40)); // 165580141
        long end = System.currentTimeMillis();
        System.out.println(end - start); // <1ms
    }

    public int loop(int n) {
        if (n < 1) {
            throw new IllegalArgumentException(n + "不能小於1");
        }
        if (n == 1 || n == 2) {
            return n;
        }

        int one = 2// 初始化 走到第二級臺階的走法
        int two = 1// 初始化 走到第一級臺階的走法
        int sum = 0;

        for (int i = 3; i <= n; i++) {
            // 最後跨2步 + 最後跨1步的走法
            sum = two + one;
            two = one;
            one = sum;
        }
        return sum;
    }
}

小總結

6、成員變量與局部變量

考點
  就近原則
  變量的分類
    成員變量:類變量、實例變量
    局部變量
  非靜態代碼塊的執行:每次創建實例對象都會執行
  方法的調用規則:調用一次執行一次

局部變量與成員變量的區別:

聲明的位置
    局部變量:方法體 {} 中,形參,代碼塊 {} 中
    成員變量:類中方法外
        類變量:有 static 修飾
        實例變量:沒有 static 修飾

修飾符
    局部變量:final
    成員變量:publicprotectedprivatefinalstaticvolatiletransient

值存儲的位置
    局部變量:棧
    實例變量:堆
    類變量:方法區

作用域
    局部變量:從聲明處開始,到所屬的 } 結束
    實例變量:在當前類中 “this.” (有時 this. 可以缺省),在其他類中 “對象名.” 訪問
    類變量:在當前類中 “類名.”(有時類名.可以省略),在其他類中 “類名.” 或 “對象名.” 訪問

生命週期
    局部變量:每一個線程,每一次調用執行都是新的生命週期
    實例變量:隨着對象的創建而初始化,隨着對象的被回收而消亡,每一個對象的實例變量是獨立的
    類變量:隨着類的初始化而初始化,隨着類的卸載而消亡,該類的所有對象的類變量是共享的

當局部變量與 xx 變量重名時,如何區分:
    局部變量與實例變量重名
        在實例變量前面加 “this.”
    局部變量與類變量重名
        在類變量前面加 “類名.”

Java 運行時數據區

示例代碼如下:

package com.atguigu.variable;

public class Exam5 {
    static int s; // 成員變量,類變量
    int i; // 成員變量,實例變量
    int j; // 成員變量,實例變量

    {
        int i = 1// 非靜態代碼塊中的局部變量 i
        i++; // i = 2
        j++; // j = 1
        s++; // 
    }

    public void test(int j) // 形參,局部變量 j
        j++; // 
        i++; // i = 1
        s++; // 
    }

    public static void main(String[] args) // 形參,局部變量 args
        Exam5 obj1 = new Exam5(); // 局部變量 obj1
        Exam5 obj2 = new Exam5(); // 局部變量 obj2
        obj1.test(10);
        obj1.test(20);
        obj2.test(30);

        System.out.println(obj1.i + "," + obj1.j + "," + obj1.s); // 2,1,5
        System.out.println(obj2.i + "," + obj2.j + "," + obj2.s); // 1,1,5
    }
}

代碼內存圖解如下:

第2章 SSM 面試題

1、Spring Bean 的作用域之間有什麼區別?

  在 Spring 中,可以在 <bean> 元素的 scope 屬性裏設置bean的作用域,以決定這個 bean 是單實例的還是多實例的。

  默認情況下,Spring 只爲每個在 IOC 容器裏聲明的 bean 創建唯一一個實例(單實例),整個 IOC 容器範圍內都能共享該實例:所有後續的 getBean() 調用和 bean 引用都將返回這個唯一的 bean 實例。該作用域被稱爲 singleton,它是所有 bean 的默認作用域。

  

  當 bean 的作用域爲單例時,Spring 會在 IOC 容器對象創建時就創建 bean 的對象實例。而當 bean 的作用域爲 prototype 時,IOC 容器在獲取 bean 的實例時創建 bean 的實例對象。

2、Spring 支持的常用數據庫事務傳播屬性和事務隔離級別

  當事務方法被另一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼續在現有事務中運行,也可能開啓一個新事務,並在自己的事務中運行。

  

  事務的傳播行爲可以由傳播屬性指定。Spring 定義了 7 種類傳播行爲。

事務的屬性:
    1.★ propagation:用來設置事務的傳播行爲
    事務的傳播行爲:一個方法運行在了一個開啓了事務的方法中時,當前方法是使用原來的事務還是開啓一個新的事務
        -Propagation.REQUIRED:默認值,使用原來的事務
        -Propagation.REQUIRES_NEW:將原來的事務掛起,開啓一個新的事務
    2.★ isolation:用來設置事務的隔離級別
        -Isolation.REPEATABLE_READ:可重複讀,MySQL默認的隔離級別
        -Isolation.READ_COMMITTED:讀已提交,Oracle默認的隔離級別,開發時通常使用的隔離級別

數據庫事務併發問題
假設現在有兩個事務:Transaction01 和 Transaction02 併發執行。
1) 髒讀(讀取到了未提交的數據)
  ① Transaction01 將某條記錄的 AGE 值從 20 修改爲 30。
  ② Transaction02 讀取了 Transaction01 更新後的值:30。
  ③ Transaction01 回滾,AGE 值恢復到了20。
  ④ Transaction02 讀取到的 30 就是一個無效的值。
2) 不可重複讀(讀取到了已提交的數據)
  ① Transaction01 讀取了 AGE 值爲 20。
  ② Transaction02 將 AGE 值修改爲 30。
  ③ Transaction01 再次讀取 AGE 值爲 30,和第一次讀取不一致。
3) 幻讀
  ① Transaction01 讀取了 STUDENT 表中的一部分數據。
  ② Transaction02 向 STUDENT 表中插入了新的行。
  ③ Transaction01 讀取了 STUDENT 表時,多出了一些行。

事務的隔離級別
  數據庫系統必須具有隔離併發運行各個事務的能力,使它們不會相互影響,避免各種併發問題。一個事務與其他事務隔離的程度稱爲隔離級別。SQL 標準中規定了多種事務隔離級別,不同隔離級別對應不同的干擾程度,隔離級別越高,數據一致性就越好,但併發性越弱。
1) 讀未提交:READ UNCOMMITTED
  允許 Transaction01 讀取 Transaction02 未提交的修改。
2) 讀已提交:READ COMMITTED
  要求 Transaction01 只能讀取 Transaction02 已提交的修改。
3) 可重複讀:REPEATABLE READ
  確保 Transaction01 可以多次從一個字段中讀取到相同的值,即 Transaction01 執行期間禁止其它事務對這個字段進行更新。
4) 串行化:SERIALIZABLE
  確保 Transaction01 可以多次從一個表中讀取到相同的行,在 Transaction01 執行期間,禁止其它事務對這個表進行添加、更新、刪除操作。可以避免任何併發問題,但性能十分低下。
5) 各個隔離級別解決併發問題的能力見下表


6) 各種數據庫產品對事務隔離級別的支持程度

3、SpringMVC 中如何解決 POST 請求中文亂碼問題

在 web.xml 中配置一個過濾器

  <filter>
    <!-- SpringMVC 中如何解決 POST 請求中文亂碼問題 -->
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <!-- POST 請求中文亂碼問題 -->
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
      <!-- POST 響應中文亂碼問題 -->
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

4、SpringMVC 中如何解決 GET 請求中文亂碼問題

方法一:稍微修改 tomcat 的配置文件 server.xml,修改第 65 行代碼

方法二:參考百度

5、簡單的談一下 SpringMVC 的工作流程

6、MyBatis 中當實體類中的屬性名和數據庫表中的字段名不一樣怎麼辦?

第一種解決方案:寫 sql 語句時起別名。別名和數據庫表中的字段名一樣即可。

第二種解決方案:在 MyBatis 的全局配置文件中開啓駝峯命名規則。可以將數據庫中下劃線映射爲駝峯命名。(有侷限性)

第三種解決方案:在 Mapper 的映射文件中使用 resultMap 來自定義映射規則。

第3章 Java 高級面試題

1、Linux 常用服務類相關命令

CentOS 6

運行級別

CentOS 7

2、git 分支相關命令和實際應用

Git 工作流

3、redis 持久化

Redis 提供了 2 種不同形式的持久化方式:

RDB(Redis Database):全量替換

AOF(Append Of File):增量追加

4、Mysql 什麼時候建索引

5、JVM 垃圾回收機制

GC 發生在 JVM 哪一個部分,有幾種 GC,它們的算法是什麼
答:堆中。2 種:Minor GC(新生代)、 Full GC(老年代)。引用計數算法、複製算法、標記清除算法、標記壓縮算法、標記清除壓縮算法。

引用計數算法(已經淘汰了,因爲沒有辦法處理循環引用)

複製算法(新生代中使用的是 Minor GC)

標記清除算法(老年代一般是由標記清除或者標記清除與標記整理的混合實現)

標記壓縮算法(老年代一般是由標記清除或者標記清除與標記整理的混合實現)

標記清除壓縮算法

第5章 Java 項目面試題

1、redis 在項目中的使用場景

2、Elasticsearch 與 solr 的區別

背景:它們都是基於 Lucene 搜索服務器基礎之上開發,一款優秀的,高性能的企業級搜索服務器。【因爲它們都是基於分詞技術構建的倒排索引的方式進行查詢
開發語言:java 語言開發
誕生時間
  Solr:2004年誕生。
  Elasticsearch:2010年誕生。更新、功能更強大。

區別
  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 本身更注重於核心功能(即檢索),高級功能多有第三方插件。

Solr 利用 zookeeper 進行分佈式管理,部署在 tomcat 上

SolrCloud:集羣圖

Elasticsearch:集羣圖

3、單點登錄實現過程

單點登錄:一處登錄多處使用!
前提:單點登錄多使用在分佈式系統中。

Demo:
參觀動物園流程:
檢票員=認證中心模塊
1、我直接帶着大家進動物園,則會被檢票員攔住【看我們是否有門票】,沒有[售票處買票]
  登錄=買票
2、我去買票【帶着票,帶着大家一起準備進入動物園】檢票員 check【有票】
  Token=piao
3.我們手中有票就可以任意觀賞動物的每處景點。
  京東:單點登錄,是將 token 放入到 cookie 中的。
  案例:如果將瀏覽器的 cookie 禁用,則在登錄京東後就失敗!即無論如何登錄不了!

4、購物車實現過程

購物車:
1. 購物車跟用戶的關係?
    a) 一個用戶必須對應一個購物車【一個用戶不管買多少商品,都會存在屬於自己的購物車中。】
    b) 單點登錄的開發一定在開發購物車之前。
2. 跟購物車有關的操作有哪些?
    a) 添加購物車
        i.用戶未登錄狀態
            1. 添加到什麼地方?未登錄將數據保存到什麼地方?
                a) Redis ? --> 京東使用
                b) Cookie ? --> 自己開發項目的時候【如果瀏覽器禁用 cookie,則京東登錄不上】
        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) 合併後,真正展示的時候應該是小於或等於八條記錄

5、消息隊列在項目中的使用

背景:在分佈式系統中是如何處理高併發的。
  由於在高併發的環境下,來不及同步處理用戶發送的請求,則會導致請求發生阻塞。比如說,大量的 insert,update 之類的請求同時到達數據庫 MySQL,直接導致無數的行鎖表鎖,甚至會導致請求堆積很多。從而觸發 too many connections 錯誤。使用消息隊列可以解決【異步通信】

1、異步

2、並行

3、排隊

消息隊列電商使用場景:

消息隊列的弊端:
  解決消息的不確定性:延遲隊列 和 輪詢技術來解決,比如:訂單模塊 定時向 支付寶 查詢當前訂單是否支付成功。
  推薦大家使用 activemq!因爲環境都是 java。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章