面試大雜燴

面試大雜燴彙總

對象頭和synchronize

在JVM中,對象在內存中除了本身的數據外還會有個對象頭,對於普通對象而言,其對象頭中有兩類信息:mark word和類型指針。另外對於數組而言還會有一份記錄數組長度的數據。

類型指針是指向該對象所屬類對象的指針,mark word用於存儲對象的HashCode、GC分代年齡、鎖狀態等信息。在32位系統上mark word長度爲32bit,64位系統上長度爲64bit。爲了能在有限的空間裏存儲下更多的數據,其存儲格式是不固定的,在32位系統上各狀態的格式如下:

可以看到鎖信息也是存在於對象的mark word中的。當對象狀態爲偏向鎖(biasable)時,mark word存儲的是偏向的線程ID;當狀態爲輕量級鎖(lightweight locked)時,mark word存儲的是指向線程棧中Lock Record的指針;當狀態爲重量級鎖(inflated)時,爲指向堆中的monitor對象的指針

輕量級鎖

線程在執行同步塊之前,JVM會先在當前的線程的棧幀中創建一個Lock Record,其包括一個用於存儲對象頭中的 mark word(官方稱之爲Displaced Mark Word)以及一個指向對象的指針。下圖右邊的部分就是一個Lock Record。

  • 加鎖過程

  • 1.在線程棧中創建一個Lock Record,將其obj(即上圖的Object reference)字段指向鎖對象。

  • 2.直接通過CAS指令將Lock Record的地址存儲在對象頭的mark word中,如果對象處於無鎖狀態則修改成功,代表該線程獲得了輕量級鎖。如果失敗,進入到步驟3。

  • 3.如果是當前線程已經持有該鎖了,代表這是一次鎖重入。設置Lock Record第一部分(Displaced Mark Word)爲null,起到了一個重入計數器的作用。然後結束。

  • 4.走到這一步說明發生了競爭,需要膨脹爲重量級鎖。

  • 解鎖過程
  • 1.遍歷線程棧,找到所有obj字段等於當前鎖對象的Lock Record。

  • 2.如果Lock Record的Displaced Mark Word爲null,代表這是一次重入,將obj設置爲null後continue。

  • 3.如果Lock Record的Displaced Mark Word不爲null,則利用CAS指令將對象頭的mark word恢復成爲Displaced Mark Word。如果成功,則continue,否則膨脹爲重量級鎖。

偏向鎖

JDK1.6中爲了提高一個對象在一段很長的時間內都只被一個線程用做鎖對象場景下的性能,引入了偏向鎖,在第一次獲得鎖時,會有一個CAS操作,之後該線程再獲取鎖,只會執行幾個簡單的命令,而不是開銷相對較大的CAS命令。

面試:—-利用solr實現商品的搜索功能

有一個1G的文件,每一行是一個詞,詞的大小不超過16字節,內存限制大小是1M。返回頻數最高的100個詞

由上面那兩個例題,分而治之 + hash統計 + 堆/快速排序這個套路再多多驗證下。此題又是文件很大,又是內存受限,無非還是

  • 分而治之/hash映射 順序讀文件中,對於每個詞x,取hash(x)%5000,然後按照該值存到5000個小文件(記爲x0,x1,…x4999)中。這樣每個文件大概是200k。 如果其中的有的文件超過了1M,還可以按照類似的方法繼續下分,直到分解得到的小文件都不超過1M

  • HashMap統計 對每個小文件,採用trie樹/HashMap等統計每個文件中出現的詞以及相應的頻率

  • 堆/歸併排 取出出現頻率最大的100個詞(可以用含100個結點的最小堆)後,再把100個詞及相應的頻率存入文件,這樣又得到了5000個文件。最後就是把這5000個文件進行歸併(類似於歸併排序)的過程了。

    海量數據分佈在10臺電腦中,想個辦法高效統計出這批數據的TOP10,如果每個數據元素只出現一次,而且只出現在某一臺機器中,那麼可以採取以下步驟統計出現次數TOP10的數據元素:

  • 堆排序 在每臺電腦上求出TOP10,可以採用包含10個元素的堆完成(TOP10小,用最大堆,TOP10大,用最小堆,比如求TOP10大,我們首先取前10個元素調整成最小堆,如果發現,然後掃描後面的數據,並與堆頂元素比較,如果比堆頂元素大,那麼用該元素替換堆頂,然後再調整爲最小堆。最後堆中的元素就是TOP10大)。

  • 求出每臺電腦上的TOP10後,然後把這100臺電腦上的TOP10組合起來,共1000個數據,再利用上面類似的方法求出TOP10就可以了。

