Java面試題二

11、如何正確終止一個線程?

/**
 * 如何正確的更好的終止一個正在執行的線程.
 * 需求:線程啓動5S之後終止。
 */
public class ThreadTest09 {
    public static void main(String[] args) throws Exception {
        Processor p = new Processor();
        Thread t = new Thread(p);
        t.setName("t");
        t.start();
        //5S之後終止.
        Thread.sleep(5000);
        //終止
        p.run = false;
    }
}
class Processor implements Runnable {
    boolean run = true;
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (run) {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                }
                System.out.println(Thread.currentThread().getName() + "-->" + i);
            } else {
                return;
            }
        }
    }
}

另一種方式:

/**
    某線程正在休眠,如果打斷它的休眠.
   以下方式依靠的是異常處理機制。
*/
public class ThreadTest08 {
    public static void main(String[] args) throws Exception {
        //需求:啓動線程,5S之後打斷線程的休眠.
        Thread t = new Thread(new Processor());
        //起名
        t.setName("t");
        //啓動
        t.start();
        //5S之後
        Thread.sleep(5000);
        //打斷t的休眠.
        t.interrupt();
    }
}
class Processor implements Runnable {
    public void run() {
        try {
            Thread.sleep(100000000000L);
            System.out.println("HelloWorld!");
        } catch (InterruptedException e) {
            //e.printStackTrace();
        }
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
}

12、MyBatis中使用#和$書寫佔位符有什麼區別?

#將傳入的數據都當成一個字符串,會對傳入的數據自動加上引號;$將傳入的數據直接顯示生成在SQL中。注意:使用$佔位符可能會導致SQL注射攻擊,能用#的地方就不要使用$,寫order by子句的時候應該用$而不是#。

13、解釋一下MyBatis中命名空間(namespace)的作用。

在大型項目中,可能存在大量的SQL語句,這時候爲每個SQL語句起一個唯一的標識(ID)就變得並不容易了。爲了解決這個問題,在MyBatis中,可以爲每個映射文件起一個唯一的命名空間,這樣定義在這個映射文件中的每個SQL語句就成了定義在這個命名空間中的一個ID。只要我們能夠保證每個命名空間中這個ID是唯一的,即使在不同映射文件中的語句ID相同,也不會再產生衝突了。

14、闡述Session加載實體對象的過程。

Session加載實體對象的步驟是: 
① Session在調用數據庫查詢功能之前,首先會在一級緩存中通過實體類型和主鍵進行查找,如果一級緩存查找命中且數據狀態合法,則直接返回; 
② 如果一級緩存沒有命中,接下來Session會在當前NonExists記錄(相當於一個查詢黑名單,如果出現重複的無效查詢可以迅速做出判斷,從而提升性能)中進行查找,如果NonExists中存在同樣的查詢條件,則返回null; 
③ 如果一級緩存查詢失敗則查詢二級緩存,如果二級緩存命中則直接返回; 
④ 如果之前的查詢都未命中,則發出SQL語句,如果查詢未發現對應記錄則將此次查詢添加到Session的NonExists中加以記錄,並返回null; 
⑤ 根據映射配置和SQL語句得到ResultSet,並創建對應的實體對象; 
⑥ 將對象納入Session(一級緩存)的管理; 
⑦ 如果有對應的攔截器,則執行攔截器的onLoad方法; 
⑧ 如果開啓並設置了要使用二級緩存,則將數據對象納入二級緩存; 
⑨ 返回數據對象。

 

 

 

15、寫一個java單例

- 餓漢式單例

publicclass Singleton {
    private Singleton(){}
    private static Singletoninstance = new Singleton();
    public static SingletongetInstance(){
           return instance;

    }

}

·        懶漢式單例

publicclass Singleton {
    private static Singletoninstance = null;
    private Singleton() {}
    public static synchronizedSingleton getInstance(){
        if (instance== null)
         instance = new Singleton();
      return instance;

    }

}

16、異常處理

Java異常類層次結構圖:

圖1 Java異常類層次結構圖

在 Java 中,所有的異常都有一個共同的祖先 Throwable(可拋出)。Throwable 指定代碼中可用異常傳播機制通過 Java 應用程序傳輸的任何問題的共性。

Throwable: 有兩個重要的子類:Exception(異常)和 Error(錯誤),二者都是 Java 異常處理的重要子類,各自都包含大量子類。

Error(錯誤):是程序無法處理的錯誤,表示運行應用程序中較嚴重問題。大多數錯誤與代碼編寫者執行的操作無關,而表示代碼運行時 JVM(Java 虛擬機)出現的問題。例如,Java虛擬機運行錯誤(Virtual MachineError),當 JVM 不再有繼續執行操作所需的內存資源時,將出現 OutOfMemoryError。這些異常發生時,Java虛擬機(JVM)一般會選擇線程終止。

這些錯誤表示故障發生於虛擬機自身、或者發生在虛擬機試圖執行應用時,如Java虛擬機運行錯誤(Virtual MachineError)、類定義錯誤(NoClassDefFoundError)等。這些錯誤是不可查的,因爲它們在應用程序的控制和處理能力之 外,而且絕大多數是程序運行時不允許出現的狀況。對於設計合理的應用程序來說,即使確實發生了錯誤,本質上也不應該試圖去處理它所引起的異常狀況。在 Java中,錯誤通過Error的子類描述。

Exception(異常):是程序本身可以處理的異常。

Exception 類有一個重要的子類RuntimeException。RuntimeException 類及其子類表示“JVM 常用操作”引發的錯誤。例如,若試圖使用空值對象引用、除數爲零或數組越界,則分別引發運行時異常(NullPointerException、ArithmeticException)和 ArrayIndexOutOfBoundException。

注意:異常和錯誤的區別:異常能被程序本身可以處理,錯誤是無法處理。

通常,Java的異常(包括Exception和Error)分爲可查的異常(checked exceptions)和不可查的異常(unchecked exceptions)。

可查異常(編譯器要求必須處置的異常):正確的程序在運行中,很容易出現的、情理可容的異常狀況。可查異常雖然是異常狀況,但在一定程度上它的發生是可以預計的,而且一旦發生這種異常狀況,就必須採取某種方式進行處理。

除了RuntimeException及其子類以外,其他的Exception類及其子類都屬於可查異常。這種異常的特點是Java編譯器會檢查它,也就是說,當程序中可能出現這類異常,要麼用try-catch語句捕獲它,要麼用throws子句聲明拋出它,否則編譯不會通過。

不可查異常(編譯器不要求強制處置的異常):包括運行時異常(RuntimeException與其子類)和錯誤(Error)。

Exception 這種異常分兩大類運行時異常和非運行時異常(編譯異常)。程序中應當儘可能去處理這些異常。

運行時異常:都是RuntimeException類及其子類異常,如NullPointerException(空指針異常)、IndexOutOfBoundsException(下標越界異常)等,這些異常是不檢查異常,程序中可以選擇捕獲處理,也可以不處理。這些異常一般是由程序邏輯錯誤引起的,程序應該從邏輯角度儘可能避免這類異常的發生。

運行時異常的特點是Java編譯器不會檢查它,也就是說,當程序中可能出現這類異常,即使沒有用try-catch語句捕獲它,也沒有用throws子句聲明拋出它,也會編譯通過。

非運行時異常 (編譯異常):是RuntimeException以外的異常,類型上都屬於Exception類及其子類。從程序語法角度講是必須進行處理的異常,如果不處理,程序就不能編譯通過。如IOException、SQLException等以及用戶自定義的Exception異常,一般情況下不自定義檢查異常。

 

在 Java 應用程序中,異常處理機制爲:拋出異常,捕捉異常。

拋出異常當一個方法出現錯誤引發異常時,方法創建異常對象並交付運行時系統,異常對象中包含了異常類型和異常出現時的程序狀態等異常信息。運行時系統負責尋找處置異常的代碼並執行。

捕獲異常在方法拋出異常之後,運行時系統將轉爲尋找合適的異常處理器(exceptionhandler)。潛在的異常處理器是異常發生時依次存留在調用棧中的方法的集合。當異常處理器所能處理的異常類型與方法拋出的異常類型相符時,即爲合適的異常處理器。運行時系統從發生異常的方法開始,依次回查調用棧中的方法,直至找到含有合適異常處理器的方法並執行。當運行時系統遍歷調用棧而未找到合適 的異常處理器,則運行時系統終止。同時,意味着Java程序的終止。

1、捕獲異常:try、catch 和 finally

try塊:用於捕獲異常。其後可接零個或多個catch塊,如果沒有catch塊,則必須跟一個finally塊。
catch 塊:用於處理try捕獲到的異常。
finally 塊:無論是否捕獲或處理異常,finally塊裏的語句都會被執行。當在try塊或catch塊中遇到return語句時,finally語句塊將在方法返回之前被執行。在以下4種特殊情況下,finally塊不會被執行:
1)在finally語句塊中發生了異常。
2)在前面的代碼中用了System.exit()退出程序。
3)程序所在的線程死亡。
4)關閉CPU。

 