如果同一個元素重複出現在不同的電腦中呢

這個時候,你可以有兩種方法

遍歷所有數據,重新hash取模,使同一個元素只出現在單獨的一臺電腦中,然後採用上面所說的方法,統計每臺電腦中各個元素的出現次數找出TOP10,繼而組合100臺電腦上的TOP10,找出最終的TOP10

暴力求解:直接統計每臺電腦中各個元素的出現次數,然後把同一個元素在不同機器中的出現次數相加,最終從所有數據中找出TOP10

第6章redis使用場景(瞭解)

1、取最新N個數據的操作

比如典型的取你網站的最新文章,通過下面方式,我們可以將最新的5000條評論的ID放在Redis的List集合中,並將超出集合部分從數據庫獲取

  • (1)使用LPUSH latest.comments命令,向list集合中插入數據
  • (2)插入完成後再用LTRIM latest.comments05000命令使其永遠只保存最近5000個ID
  • (3)然後我們在客戶端獲取某一頁評論時可以用下面的邏輯(僞代碼)
    僞代碼

    FUNCTION get_latest_comments(start,num_items):id_list=redis.lrange(”latest.comments”,start,start+num_items-1) IFid_list.length< num_items id_list=SQL_DB(”SELECT…ORDER BY time LIMIT..…”) END RETURN id_list END

如果你還有不同的篩選維度,比如某個分類的最新N條,那麼你可以再建一個按此分類的List,只存ID的話,Redis是非常高效的。

2、排行榜應用,取TOPN操作

這個需求與上面需求的不同之處在於,前面操作以時間爲權重,這個是以某 個條件爲權重,比如按頂的次數排序,這時候就需要我們的sorted set出 馬了,將你要排序的值設置成sorted set的score,將具體的數據設置成 相應的value,每次只需要執行一條ZADD命令即可。

3、需要精準設定過期時間的應用

比如你可以把上面說到的sorted set的score值設置成過期時間的時間 戳,那麼就可以簡單地通過過期時間排序,定時清除過期數據了,不僅是清 除Redis中的過期數據,你完全可以把Redis裏這個過期時間當成是對數據 庫中數據的索引,用Redis來找出哪些數據需要過期刪除,然後再精準地從 數據庫中刪除相應的記錄。

4、計數器應用

Redis的命令都是原子性的,你可以輕鬆地利用INCR,DECR命令來構建計數 器系統。

5、Uniq操作,獲取某段時間所有數據排重值

這個使用Redis的set數據結構最合適了,只需要不斷地將數據往set中扔就 行了,set意爲集合,所以會自動排重。

6、實時系統,反垃圾系統

通過上面說到的set功能,你可以知道一個終端用戶是否進行了某個操作, 可以找到其操作的集合並進行分析統計對比等。沒有做不到,只有想不到。

7、Pub/Sub構建實時消息系統

Redis的Pub/Sub系統可以構建實時的消息系統,比如很多用Pub/Sub構建 的實時聊天系統的例子。

8、構建隊列系統

使用list可以構建隊列系統,使用sorted set甚至可以構建有優先級的隊列系統。

消息隊列面試相關

redis

12、MySQL裏有2000w數據,redis中只存20w的數據,如何保證redis中的數據都是熱點數據?

redis內存數據集大小上升到一定大小的時候,就會施行數據淘汰策略。

13、Redis有哪些適合的場景?

  • (1)、會話緩存(Session Cache)最常用的一種使用Redis的情景是會話緩存(session cache)。用Redis緩存會話比其他存儲(如Memcached)的優勢在於:Redis提供持久化。當維護一個不是嚴格要求一致性的緩存時,如果用戶的購物車信息全部丟失,大部分人都會不高興的,現在,他們還會這樣嗎?幸運的是,隨着 Redis 這些年的改進,很容易找到怎麼恰當的使用Redis來緩存會話的文檔。甚至廣爲人知的商業平臺Magento也提供Redis的插件。
  • (2)、全頁緩存(FPC)除基本的會話token之外,Redis還提供很簡便的FPC平臺。回到一致性問題,即使重啓了Redis實例,因爲有磁盤的持久化,用戶也不會看到頁面加載速度的下降,這是一個極大改進,類似PHP本地FPC。再次以Magento爲例,Magento提供一個插件來使用Redis作爲全頁緩存後端。此外,對WordPress的用戶來說,Pantheon有一個非常好的插件 wp-redis,這個插件能幫助你以最快速度加載你曾瀏覽過的頁面。
  • (3)、隊列Reids在內存存儲引擎領域的一大優點是提供 list 和 set 操作,這使得Redis能作爲一個很好的消息隊列平臺來使用。Redis作爲隊列使用的操作,就類似於本地程序語言(如Python)對 list 的 push/pop 操作。如果你快速的在Google中搜索“Redis queues”,你馬上就能找到大量的開源項目,這些項目的目的就是利用Redis創建非常好的後端工具,以滿足各種隊列需求。例如,Celery有一個後臺就是使用Redis作爲broker,你可以從這裏去查看。
  • (4),排行榜/計數器Redis在內存中對數字進行遞增或遞減的操作實現的非常好。集合(Set)和有序集合(Sorted Set)也使得我們在執行這些操作的時候變的非常簡單,Redis只是正好提供了這兩種數據結構。所以,我們要從排序集合中獲取到排名最靠前的10個用戶–我們稱之爲“user_scores”,我們只需要像下面一樣執行即可:當然,這是假定你是根據你用戶的分數做遞增的排序。如果你想返回用戶及用戶的分數,你需要這樣執行:ZRANGE user_scores 0 10 WITHSCORESAgora Games就是一個很好的例子,用Ruby實現的,它的排行榜就是使用Redis來存儲數據的,你可以在這裏看到。
  • (5)、發佈/訂閱最後(但肯定不是最不重要的)是Redis的發佈/訂閱功能。發佈/訂閱的使用場景確實非常多。我已看見人們在社交網絡連接中使用,還可作爲基於發佈/訂閱的腳本觸發器,甚至用Redis的發佈/訂閱功能來建立聊天系統!(不,這是真的,你可以去核實)。

Redis中的管道有什麼用?

一次請求/響應服務器能實現處理新的請求即使舊的請求還未被響應。這樣就可以將多個命令發送到服務器,而不用等待回覆,最後在一個步驟中讀取該答覆。這就是管道(pipelining),是一種幾十年來廣泛使用的技術。例如許多POP3協議已經實現支持這個功能,大大加快了從服務器下載新郵件的過程。

怎麼理解Redis事務?

事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行。事務在執行的過程中,不會被其他客戶端發送來的命令請求所打斷。事務是一個原子操作:事務中的命令要麼全部被執行,要麼全部都不執行。

Redis如何做內存優化?

儘可能使用散列表(hashes),散列表(是說散列表裏面存儲的數少)使用的內存非常小,所以你應該儘可能的將你的數據模型抽象到一個散列表裏面。比如你的web系統中有一個用戶對象,不要爲這個用戶的名稱,姓氏,郵箱,密碼設置單獨的key,而是應該把這個用戶的所有信息存儲到一張散列表裏面.

Redis事務相關的命令有哪幾個?

MULTI、EXEC、DISCARD、WATCH

Redis的內存佔用情況怎麼樣?

給你舉個例子: 100萬個鍵值對(鍵是0到999999值是字符串“hello world”)在我的32位的Mac筆記本上 用了100MB。同樣的數據放到一個key裏只需要16MB, 這是因爲鍵值有一個很大的開銷。 在Memcached上執行也是類似的結果,但是相對Redis的開銷要小一點點,因爲Redis會記錄類型信息引用計數等等。當然,大鍵值對時兩者的比例要好很多。64位的系統比32位的需要更多的內存開銷,尤其是鍵值對都較小時,這是因爲64位的系統裏指針佔用了8個字節。 但是,當然,64位系統支持更大的內存,所以爲了運行大型的Redis服務器或多或少的需要使用64位的系統。