2、try-catch-finally 規則(異常處理語句的語法規則):

1)  必須在 try 之後添加 catch 或 finally 塊。try塊後可同時接 catch 和 finally 塊,但至少有一個塊。
2) 必須遵循塊順序:若代碼同時使用 catch 和finally 塊,則必須將 catch 塊放在 try 塊之後。
3) catch 塊與相應的異常類的類型相關。
4) 一個 try 塊可能有多個 catch 塊。若如此,則執行第一個匹配塊。即Java虛擬機會把實際拋出的異常對象依次和各個catch代碼塊聲明的異常類型匹配,如果異常對象爲某個異常類型或其子類的實例,就執行這個catch代碼塊,不會再執行其他的 catch代碼塊
5) 可嵌套 try-catch-finally 結構。
6) 在 try-catch-finally 結構中,可重新拋出異常。
7) 除了下列情況,總將執行 finally 做爲結束:JVM過早終止(調用 System.exit(int));在finally 塊中拋出一個未處理的異常;計算機斷電、失火、或遭遇病毒攻擊。

3、 try、catch、finally語句塊的執行順序:

1)當try沒有捕獲到異常時:try語句塊中的語句逐一被執行,程序將跳過catch語句塊,執行finally語句塊和其後的語句;

2)當try捕獲到異常,catch語句塊裏沒有處理此異常的情況:當try語句塊裏的某條語句出現異常時,而沒有處理此異常的catch語句塊時,此異常將會拋給JVM處理,finally語句塊裏的語句還是會被執行,但finally語句塊後的語句不會被執行;

3)當try捕獲到異常,catch語句塊裏有處理此異常的情況:在try語句塊中是按照順序來執行的,當執行到某一條語句出現異常時,程序將跳到catch語句塊,並與catch語句塊逐一匹配,找到與之對應的處理程序,其他的catch語句塊將不會被執行,而try語句塊中,出現異常之後的語句也不會被執行,catch語句塊執行完後,執行finally語句塊裏的語句,最後執行finally語句塊後的語句;

圖示try、catch、finally語句塊的執行:

 圖2  圖示try、catch、finally語句塊的執行

 

拋出異常

任何Java代碼都可以拋出異常,如:自己編寫的代碼、來自Java開發環境包中代碼,或者Java運行時系統。無論是誰,都可以通過Java的throw語句拋出異常。從方法中拋出的任何異常都必須使用throws子句。

Throws拋出異常的規則:

1) 如果是不可查異常(uncheckedexception),即Error、RuntimeException或它們的子類,那麼可以不使用throws關鍵字來聲明要拋出的異常,編譯仍能順利通過,但在運行時會被系統拋出。

2)必須聲明方法可拋出的任何可查異常(checked exception)。即如果一個方法可能出現受可查異常,要麼用try-catch語句捕獲,要麼用throws子句聲明將它拋出,否則會導致編譯錯誤

3)僅當拋出了異常,該方法的調用者才必須處理或者重新拋出該異常。當方法的調用者無力處理該異常的時候,應該繼續拋出,而不是囫圇吞棗。

4)調用方法必須遵循任何可查異常的處理和聲明規則。若覆蓋一個方法,則不能聲明與覆蓋方法不同的異常。聲明的任何異常必須是被覆蓋方法所聲明異常的同類或子類。

Java常見異常

在Java中提供了一些異常用來描述經常發生的錯誤,對於這些異常,有的需要程序員進行捕獲處理或聲明拋出,有的是由Java虛擬機自動進行捕獲處理。Java中常見的異常類:

1.runtimeException子類:

   1java.lang.ArrayIndexOutOfBoundsException

   數組索引越界異常。當對數組的索引值爲負數或大於等於數組大小時拋出。

   2java.lang.ArithmeticException

   算術條件異常。譬如:整數除零等。

   3java.lang.NullPointerException

   空指針異常。當應用試圖在要求使用對象的地方使用了null時,拋出該異常。譬如:調用null對象的實例方法、訪問null對象的屬性、計算null對象的長度、使用throw語句拋出null等等

   4java.lang.ClassNotFoundException

   找不到類異常。當應用試圖根據字符串形式的類名構造類,而在遍歷CLASSPAH之後找不到對應名稱的class文件時,拋出該異常。

  5java.lang.NegativeArraySizeException  數組長度爲負異常

  6java.lang.ArrayStoreException 數組中包含不兼容的值拋出的異常

  7java.lang.SecurityException 安全性異常

  8java.lang.IllegalArgumentException 非法參數異常