都有哪些辦法可以降低Redis的內存使用情況呢?

如果你使用的是32位的Redis實例,可以好好利用Hash,list,sorted set,set等集合類型數據,因爲通常情況下很多小的Key-Value可以用更緊湊的方式存放到一起。

Redis是單線程的,如何提高多核CPU的利用率?

可以在同一個服務器部署多個Redis的實例,並把他們當作不同的服務器來使用,在某些時候,無論如何一個服務器是不夠的, 所以,如果你想使用多個CPU,你可以考慮一下分片(shard)。

Redis常見性能問題和解決方案?

  • (1) Master最好不要做任何持久化工作,如RDB內存快照和AOF日誌文件
  • (2) 如果數據比較重要,某個Slave開啓AOF備份數據,策略設置爲每秒同步一次
  • (3) 爲了主從複製的速度和連接的穩定性,Master和Slave最好在同一個局域網內
  • (4) 儘量避免在壓力很大的主庫上增加從庫
  • (5) 主從複製不要用圖狀結構,用單向鏈表結構更爲穩定,即:Master <- Slave1 <- Slave2 <- Slave3…這樣的結構方便解決單點故障問題,實現Slave對Master的替換。如果Master掛了,可以立刻啓用Slave1做Master,其他不變。

    Redis提供了哪幾種持久化方式?

  • RDB持久化方式能夠在指定的時間間隔能對你的數據進行快照存儲.
  • AOF持久化方式記錄每次對服務器寫的操作,當服務器重啓的時候會重新執行這些命令來恢復原始的數據,AOF命令以redis協議追加保存每次寫的操作到文件末尾.Redis還能對AOF文件進行後臺重寫,使得AOF文件的體積不至於過大.如果你只希望你的數據在服務器運行的時候存在,你也可以不使用任何持久化方式.你也可以同時開啓兩種持久化方式, 在這種情況下, 當redis重啓的時候會優先載入AOF文件來恢復原始的數據,因爲在通常情況下AOF文件保存的數據集要比RDB文件保存的數據集要完整.最重要的事情是瞭解RDB和AOF持久化方式的不同,讓我們以RDB持久化方式開始。

    如何選擇合適的持久化方式?

    一般來說, 如果想達到足以媲美PostgreSQL的數據安全性, 你應該同時使用兩種持久化功能。如果你非常關心你的數據, 但仍然可以承受數分鐘以內的數據丟失,那麼你可以只使用RDB持久化。有很多用戶都只使用AOF持久化,但並不推薦這種方式:因爲定時生成RDB快照(snapshot)非常便於進行數據庫備份, 並且 RDB 恢復數據集的速度也要比AOF恢復的速度要快,除此之外, 使用RDB還可以避免之前提到的AOF程序的bug。

用java代碼實現守護進程

思路:

實際上就是執行某一段程序(不停的循環執行),每隔5秒更新job.log文件
代碼如下:

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;
 
 
public class Program {
 
/**
 * @param args
 */
public static void main(String[] args) {
    File f = new File("/home/xieping/job.log");
    if (!f.exists()) {
        try {
            f.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    while (true) {
        try {
            BufferedWriter output = new BufferedWriter(new FileWriter(f));
            output.write(new Date().toString());
            output.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
  }
} 

打包,我是用eclipse打包的,導出個jar包就行了。 把文件放到/home/xieping/目錄下。文件名 job.jar。 開始設置爲守護進程了!!

	#java -jar job.jar &  

簡單吧,就加一個&符號。可以查看下

	#ps aux|grep job

看到了,果然啓動了。

	#more /home/xieping/job.log

文件的時間也一直換。

注意,在開多線程的情況下,進程可能無法終止。
#killall java 或者 #kill 進程ID 無效。需要強制終止。 #kill -9 進程ID。

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