2.IOException

IOException:操作輸入流和輸出流時可能出現的異常。

EOFException  文件已結束異常

FileNotFoundException  文件未找到異常

3. 其他

ClassCastException   類型轉換異常類

ArrayStoreException 數組中包含不兼容的值拋出的異常

SQLException  操作數據庫異常類

NoSuchFieldException  字段未找到異常

NoSuchMethodException  方法未找到拋出的異常

NumberFormatException   字符串轉換爲數字拋出的異常

StringIndexOutOfBoundsException字符串索引超出範圍拋出的異常

IllegalAccessException 不允許訪問某類異常

InstantiationException 當應用程序試圖使用Class類中的newInstance()方法創建一個類的實例,而指定的類對象無法被實例化時,拋出該異常

17、說一說對 java io 的理解

Java 的 I/O 操作類在包 java.io 下,大概有將近 80 個類,但是這些類大概可以分成四組,分別是:

  1. 基於字節操作的 I/O 接口:InputStream 和 OutputStream
  2. 基於字符操作的 I/O 接口:Writer 和 Reader
  3. 基於磁盤操作的 I/O 接口:File
  4. 基於網絡操作的 I/O 接口:Socket

18、SpringMVC 處理請求的流程

 

19、分庫分表的幾種常見形式

在談論數據庫架構和數據庫優化的時候,我們經常會聽到“分庫分表”、“分片”、“Sharding”…這樣的關鍵詞。讓人感到高興的是,這些朋友所服務的公司業務量正在(或者即將面臨)高速增長,技術方面也面臨着一些挑戰。讓人感到擔憂的是,他們系統真的就需要“分庫分表”了嗎?“分庫分表”有那麼容易實踐嗎?爲此,筆者整理了分庫分表中可能遇到的一些問題,並結合以往經驗介紹了對應的解決思路和建議。

垂直分表

垂直分表在日常開發和設計中比較常見,通俗的說法叫做“大表拆小表”,拆分是基於關係型數據庫中的“列”(字段)進行的。通常情況,某個表中的字段比較多,可以新建立一張“擴展表”,將不經常使用或者長度較大的字段拆分出去放到“擴展表”中,如下圖所示:

小結

在字段很多的情況下,拆分開確實更便於開發和維護(筆者曾見過某個遺留系統中,一個大表中包含100多列的)。某種意義上也能避免“跨頁”的問題(MySQL、MSSQL底層都是通過“數據頁”來存儲的,“跨頁”問題可能會造成額外的性能開銷,這裏不展開,感興趣的朋友可以自行查閱相關資料進行研究)。

拆分字段的操作建議在數據庫設計階段就做好。如果是在發展過程中拆分,則需要改寫以前的查詢語句,會額外帶來一定的成本和風險,建議謹慎。

垂直分庫

垂直分庫在“微服務”盛行的今天已經非常普及了。基本的思路就是按照業務模塊來劃分出不同的數據庫,而不是像早期一樣將所有的數據表都放到同一個數據庫中。如下圖:

 

小結

系統層面的“服務化”拆分操作,能夠解決業務系統層面的耦合和性能瓶頸,有利於系統的擴展維護。而數據庫層面的拆分,道理也是相通的。與服務的“治理”和“降級”機制類似,我們也能對不同業務類型的數據進行“分級”管理、維護、監控、擴展等。

衆所周知,數據庫往往最容易成爲應用系統的瓶頸,而數據庫本身屬於“有狀態”的,相對於Web和應用服務器來講,是比較難實現“橫向擴展”的。數據庫的連接資源比較寶貴且單機處理能力也有限,在高併發場景下,垂直分庫一定程度上能夠突破IO、連接數及單機硬件資源的瓶頸,是大型分佈式系統中優化數據庫架構的重要手段。

然後,很多人並沒有從根本上搞清楚爲什麼要拆分,也沒有掌握拆分的原則和技巧,只是一味的模仿大廠的做法。導致拆分後遇到很多問題(例如:跨庫join,分佈式事務等)。

水平分表

水平分表也稱爲橫向分表,比較容易理解,就是將表中不同的數據行按照一定規律分佈到不同的數據庫表中(這些表保存在同一個數據庫中),這樣來降低單表數據量,優化查詢性能。最常見的方式就是通過主鍵或者時間等字段進行Hash和取模後拆分。如下圖所示:

小結

水平分表,能夠降低單表的數據量,一定程度上可以緩解查詢性能瓶頸。但本質上這些表還保存在同一個庫中,所以庫級別還是會有IO瓶頸。所以,一般不建議採用這種做法。

水平分庫分表

水平分庫分表與上面講到的水平分表的思想相同,唯一不同的就是將這些拆分出來的表保存在不同的數據中。這也是很多大型互聯網公司所選擇的做法。如下圖:

某種意義上來講,有些系統中使用的“冷熱數據分離”(將一些使用較少的歷史數據遷移到其他的數據庫中。而在業務功能上,通常默認只提供熱點數據的查詢),也是類似的實踐。在高併發和海量數據的場景下,分庫分表能夠有效緩解單機和單庫的性能瓶頸和壓力,突破IO、連接數、硬件資源的瓶頸。當然,投入的硬件成本也會更高。同時,這也會帶來一些複雜的技術問題和挑戰(例如:跨分片的複雜查詢,跨分片事務等)

分庫分表的難點

垂直分庫帶來的問題和解決思路:

跨庫join的問題

在拆分之前,系統中很多列表和詳情頁所需的數據是可以通過sql join來完成的。而拆分後,數據庫可能是分佈式在不同實例和不同的主機上,join將變得非常麻煩。而且基於架構規範,性能,安全性等方面考慮,一般是禁止跨庫join的。那該怎麼辦呢?首先要考慮下垂直分庫的設計問題,如果可以調整,那就優先調整。如果無法調整的情況,下面筆者將結合以往的實際經驗,總結幾種常見的解決思路,並分析其適用場景。

跨庫Join的幾種解決思路

全局表

所謂全局表,就是有可能系統中所有模塊都可能會依賴到的一些表。比較類似我們理解的“數據字典”。爲了避免跨庫join查詢,我們可以將這類表在其他每個數據庫中均保存一份。同時,這類數據通常也很少發生修改(甚至幾乎不會),所以也不用太擔心“一致性”問題。

字段冗餘

這是一種典型的反範式設計,在互聯網行業中比較常見,通常是爲了性能來避免join查詢。

舉個電商業務中很簡單的場景:

“訂單表”中保存“賣家Id”的同時,將賣家的“Name”字段也冗餘,這樣查詢訂單詳情的時候就不需要再去查詢“賣家用戶表”。

字段冗餘能帶來便利,是一種“空間換時間”的體現。但其適用場景也比較有限,比較適合依賴字段較少的情況。最複雜的還是數據一致性問題,這點很難保證,可以藉助數據庫中的觸發器或者在業務代碼層面去保證。當然,也需要結合實際業務場景來看一致性的要求。就像上面例子,如果賣家修改了Name之後,是否需要在訂單信息中同步更新呢?

數據同步

定時A庫中的tab_a表和B庫中tbl_b有關聯,可以定時將指定的表做同步。當然,同步本來會對數據庫帶來一定的影響,需要性能影響和數據時效性中取得一個平衡。這樣來避免複雜的跨庫查詢。筆者曾經在項目中是通過ETL工具來實施的。

系統層組裝

在系統層面,通過調用不同模塊的組件或者服務,獲取到數據並進行字段拼裝。說起來很容易,但實踐起來可真沒有這麼簡單,尤其是數據庫設計上存在問題但又無法輕易調整的時候。

具體情況通常會比較複雜。下面筆者結合以往實際經驗,並通過僞代碼方式來描述。

簡單的列表查詢的情況

僞代碼很容易理解,先獲取“我的提問列表”數據,然後再根據列表中的UserId去循環調用依賴的用戶服務獲取到用戶的RealName,拼裝結果並返回。

有經驗的讀者一眼就能看出上訴僞代碼存在效率問題。循環調用服務,可能會有循環RPC,循環查詢數據庫…不推薦使用。再看看改進後的:

這種實現方式,看起來要優雅一點,其實就是把循環調用改成一次調用。當然,用戶服務的數據庫查詢中很可能是In查詢,效率方面比上一種方式更高。(坊間流傳In查詢會全表掃描,存在性能問題,傳聞不可全信。其實查詢優化器都是基本成本估算的,經過測試,在In語句中條件字段有索引的時候,條件較少的情況是會走索引的。這裏不細展開說明,感興趣的朋友請自行測試)

小結

簡單字段組裝的情況下,我們只需要先獲取“主表”數據,然後再根據關聯關係,調用其他模塊的組件或服務來獲取依賴的其他字段(如例中依賴的用戶信息),最後將數據進行組裝。

通常,我們都會通過緩存來避免頻繁RPC通信和數據庫查詢的開銷。

列表查詢帶條件過濾的情況

在上述例子中,都是簡單的字段組裝,而不存在條件過濾。看拆分前的SQL

這種連接查詢並且還帶條件過濾的情況,想在代碼層面組裝數據其實是非常複雜的(尤其是左表和右表都帶條件過濾的情況會更復雜),不能像之前例子中那樣簡單的進行組裝了。試想一下,如果像上面那樣簡單的進行組裝,造成的結果就是返回的數據不完整,不準確。 

有如下幾種解決思路:

查出所有的問答數據,然後調用用戶服務進行拼裝數據,再根據過濾字段state字段進行過濾,最後進行排序和分頁並返回。

這種方式能夠保證數據的準確性和完整性,但是性能影響非常大,不建議使用。

查詢出state字段符合/不符合的UserId,在查詢問答數據的時候使用in/not in進行過濾,排序,分頁等。過濾出有效的問答數據後,再調用用戶服務獲取數據進行組裝。

這種方式明顯更優雅點。筆者之前在某個項目的特殊場景中就是採用過這種方式實現。

跨庫事務(分佈式事務)的問題

按業務拆分數據庫之後,不可避免的就是“分佈式事務”的問題。以往在代碼中通過spring註解簡單配置就能實現事務的,現在則需要花很大的成本去保證一致性。這裏不展開介紹, 
感興趣的讀者可以自行參考《分佈式事務一致性解決方案》,鏈接地址: 
http://www.infoq.com/cn/articles/solution-of-distributed-system-transaction-consistency

垂直分庫總結和實踐建議

本篇中主要描述了幾種常見的拆分方式,並着重介紹了垂直分庫帶來的一些問題和解決思路。讀者朋友可能還有些問題和疑惑。

1.我們目前的數據庫是否需要進行垂直分庫?

根據系統架構和公司實際情況來,如果你們的系統還是個簡單的單體應用,並且沒有什麼訪問量和數據量,那就彆着急折騰“垂直分庫”了,否則沒有任何收益,也很難有好結果。

切記,“過度設計”和“過早優化”是很多架構師和技術人員常犯的毛病。

2.垂直拆分有沒有原則或者技巧?

沒有什麼黃金法則和標準答案。一般是參考系統的業務模塊拆分來進行數據庫的拆分。比如“用戶服務”,對應的可能就是“用戶數據庫”。但是也不一定嚴格一一對應。有些情況下,數據庫拆分的粒度可能會比系統拆分的粒度更粗。筆者也確實見過有些系統中的某些表原本應該放A庫中的,卻放在了B庫中。有些庫和表原本是可以合併的,卻單獨保存着。還有些表,看起來放在A庫中也OK,放在B庫中也合理。

如何設計和權衡,這個就看實際情況和架構師/開發人員的水平了。

3.上面舉例的都太簡單了,我們的後臺報表系統中join的表都有n個了, 
分庫後該怎麼查?

有很多朋友跟我提過類似的問題。其實互聯網的業務系統中,本來就應該儘量避免join的,如果有多個join的,要麼是設計不合理,要麼是技術選型有誤。請自行科普下OLAP和OLTP,報表類的系統在傳統BI時代都是通過OLAP數據倉庫去實現的(現在則更多是藉助離線分析、流式計算等手段實現),而不該向上面描述的那樣直接在業務庫中執行大量join和統計。

由於篇幅關係,下篇中我們再繼續細聊“水平分庫分表”相關的話題

20、詳解Java中的訪問控制修飾public,protected,default,private

修飾符      當前類   同包     子類    其他包
public     √    √     √     √
protected    √     √     √     ×
default     √     √     ×     ×
private     √     ×     ×     ×

所以,訪問權限修飾符權限從高到低排列是public,protected,default,private。

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