- 項目
- 工作中遇到的印象深刻的問題
- 介紹一個項目中遇到的問題
- 挑復雜的,能說清楚就行,不一定是自己做的
- DDCI的性能優化
- 基於LDAP的嵌套的group信息進行權限配置和檢查,一方面可以放到redis緩存中;另一方面不應該自下而上一層層找所有的group來判斷,不如自上而下提前處理好各個group下面最終有哪些人,那么也就知道每個人所屬的所有層的group了,然后直接放緩存用於后面的權限判斷,每日刷新就行了
- cpu profiling(和java的jvisualjvm類似)
- 數據庫優化
- 索引
- sql語句
- 新三板的日志模塊由於單例模式有問題,導致出現大量異常時日志實例占用過多資源
- 機器人新聞的異動信息的異步處理,由於沒有使用mq,導致自己要另外開線程,如果有mq的話,就可以先放進去,然后有一個后台任務一直處理了。
- mcapps
- 壓測用狀態模式,避免面向過程編程,划分清晰
- 問題排查
- rabbitmq用臨時隊列來檢查數據情況
- 提前研究好整個流程,畫好圖,如rabbitmq的架構圖、微服務的架構圖、業務流程交互圖
- 工作中遇到的問題如何排查
- 先腦子里過一遍流程,按順序、按可能性一個個排查
- 也可以第一反應就去看日志、數據庫等
- 對崗位的問題
- 加班、值班、個人時間?
- 業務范圍
- 工作職責、和其他職能如前端同事的合作
- 和其他部門的、和總公司的合作關系
- 績效?
- 項目或需求的排期、流程、風格
- 上線流程、頻率
- 項目開發性設計,自己設計,自己定規則
- a.產品設計過程中面對的挑戰和難點;b、周圍同事對我的評價;c、產品設計考慮的幾個點
- 工作中遇到的印象深刻的問題
- 算法
- 鏈表
- 鏈表操作(在線寫代碼)
- 用單向鏈表表示十進制整數,求兩個正整數的和。
- 相當於鏈表倒序相加的變種,先反轉鏈表,再倒序相加,最后再反轉即可
- 兩個單鏈表求和
- 合並K個有序鏈表(我依然用了優先隊列,也可以使用分治)。
- 實現一個有順序的LinkedList,實現deleteAll(int val)方法刪除所有值為val的節點,和save(int val)方法添加一個值為val的節點。
- 單鏈表非遞歸翻轉,不借助其他數據結構
- 棧
- 棧,增加一個max方法;
- stack實現一個min方法,O(1)的復雜度
- 數組
- 一個無序數組找其子序列構成的和最大,要求子序列中的元素在原數組中兩兩都不相鄰
- 輸入一個int數組,返回一個數組,其中奇數都在左邊,偶數都在右邊
- 給一個數組和一個整數,輸出數組中所有和為給定整數的元素下標
- 一個有序數組,求一個數出現的次數
- 兩個線程,一個只能存有數組1、2、3和另一個存有a、b、c,然后通過調度,最終結果輸出1a2b3c
- 給個數組,只有重復的,求所有和為21的下標:不用n2的暴力算法,要考慮數字相同的下標,先說思路沒問 題才能共享屏幕寫
- 給你一個連續素數數組 讓你找出增長比第k小的位置
- 有序數組 A = [1, 2, 2, 3, 3, 5, 5, 5],b = 5,在A中找出b的序號的上下界,考察思路和代碼熟練度
- k路歸並排序
- 字符串
- 給定字符串的全排列
- 最小公共子串得長度,最大不重復子串的長度
- 是輸入[I love byte bytedance] Ilovebytebytedance,然后判斷中括號里的單詞能否組成括號外的語句中括號里的單詞用空格符隔開,括號外的語句不能由單詞重疊組成,如果輸入這個[I love byte bytedance] Ilovebytedance,預期結果返回true
- 樹
- 給出一棵二叉樹的根節點,現在有這個二叉樹的部分節點,要求這些節點最近的公共祖先。
- 給定一個二叉樹,返回二叉樹每層的最左的值。
- 二叉樹鏡像
- 反轉二叉樹
- 根據二叉樹前序遍歷生成avl樹
- 二叉樹的bfs
- LRU
- LRU cache(實現構造函數與get、put,我是用了LinkedHashMap,如果不用LinkedHashMap該如何設計)。
- LRU的實現
- lru原理要非常詳細的數據結果和過程結構
- 編碼實現LRU算法
- 大數據量
- 大數據量的IP去重
- 大量數據第K大的數(我說可以用優先隊列,問到priorityQueue的底層實現,小頂堆的實現方式(數組)、插入刪除過程)。
- 10G文件1G內存,
- 一個100G的文件,內存只有8G,如何給文件排序,文件內容都是按行存儲的URL
- 統計大量訪問日志(分幾百M 和 幾百G的場景);得出訪問次數最多的前K個人 (單台機器實現)
- 接雨水問題。用數組表示不同高度的台階,下雨后(足夠多的),能存多少水。
- 左右兩個指針即可
- 一個n邊形(P0到Pn), 每一邊都是垂直或水平的線段,給定數值k,以P0為起點將它平均分成k段,請打印出所有的k等分點坐標
- 先算一遍周長?還是說由於都是垂直或水平,會有捷徑?
- 缺失的第一個正數(leetcode第41題)
- 現有一個隨機數生成器可以生成0到4的數,現在要讓你用這個隨機數生成器生成0到6的隨機數,要保證生成的數概率均勻。
- 有 N 枚棋子,每個人一次可以拿1到 M 個,誰拿完后棋子的數量為0誰就獲勝。現在有1000顆棋子,每次最多拿8個,A 先拿,那么 A 有必勝的拿法嗎?第一個人拿完后剩余棋子的數量是8的倍數就必勝,否則就必輸。
- 手里有一副撲克牌。按照下列規則吧他堆放桌上。一,拿出最上面的一張牌,放桌上,然后把接下來的一張牌放在撲克牌的最下面。循環,直到沒有手牌。現在已知桌上牌的順序。求原手牌的順序
- 一排盒子,每個盒子上標了能走的最大步數,從第一個盒子開始,判斷能否走到最后的盒子
- 輸入一串空格分開的整數,相鄰不會重復如:1 2 4 3 5,只返回其中一個峰值就可以,比如返回 4 或者 5
- 峰值定義:大於相鄰兩個數字就是峰值;對於頭尾兩個數字,分別大於相鄰的一個數字即算峰值
- 附加條件 不使用額外的空間
- 找到一個文件里出現次數最多的的數字,文件大小遠大於內存容量
- 打印重復數字
- 查找前topK
- B B+ 跳表區別 時間復雜度
- 貪心算法
- ab 輪流拋硬幣,誰先拋出正面勝出,先拋勝出概率多少
- 假設是一個抽獎的游戲,不同的人是有不同的概率倍數,是一個整數,例如1、3、5...輸入100萬人,要求抽獎抽出來2萬個人;並且假設每個人都有一個唯一id
- 搜索引擎倒排原理及實現方式
- 鏈表
- 策略
- 分布式
- 分布鎖
- 分布式ID(講講雪花算法怎么實現的)
- 限流
- 讓你設計一個限流的系統怎么做?
- 令牌桶
- 限流。限流算法。令牌桶的不足
- 讓你設計一個限流的系統怎么做?
- 任務系統怎么保證任務完成后發獎一定成功
- 讓你設計一個延時任務系統怎么做
- 說了兩個方案,一個是使用 redis 的 ZSET 來實現,考慮分片來抗高並發,使用 redis 的持久化來實現落地,使用 redis 的哨兵實現故障轉移。一個是使用時間輪的方法。
- 設計一個文本編輯器實現插入/刪除/查詢
- 頭條文章向用戶推送避免重復推送的問題
- 微博限定用戶每次輸入最多140個字符,用戶如果傳字符串很長的鏈接,怎么辦
- 多個人玩貪吃蛇設計
- 設計群消息已讀功能
- 緩存設計
- 寫一個函數做下抽獎,輸入和輸出的數據結構自己設計
- 微信掃碼登錄
- 分布式
- 數據庫
-
分庫分區分表
- 數據庫分表 / 分庫 / 集群
- 如何分庫分表
- 分庫分表
-
索引
- 原理及實現
- 數據庫索引實現原理
- 平衡樹、B+樹
- 數據庫聊到了索引,B+樹(大致結構、為何要用、插入刪除大致流程)。
- 索引的實現方式,為什么會采用這種數據結構
- 非葉子節點不帶數據,這樣一個塊可以容納更多的索引項,一是可以降低樹的高度,二是一個內部節點可以定位更多的葉子節點。
- 葉子節點之間通過指針連接,范圍掃描將十分簡單。而對於B樹來說,則需要在葉子節點和內部節點間不停的往返移動。
- 平衡樹比簡單的折半查找要好,分布更均勻,樹的層數盡可能少
- 事物都是有兩面的, 索引能讓數據庫查詢數據的速度上升, 而使寫入數據的速度下降,原因很簡單的, 因為平衡樹這個結構必須一直維持在一個正確的狀態, 增刪改數據都會改變平衡樹各節點中的索引數據內容,破壞樹結構, 因此,在每次數據改變時, DBMS必須去重新梳理樹(索引)的結構以確保它的正確,這會帶來不小的性能開銷,也就是為什么索引會給查詢以外的操作帶來副作用的原因。
- 聚集索引與非聚集索引(主鍵索引和非主鍵索引的區別)
- 非聚集索引和聚集索引的區別在於, 通過聚集索引可以查到需要查找的數據, 而通過非聚集索引可以查到記錄對應的主鍵值 , 再使用主鍵的值通過聚集索引查找到需要的數據
- 聯合索引/復合索引(設計索引,為何最左原則)
- 關鍵要根據具體業務、代碼中的sql語句分析出哪些字段會用於where語句
- 數據庫3個字段的聯合索引,在用單字段時能否命中索引
- 能,但只能命中最左邊那個字段的單字段查詢
- 但是如果用兩個字段來查詢,必須是左邊的兩個
- 對於復合索引:Mysql從左到右的使用索引中的字段,一個查詢可以只使用索引中的一部份,但只能是最左側部分。例如索引是key index (a,b,c). 可以支持a | a,b| a,b,c 3種組合進行查找,但不支持 b,c進行查找 .當最左側字段是常量引用時,索引就十分有效。
- 數據庫索引:表有3列a,b,c,需支持查詢(a,b)(b) (b,c),索引如何建
- (a,b),(b,c)
- where a>1 and b>1;where a = 1; where b = 2,如何為這種條件語句建立索引;
- (a,b),(b)
- 索引不會包含有NULL值的列
- 只要列中包含有NULL值都將不會被包含在索引中,復合索引中只要有一列含有NULL值,那么這一列對於此復合索引就是無效的。所以我們在數據庫設計時不要讓字段的默認值為NULL。
- 表掃描(Table Scan)
- 最慢,即掃描全表
- hash索引(范圍查找為何不適用)。
- 數據庫如何建索引
- 數據庫索引+前綴匹配
- 主鍵與索引的聯系和區別
- 原理及實現
-
sql
- Sql查詢第三大的age
// 2、3名,第二次升序后,把第一名過濾掉了 SELECT t3.* FROM ( SELECT top 2 t2.* FROM ( SELECT top 3 NAME, SUBJECT, score, stuid FROM stuscore WHERE SUBJECT = '數學' ORDER BY score DESC ) t2 ORDER BY t2.score ) t3 ORDER BY t3.score DESC SELECT * FROM stuscore WHERE SUBJECT = '數學' ORDER BY score DESC LIMIT 2, 1;
- 每門課程都大於80分的學生名字
//not in SELECT DISTINCT A.name FROM Student A WHERE A.name not in( SELECT Distinct S.name FROM Student S WHERE S.score <80) //not exists SELECT DISTINCT A.name From Student A where not exists (SELECT 1 From Student S Where S.score <80 AND S.name =A.name) SELECT S.name FROM Student S GROUP BY S.name Having MIN(S.score)>=80
-
sql優化
- 數據庫查詢優化
-
事務、隔離級別、鎖
- 數據庫事務的隔離級別
- 事務的四大特性(ACID)
- 1、原子性(Atomicity)
- 原子性是指事務包含的所有操作要么全部成功,要么全部失敗回滾,因此事務的操作如果成功就必須要完全應用到數據庫,如果操作失敗則不能對數據庫有任何影響。
- 2、一致性(Consistency)
- 一致性是指事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之后都必須處於一致性狀態。
- 拿轉賬來說,假設用戶A和用戶B兩者的錢加起來一共是5000,那么不管A和B之間如何轉賬,轉幾次賬,事務結束后兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。
- 3、隔離性(Isolation)
- 隔離性是當多個用戶並發訪問數據庫時,比如操作同一張表時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作所干擾,多個並發事務之間要相互隔離。
- 即要達到這么一種效果:對於任意兩個並發的事務T1和T2,在事務T1看來,T2要么在T1開始之前就已經結束,要么在T1結束之后才開始,這樣每個事務都感覺不到有其他事務在並發地執行。
- 4、持久性(Durability)
- 持久性是指一個事務一旦被提交了,那么對數據庫中的數據的改變就是永久性的,即便是在數據庫系統遇到故障的情況下也不會丟失提交事務的操作。
- 例如我們在使用JDBC操作數據庫時,在提交事務方法后,提示用戶事務操作完成,當我們程序執行完成直到看到提示后,就可以認定事務以及正確提交,
- 即使這時候數據庫出現了問題,也必須要將我們的事務完全執行完成,否則就會造成我們看到提示事務處理完畢,但是數據庫因為故障而沒有執行事務的重大錯誤。
- 1、原子性(Atomicity)
- 事務的隔離級別(默認事務級別為可重復讀)
- 總的說,數據庫事務無非就兩種:讀取事務(select)、修改事務(update,insert)。在沒有事務隔離控制的時候,多個事務在同一時刻對同一數據的操作可能就會影響到最終期望的結果,通常有四種情況:
- (1)修改時允許修改(丟失更新)。兩個更新事務同時修改一條數據時,很顯然這種情況是最嚴重的了,程序中無論如何也不能出現這種情況,因為它會造成更新的丟失!
- (2)修改時允許讀取(臟讀)。一個更新事務更新一條數據時,另一個讀取事務讀取了還沒提交的更新,這種情況下會出現讀取到臟數據。更新期間發生讀
- (3)讀取時允許修改(不可重復讀)。一個讀取事務讀取一條數據時,另一個更新事務修改了這條數據,這時就會出現不可重現的讀取。讀之間發生更新
- (4)讀取時允許插入(幻讀)。一個讀取事務讀取時,另一個插入事務(注意此處時插入)插入了一條新數據,這樣就可能多讀出一條數據,出現幻讀。
- 總的說,數據庫事務無非就兩種:讀取事務(select)、修改事務(update,insert)。在沒有事務隔離控制的時候,多個事務在同一時刻對同一數據的操作可能就會影響到最終期望的結果,通常有四種情況:
- 事務的四大特性(ACID)
- 樂觀鎖 / 悲觀鎖
- 悲觀鎖(肯定沒問題,但是性能差)
- 總是假設最壞的情況,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖(共享資源每次只給一個線程使用,其它線程阻塞,用完后再把資源轉讓給其它線程)。傳統的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。Java中synchronized和ReentrantLock等獨占鎖就是悲觀鎖思想的實現
- 樂觀鎖(更新的時候復雜一點,還要有版本管理)
- 總是假設最好的情況,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號機制和CAS算法實現。樂觀鎖適用於多讀的應用類型,這樣可以提高吞吐量,像數據庫提供的類似於write_condition機制(寫時的加1后的版本號要大於前面取到的版本號),其實都是提供的樂觀鎖。在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現方式CAS實現的。
- 悲觀鎖(肯定沒問題,但是性能差)
- 數據庫事務的隔離級別
-
自增ID與uuid的優劣
- 自增主鍵
- 自增ID是在設計表時將id字段的值設置為自增的形式,這樣當插入一行數據時無需指定id會自動根據前一字段的ID值+1進行填充。在MySQL數據庫中,可通過sql語句AUTO_INCREMENT來對特定的字段啟用自增賦值 使用自增ID作為主鍵,能夠保證字段的原子性.
- 優點
- 數據庫自動編號,速度快,而且是增量增長,按順序存放,對於檢索非常有利;
- 數字型,占用空間小,易排序,在程序中傳遞也方便;
- 如果通過非系統增加記錄時,可以不用指定該字段,不用擔心主鍵重復問題。
- 缺點
- 因為自動增長,在手動要插入指定ID的記錄時會顯得麻煩,尤其是當系統與其它系統集成時,需要數據導入時,很難保證原系統的ID不發生主鍵沖突(前提是老系統也是數字型的)。特別是在新系統上線時,新舊系統並行存在,並且是異庫異構的數據庫的情況下,需要雙向同步時,自增主鍵將是你的噩夢;
- 在系統集成或割接時,如果新舊系統主鍵不同是數字型就會導致修改主鍵數據類型,這也會導致其它有外鍵關聯的表的修改,后果同樣很嚴重;
- 若系統也是數字型的,在導入時,為了區分新老數據,可能想在老數據主鍵前統一加一個字符標識(例如“o”,old)來表示這是老數據,那么自動增長的數字型又面臨一個挑戰。
- UUID
- UUID含義是通用唯一識別碼 (Universally Unique Identifier),指在一台機器上生成的數字,它保證對在同一時空中的所有機器都是唯一的。通常平台會提供生成的API。換句話說能夠在一定的范圍內保證主鍵id的唯一性。
- 優點
- 出現數據拆分、合並存儲的時候,能達到全局的唯一性
- 缺點
- 影響插入速度, 並且造成硬盤使用率低
- uuid之間比較大小相對數字慢不少, 影響查詢速度。
- uuid占空間大, 如果你建的索引越多, 影響越嚴重
- 自增主鍵
-
MySQL
- 索引
- mysql索引的使用和原理
- Mysql的索引結構 聚簇索引
- 存儲引擎
- 兩種存儲引擎比較
- mysql的innodb與myisam區別
- myisam(默認)
- MyISAM是MySQL默認的引擎,但是它沒有提供對數據庫事務的支持,也不支持行級鎖和外鍵,因此當INSERT(插入)或UPDATE(更新)數據時即寫操作需要鎖定整個表,效率便會低一些。不過和Innodb不同,MyISAM中存儲了表的行數,於是SELECT COUNT(*) FROM TABLE時只需要直接讀取已經保存好的值而不需要進行全表掃描。如果表的讀操作遠遠多於寫操作且不需要數據庫事務的支持,那么MyISAM也是很好的選擇。
- innodb(常用)
- Innodb引擎提供了對數據庫ACID事務的支持,並且實現了SQL標准的四種隔離級別。該引擎還提供了行級鎖和外鍵約束,它的設計目標是處理大容量數據庫系統,它本身其實就是基於MySQL后台的完整數據庫系統,MySQL運行時Innodb會在內存中建立緩沖池,用於緩沖數據和索引。但是該引擎不支持FULLTEXT類型的索引,而且它沒有保存表的行數,當SELECT COUNT(*) FROM TABLE時需要掃描全表。當需要使用數據庫事務時,該引擎當然是首選。由於鎖的粒度更小,寫操作不會鎖定全表,所以在並發較高時,使用Innodb引擎會提升效率。但是使用行級鎖也不是絕對的,如果在執行一個SQL語句時MySQL不能確定要掃描的范圍,InnoDB表同樣會鎖全表。
- MySQL 事務的四個隔離級別?
- 先說了四個級別的區別,然后說了每個級別可能產生的問題
- binlog 日志和 redolog 日志清楚嗎?
- 說了兩個日志的作用以及兩階段提交
- 日志系統主要有redo log(重做日志)和binlog(歸檔日志)。redo log是InnoDB存儲引擎層的日志,binlog是MySQL Server層記錄的日志, 兩者都是記錄了某些操作的日志(不是所有)自然有些重復(但兩者記錄的格式不同)。
- redolog
- redo log是InnoDB存儲引擎層的日志,又稱重做日志文件,用於記錄事務操作的變化,記錄的是數據修改之后的值,不管事務是否提交都會記錄下來。在實例和介質失敗(media failure)時,redo log文件就能派上用場,如數據庫掉電,InnoDB存儲引擎會使用redo log恢復到掉電前的時刻,以此來保證數據的完整性。
- 在一條更新語句進行執行的時候,InnoDB引擎會把更新記錄寫到redo log日志中,然后更新內存,此時算是語句執行完了,然后在空閑的時候或者是按照設定的更新策略將redo log中的內容更新到磁盤中,這里涉及到WAL即Write Ahead logging技術,他的關鍵點是先寫日志,再寫磁盤。
- binlog
- binlog是屬於MySQL Server層面的,又稱為歸檔日志,屬於邏輯日志,是以二進制的形式記錄的是這個語句的原始邏輯,依靠binlog是沒有crash-safe能力的
- 區別
- redo log是屬於innoDB層面,binlog屬於MySQL Server層面的,這樣在數據庫用別的存儲引擎時可以達到一致性的要求。
- redo log是物理日志,記錄該數據頁更新的內容;binlog是邏輯日志,記錄的是這個更新語句的原始邏輯
- redo log是循環寫,日志空間大小固定;binlog是追加寫,是指一份寫到一定大小的時候會更換下一個文件,不會覆蓋。
- binlog可以作為恢復數據使用,主從復制搭建,redo log作為異常宕機或者介質故障后的數據恢復使用。
- 索引
-
數據庫表(id, name, parentId)如何組裝成樹?
-
PostgreSQL
-
ORM
-
- Java
- 基礎
- JDK基礎 集合 鎖
- fail-fast
- string轉double
- JVM
- GC CMS,CMS的參數
- JVM的一些排查工具jstat, jstack ,jmap
- out Of memory Error 跟 stack Over flow Error 的比較
- 源碼
- Hash相關數據結構
- Set實現、HashSet
- 解決哈希沖突的方法(詳細說說實現)、哈希一致性算法(為什么是環形結構,聊到應用場景會問到分布式緩存)
- 問HashSet底層實現的時候自然而然提到HashHashMap、HashMap底層實現、1.8改進、線程安不安全、為什么不安全,解決方式。
- Hashtable、concurrentHashMap(並發、分段鎖、cas、synchronized、鎖粒度)
- HashMap
- hashmap實現方式
- 自己設計hash;
- Map 底層原理
- java 的 array 跟 list的比較,底層原理上的
- Hash相關數據結構
- 多線程
- 線程池的工作流程。線程的幾種狀態,什么情況會進入阻塞
- java多線程知識點,如synchronized,reentrantLock以及兩者比較
- java線程池,submit跟execute的區別
- java線程池基礎知識、自己實現一個線程池
- 並發控制(樂觀鎖,悲觀鎖)
- 異常處理
- 設計模式
- 設計模式總結 - 總覽
- 六大設計原則
- 基礎
- 中間件
- Redis
- Redis是單線程的,所以很安全。
- 數據結構
- 數據結構有哪些?分別怎么實現的?
- string
- 使用場景
- 存儲簡單的value值,如用Incrby命令操作一個IP地址的訪問次數,用於判斷什么時候封鎖一個惡意的IP地址
- Set(key, value)
- Get(key)
- 使用場景
- hash
- 使用場景
- 存儲復雜的value值,和使用string相比,避免了修改其中某個字段時,需要反序列化字符串
- Hset(key, field, value)
- Hget(key, field)
- 使用場景
- list
- 使用場景
- 實現最新消息的排行
- 模擬消息隊列,利用list的push命令,將任務存在list集合中,同時使用另一個pop命令,將任務從集合中取出。
- 比如電商中的秒殺可以用這種方式來完成一個秒殺活動。
- 使用場景
- set
- 使用場景
- 微博中將每個人的好友存在集合(Set)中,避免列表中出現某個好友的兩次,這樣求兩個人的共同好友的操作,只需要求交集即可。
- 可以自動去重
- 使用場景
- ZSET
- 使用場景
- 都是利用了其高效的排序、redis的性能
- 查看商品詳情時,都會有一個綜合排名,還可以按照價格進行排名
- 延遲隊列
- 概念
- 即Sorted set
- 可以以某一個條件為權重進行排序
- Redis有序集合和Redis集合類似,是不包含 相同字符串的合集。它們的差別是,每個有序集合 的成員都關聯着一個評分,這個評分用於把有序集 合中的成員按最低分到最高分排列。
- 使用有序集合,你可以非常快地(O(log(N)))完成添加,刪除和更新元素的操作。 因為元素是在插入時就排好序的,所以很快地通過評分(score)或者 位次(position)獲得一個范圍的元素。 訪問有序集合的中間元素同樣也是非常快的,因此你可以使用有序集合作為一個沒用重復成員的智能列表。 在這個列表中, 你可以輕易地訪問任何你需要的東西: 有序的元素,快速的存在性測試,快速訪問集合中間元素
- zset 延時隊列怎么實現的
- 將延遲的時間+系統當前時間,作為元素的score屬性的值,在while循環中,每次取zset集合中排在最前面的一個元素,通過判斷該元素的score值是否大於或等於當前系統時間,來判定隊列中該元素是否已達到延遲的時間。
- 然后多個線程的環境怎么保證任務不被多個線程搶了?這里可以使用Redis的zrem命令來實現
- Redis Zrem 命令用於移除有序集中的一個或多個成員,不存在的成員將被忽略。
- Redis 的 ZSET 怎么實現的?
- 盡量介紹的全一點,跳躍表加哈希表以及壓縮鏈表
- 和平衡樹(紅黑樹)等的對比
- redis zset內部實現
- Redis 的 ZSET 做排行榜時,如果要實現分數相同時按時間順序排序怎么實現?
- 使用Redis Zset來處理活動常用排行榜(精確排行)
- Redis 排行榜 相同分數根據時間優先排行
- 默認
- Redis 提供了按分數進行排序的有序集合。 比如在游戲里面,比如戰斗力排行,充值排行,用默認的Redis 實現就可以達到需求。
- 但是,比如等級排行,大家都是30級,誰先到30級誰第一。Redis 默認實現是,相同分數的成員按字典順序排序(0 ~9 , A ~Z,a ~ z),所以相同分數排序就不能根據時間優先來排序。
- 方案
- 分值由業務分值+時間戳組成(給分值賦值時,業務分值乘以時間戳的位數,然后加上時間戳,得到存到redis中的分值。)。由於redis的分值只有有限的16位,先給正常的業務分值留夠位置(比如粉絲數最大可能到億級,那么就要留9位),那么只剩7位給時間戳了(以秒為單位的話)。由於redis是將分值越大的排名越靠前,而業務上一般需要時間戳越小的越排名靠前說明他更早完成當前業務分值,所以需要用一個常量(秒數)減去當前時間戳(活動開始后的秒數)的差值再作為分值的時間戳部分。而且要保證這個7位是夠用的,不然如果活動時間過長的話,7位的秒數可能不夠用。
- 首先我們要理解一個redis的排序,redis的zset中的score值為double類型,精度只有16位,事實上在存儲9999999999999999 16位整數的時候,會變為17位的10000000000000000,超過17位會變為科學計數法1e+17。
- 在活動中,我們要保證用戶的分數不能丟失,所以必須保證在16位以內
- 首先保證數字A-結束時間為一個7位數 ,那么我們要給這個結束時間+1000000 (一百萬) 得到數字A,這樣能最低限度保證活動結束之前時間戳系數的位數不變為7位數。
- 然后我們要保證數字A-活動開始時間也是一個七位數,也就是說開始時間和結束時間之間的跨度只能是900w,一旦超過900w,那么數字A減去開始時間就會得到一個8位數。
- 說了一個將 score 拆成高 32 位和低 32 位,高 32 位存分數,低 32 位存時間的方法。問還有沒有其他方法,想不出了
- 如果有三個,四個排序條件怎么辦,這種情況還是推薦使用數據庫,就別考慮 Redis了 。Redis 優勢在於可以做到實時排行
- 操作
- zadd
- zcard
- zrange
- zrank
- zcount
- zrem
- zscore
- zincrby
- 等等
- 使用場景
- 持久化
- RDB
-
類似於數據庫的全量備份,全量替換
-
在指定的時間間隔內將內存中的所有數據集快照寫入磁盤,也就是行話講的SnapShot快照,它恢復時是將快照文件直接讀到內存里,全量的替換原先的文件。
-
Redis會單獨創建(fork)一個子進程來進行持久化,不會影響主進程,會先將數據寫入到一個臨時文件中,待持久化過程都結束了,再用這個臨時文件替換上次持久化好的文件。
-
整個過程中,主進程是不進行任何IO操作的,這就確保了極高的性能,如果需要進行大規模數據的恢復,且對於數據恢復的完整性不是非常敏感,那RDB的方式要比AOF方式更加的高效。
-
操作
- 執行save(阻塞, 只管保存快照,其他的等待) 或者是bgsave (異步)命令
-
過程
- 通過rdbSave、rdbLoad操作在內存中的數據對象和磁盤中的RDB文件之間進行轉換。
-
配置
- 打開 redis.conf 文件,找到 SNAPSHOTTING 對應內容
-
RDB核心規則配置(重點)
- save <指定時間間隔> <執行指定次數更新操作>,滿足條件就將內存中的數據同步到硬盤中。官方出廠配置默認是 900秒內有1個更改,300秒內有10個更改以及60秒內有10000個更改,則將內存中的數據快照寫入磁盤。
- 若不想用RDB方案,可以把 save "" 的注釋打開,下面三個注釋。
save <seconds> <changes> # save "" save 900 1 save 300 10 save 60 10000
-
指定本地數據庫文件名,一般采用默認的 dump.rdb:dbfilename dump.rdb
-
指定本地數據庫存放目錄,一般也用默認配置:dir ./
-
默認開啟數據壓縮:rdbcompression yes
-
- 打開 redis.conf 文件,找到 SNAPSHOTTING 對應內容
-
優點
- 節省磁盤空間
- 恢復速度快
- 不影響Redis的正常使用,高效。
-
缺點
- 雖然Redis在fork時使用了寫時拷貝技術,但是如果數據量龐大時還是比較消耗性能的。
- 粒度比較大,滿足一定條件時才會觸發備份,那么在備份周期在一定間隔時間做一次備份,所以如果Redis意外down掉的話,就會丟失最后一次快照后的所有修改。
- 最后一次持久化后的數據可能會丟失。
-
- AOF
-
和RDB方式互補
-
類似於數據庫的基於事務日志的恢復機制
-
Redis 默認不開啟。它的出現是為了彌補RDB的不足(數據的不一致性),所以它采用日志的形式來記錄每個寫操作,並追加到文件中。Redis 重啟的會根據日志文件的內容將寫指令從前到后執行一次以完成數據的恢復工作。
-
以日志的形式來記錄每個寫操作,將Redis執行過得所有寫指令記錄下來(讀操作不記錄),只許追加文件但不可以改寫文件(增量的),Redis啟動之初會讀取該文件重新構建數據,換言之,Redis重啟的話就根據日志文件的內容將寫指令從前到后執行一次以完成數據的恢復工作。
-
操作
- 觸發AOF快照。根據配置文件觸發,可以是每次執行觸發,可以是每秒觸發,可以不同步。
- 根據AOF文件恢復數據。正常情況下,將appendonly.aof 文件拷貝到redis的安裝目錄的bin目錄下,重啟redis服務即可。但在實際開發中,可能因為某些原因導致appendonly.aof 文件格式異常,從而導致數據還原失敗,可以通過命令redis-check-aof --fix appendonly.aof 進行修復 。
-
過程
- 客戶端在進行業務操作時,發送命令請求到服務器,服務器會同時把網絡協議格式的命令內容寫到AOF文件中
-
配置
-
redis 默認關閉,開啟需要手動把no改為yes:appendonly yes
-
指定本地數據庫文件名,默認值為 appendonly.aof:appendfilename "appendonly.aof"
-
指定更新日志條件:
- always:同步持久化,每次發生數據變化會立刻寫入到磁盤中。性能較差當數據完整性比較好(慢,安全)
- everysec:出廠默認推薦,每秒異步記錄一次(默認值)
- no:不同步
# appendfsync always appendfsync everysec # appendfsync no
-
配置重寫觸發機制:
- 當AOF文件大小是上次rewrite后大小的一倍且文件大於64M時觸發。一般都設置為3G,64M太小了。
auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb
-
-
優點
- 備份機制更穩健,丟失數據概率更低
- 可讀的日志文本,通過操作AOF穩健,可以處理誤操作
-
缺點
- 比起RDB占用更多的磁盤空間
- 恢復備份速度要慢(重新執行一遍)
- 每次讀寫都同步的話,有一定的性能壓力
- 存在個別Bug,造成不能恢復
-
- RDB
- 分布式鎖
- Redis緩存刪除策略 & redis內存不足,如何解決
- 被動刪除:當讀/寫一個已經過期的key時,會觸發惰性刪除策略,直接刪除掉這個過期key
- 注:被動刪除:只有key被操作時(如GET),REDIS才會被動檢查該key是否過期,如果過期則刪除之並且返回NIL。
- 主動刪除:由於惰性刪除策略無法保證冷數據被及時刪掉,所以Redis會定期主動淘汰一批已過期的key
- 過期的
- Redis會周期性的隨機測試一批設置了過期時間的key並進行處理。測試到的已過期的key將被刪除。
- 如果沒有設置時間,那緩存就是永不過期
- 如果設置了過期時間,之后又想讓緩存永不過期,使用persist key
- redis過期怎么實現
- Redis過期策略---實現原理
- 三種過期策略
- 定時刪除:在設置key的過期時間的同時,為該key創建一個定時器,讓定時器在key的過期時間來臨時,對key進行刪除
- 懶漢式刪除:key過期的時候不刪除,每次通過key獲取值的時候去檢查是否過期,若過期,則刪除,返回null(用的時候再檢查刪除)。
- 定期刪除:每隔一段時間執行一次刪除過期key操作
- 內存滿了時
- Redis是基於內存的key-value數據庫,因為系統的內存大小有限,所以我們在使用Redis的時候可以配置Redis能使用的最大的內存大小。
- 通過在Redis安裝目錄下面的redis.conf配置文件中添加以下配置設置內存大小:maxmemory 100mb
- Redis支持運行時通過命令動態修改內存大小:config set maxmemory 100mb
- Redis的內存淘汰(當前已用內存超過maxmemory限定時,觸發主動清理策略)
- Redis內存滿了的解決辦法
- 既然可以設置Redis最大占用內存大小,那么配置的內存就有用完的時候。那在內存用完的時候,還繼續往Redis里面添加數據不就沒內存可用了嗎?
- LRU:表示最近最少使用; LFU:表示最不常用的
- 實際上Redis定義了幾種策略用來處理這種情況:
- noeviction(默認策略):對於寫請求不再提供服務,直接返回錯誤(DEL請求和部分特殊請求除外)
- volatile-lru:從設置了過期時間的key中使用LRU算法進行淘汰
- allkeys-lru:從所有key中使用LRU算法進行淘汰
- volatile-lfu 篩選出設置了有效期的,最不常用的 key;
- allkeys-lfu 所有 key 中,篩選出最不常用的 key ;
- allkeys-random:從所有key中隨機淘汰數據
- volatile-random:從設置了過期時間的key中隨機淘汰
- volatile-ttl:在設置了過期時間的key中,根據key的過期時間進行淘汰,越早過期的越優先被淘汰
- 當使用volatile-lru、volatile-random、volatile-ttl這三種策略時,如果沒有key可以被淘汰,則和noeviction一樣返回錯誤。
- 通過配置文件設置淘汰策略(修改redis.conf文件):maxmemory-policy allkeys-lru
- 通過命令修改淘汰策略:config set maxmemory-policy allkeys-lru
- Redis使用的是近似LRU算法,它跟常規的LRU算法還不太一樣。近似LRU算法通過隨機采樣法淘汰數據,每次隨機出5(默認)個key,從里面淘汰掉最近最少使用的key。可以通過maxmemory-samples參數修改采樣數量:例:maxmemory-samples 10
- Redis3.0對近似LRU算法進行了一些優化。新算法會維護一個候選池(大小為16),池中的數據根據訪問時間進行排序,第一次隨機選取的key都會放入池中,隨后每次隨機選取的key只有在訪問時間小於池中最小的時間才會放入池中,直到候選池被放滿。當放滿后,如果有新的key需要放入,則將池中最后訪問時間最大(最近被訪問)的移除。
- LFU算法是Redis4.0里面新加的一種淘汰策略。它的全稱是Least Frequently Used,它的核心思想是根據key的最近被訪問的頻率進行淘汰,很少被訪問的優先被淘汰,被訪問的多的則被留下來。
- Redis是基於內存的key-value數據庫,因為系統的內存大小有限,所以我們在使用Redis的時候可以配置Redis能使用的最大的內存大小。
- 過期的
- 被動刪除:當讀/寫一個已經過期的key時,會觸發惰性刪除策略,直接刪除掉這個過期key
- Redis的事務以及實現方式
- Redis的事務中啟用的是樂觀鎖,只負責監測key沒有被改動.如果沒變正常執行,如果有變事務取消
- Redis高可用方案
-
主從復制(Replication-Sentinel模式)、讀寫分離、主從架構、redis replication?
-
如果采用了主從架構,那么建議必須開啟master node的持久化
-
主節點(master)負責讀寫,從節點(slave)負責讀
-
Sentinel(哨兵)+主從復制
- 簡單的主從集群有個問題,就是主節點掛了之后,無法從新選舉新的節點作為主節點進行寫操作,導致服務不可用。所以接下來介紹Sentinel(哨兵)功能的使用。哨兵是一個獨立的進程,哨兵會實時監控master節點的狀態,當master不可用時會從slave節點中選出一個作為新的master,並修改其他節點的配置指向到新的master。
- 當主節點出現故障時,由Redis Sentinel自動完成故障發現和轉移,並通知應用方,實現高可用性
- 可以管理多個Redis服務器,它提供了監控,提醒以及自動的故障轉移的功能
- 所以經常和主從復制結合起來用
- 程序中也要進行配置,如:
spring: redis: sentinel: master: mymaster nodes: 192.168.1.167:26379,192.168.1.167:26380,192.168.1.167:26381 host: 192.168.1.164 port: 7003 database: 0 password: <password>
-
-
Redis集群(Redis-Cluster模式)
- Redis 集群是一個提供在多個Redis節點間共享數據的程序集。
- 為了使在部分節點失敗或者大部分節點無法通信的情況下集群仍然可用,所以集群使用了主從復制模型,每個節點都會有1-n個從節點。例如master-A節點不可用了,集群便會選舉slave-A節點作為新的主節點繼續服務。
- 優勢
- 自動分割數據到不同的節點上。
- 整個集群的部分節點失敗或者不可達的情況下能夠繼續處理命令。
-
對比
- 主從復制是為了數據備份,哨兵是為了高可用,Redis主服務器掛了哨兵可以切換,集群則是因為單實例能力有限,搞多個分散壓力,簡短總結如下:
- 主從模式:備份數據、負載均衡,一個Master可以有多個Slaves。
- sentinel發現master掛了后,就會從slave中重新選舉一個master。
- cluster是為了解決單機Redis容量有限的問題,將數據按一定的規則分配到多台機器。
- sentinel着眼於高可用,Cluster提高並發量。
- 1.主從模式:讀寫分離,備份,一個Master可以有多個Slaves。
- 2.哨兵sentinel:監控,自動轉移,哨兵發現主服務器掛了后,就會從slave中重新選舉一個主服務器。
- 3.集群:為了解決單機Redis容量有限的問題,將數據按一定的規則分配到多台機器,內存/QPS不受限於單機,可受益於分布式集群高擴展性。
-
redis達到十萬的並發
- redis高可用,保證高並發
- QPS
- Redis主要消耗的服務器的內存資源,當Redis內存需求超過機器的最大內存,一台機器就夠不用了;另外,當我們用戶超過一定數量時,單機的redis讀寫能力就達到瓶頸;最后我們還要考慮單節點故障的問題,如果單機Redis出現問題我們的系統整體將崩潰。因此,需要分片儲存,保障我們系統的高可用。
- 用redis做限流
- 使用Redis進行簡單的限流
- 用zset的zcard操作來查看指定時間窗口內的行為數量
- RabbitMQ
- 高並發環境下,來不及同步處理用戶發送的請求(insert、update之類的請求),那么通過分析,將可以拆成異步的操作的數據,放到mq中,然后通過輪訓任務異步處理
- 只要發送消息隊列,就算業務成功
- 充分利用消費能力,削峰
- 避免直接同步的接口調用
- 異步
- 並行
- 排隊
- 弊端
- 消息的不確定性
- 通過延遲隊列、輪詢技術來解決
- 消息的不確定性
- 集群
- RabbitMQ這款消息隊列中間件產品本身是基於Erlang編寫,Erlang語言天生具備分布式特性(通過同步Erlang集群各節點的magic cookie來實現)。因此,RabbitMQ天然支持Clustering。這使得RabbitMQ本身不需要像ActiveMQ、Kafka那樣通過ZooKeeper分別來實現HA方案和保存集群的元數據。集群是保證可靠性的一種方式,同時可以通過水平擴展以達到增加消息吞吐量能力的目的。
- RabbitMQ 的4種集群架構
- RabbitMQ集群搭建
- 4種集群架構
- 主備模式(普通、默認集群配置)
- 也稱為 Warren (兔子窩) 模式。實現 rabbitMQ 的高可用集群,一般在並發和數據量不高的情況下,這種模式非常的好用且簡單。
- 也就是一個主/備方案,主節點提供讀寫,備用節點不提供讀寫。如果主節點掛了,就切換到備用節點,原來的備用節點升級為主節點提供讀寫服務,當原來的主節點恢復運行后,原來的主節點就變成備用節點,和 activeMQ 利用 zookeeper 做主/備一樣,也可以一主多備。
- 使用HaProxy
- 遠程模式
- 遠程模式可以實現雙活的一種模式,簡稱 shovel 模式,所謂的 shovel 就是把消息進行不同數據中心的復制工作,可以跨地域的讓兩個 MQ 集群互聯,遠距離通信和復制。
- Shovel 就是我們可以把消息進行數據中心的復制工作,我們可以跨地域的讓兩個 MQ 集群互聯。
- 鏡像模式
- 非常經典的 mirror 鏡像模式,保證 100% 數據不丟失。在實際工作中也是用得最多的,並且實現非常的簡單,一般互聯網大廠都會構建這種鏡像集群模式。
- mirror 鏡像隊列,目的是為了保證 rabbitMQ 數據的高可靠性解決方案,主要就是實現數據的同步,一般來講是 2 - 3 個節點實現數據同步。對於 100% 數據可靠性解決方案,一般是采用 3 個節點。
- RabbitMQ默認集群模式,但並不保證隊列的高可用性,盡管交換機、綁定這些可以復制到集群里的任何一個節點,但是隊列內容不會復制。雖然該模式解決一項目組節點壓力,但隊列節點宕機直接導致該隊列無法應用,只能等待重啟,所以要想在隊列節點宕機或故障也能正常應用,就要復制隊列內容到集群里的每個節點,必須要創建鏡像隊列。
- 鏡像隊列是基於普通的集群模式的,然后再添加一些策略,所以你還是得先配置普通集群,然后才能設置鏡像隊列,我們就以上面的集群接着做。
- 多活模式
- 也是實現異地數據復制的主流模式,因為 shovel 模式配置比較復雜,所以一般來說,實現異地集群的都是采用這種雙活 或者 多活模型來實現的。這種模式需要依賴 rabbitMQ 的 federation 插件,可以實現持續的,可靠的 AMQP 數據通信,多活模式在實際配置與應用非常的簡單。
- rabbitMQ 部署架構采用雙中心模式(多中心),那么在兩套(或多套)數據中心各部署一套 rabbitMQ 集群,各中心的rabbitMQ 服務除了需要為業務提供正常的消息服務外,中心之間還需要實現部分隊列消息共享。
- 主備模式(普通、默認集群配置)
- 持久化
- 如果我們希望即使在RabbitMQ服務重啟的情況下,也不會丟失消息,我們可以將Queue與Message都設置為可持久化的(durable),這樣可以保證絕大部分情況下我們的RabbitMQ消息不會丟失。當然還是會有一些小概率事件會導致消息丟失。
- 隊列持久化需要在聲明隊列時添加參數 durable=True,這樣在rabbitmq崩潰時也能保存隊列
- 僅僅使用durable=True ,只能持久化隊列,不能持久化消息
- 消息持久化需要在消息生成時,添加參數 properties=pika.BasicProperties(delivery_mode=2)
- 多個生產者,多個消費者 並發控制queue
- Kafka
- MongoDB
- ELK(Elasticsearch、Logstash、Kibana)
- Redis
- 框架
- Spring Boot
- Security
- JPA
- Spring Cloud
- Zuul:網關組件,提供智能路由、訪問過濾等功能
- Ribbon:客戶端負載均衡的服務調用組件
- Spring Boot
- 計算機網絡
- TCP的擁塞窗口、滑動窗口。
- 滑動窗口和擁塞窗口
- 滑動窗口和流量控制和重發機制
- 所謂流量控制,主要是接收方傳遞信息給發送方,使其不要發送數據太快,是一種端到端的控制。主要的方式就是返回的ACK中會包含自己的接收窗口的大小,並且利用大小來控制發送方的數據發送
- 流控只簡單地表明了接收方的處理能力,並不能代表中間網絡的處理能力
- 發送方和接收方都有滑動窗口。
- 擁塞窗口和擁塞控制
- 擁塞:路由器因無法處理高速到達的流量而被迫丟棄數據信息的現象稱為擁塞。
- 如果一開始把流控窗口內的數據全部發送出去,中間路由器可能一時處理不了如此多的突發流量
- 擁塞控制算法
- 慢啟動
- 擁塞避免
- 超時重傳
- 快速重發
- 快速恢復
- UDP / TCP 協議
- 一個 10M 大小的 buffer 里存滿了數據,現在要把這個 buffer 里的數據盡量發出去,可以允許部分丟包,問是用TCP好還是UDP好?為什么?
- TCP 是面向連接的,UDP 是面向無連接的
- 關鍵是因為TCP有序號!
- UDP程序結構較簡單
- TCP 是面向字節流的,UDP 是基於數據報的
- TCP 保證數據正確性,UDP 可能丟包
- TCP 保證數據順序,UDP 不保證
- UDP不像TCP那樣關心順序問題、丟包問題、連接維護、流量控制、擁塞控制
- 三次握手
- 為了在發送數據之前,確認雙方都可以正常收發數據並都准備好了
- 過程
- 第一次握手:建立連接時,客戶端發送syn包(syn=x)到服務器,並進入SYN_SENT狀態,等待服務器確認;SYN:同步序列編號(Synchronize Sequence Numbers)。
- 第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=x+1),同時自己也發送一個SYN包(syn=y),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
- 第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=y+1),此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP連接成功)狀態,完成三次握手。
- 第一次讓B明白,A的發送沒問題,B的接收沒問題。
- 第二次讓A明白,A的發送沒問題,A的接收沒問題,B的發送沒問題,B的接收沒問題。
- 第三次讓B明白,A的接收沒問題,B的發送沒問題。
- 而這也就是雙方各個狀態名字的由來,第一次后,A是SYNC-SENT,B是SYNC-RCVD;第二次后,A明白雙方都可以了,所以是ESTAB-LISHED;第三次后,B才明白雙方都可以了,所以才是ESTAB-LISHED
- 為什么不能用兩次握手進行連接?
- 3次握手完成兩個重要的功能,既要雙方做好發送數據的准備工作(雙方都知道彼此已准備好),也要允許雙方就初始序列號進行協商,這個序列號在握手過程中被發送和確認。
- 現在把三次握手改成僅需要兩次握手,死鎖是可能發生的。作為例子,考慮計算機S和C之間的通信,假定C給S發送一個連接請求分組,S收到了這個分組,並發 送了確認應答分組。按照兩次握手的協定,S認為連接已經成功地建立了,可以開始發送數據分組。可是,C在S的應答分組在傳輸中被丟失的情況下,將不知道S 是否已准備好,不知道S建立什么樣的序列號,C甚至懷疑S是否收到自己的連接請求分組。在這種情況下,C認為連接還未建立成功,將忽略S發來的任何數據分 組,只等待連接確認應答分組。而S在發出的分組超時后,重復發送同樣的分組。這樣就形成了死鎖。
- 如果已經建立了連接,但是客戶端突然出現故障了怎么辦?
- TCP還設有一個保活計時器,顯然,客戶端如果出現故障,服務器不能一直等下去,白白浪費資源。服務器每收到一次客戶端的請求后都會重新復位這個計時器,時間通常是設置為2小時,若兩小時還沒有收到客戶端的任何數據,服務器就會發送一個探測報文段,以后每隔75秒鍾發送一次。若一連發送10個探測報文仍然沒反應,服務器就認為客戶端出了故障,接着就關閉連接。
- 四次揮手
- 為什么連接的時候是三次握手,關閉的時候卻是四次握手?
- 因為當Server端收到Client端的SYN連接請求報文后,可以直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連接時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端所有的報文都發送完了,我才能發送FIN報文,因此不能一起發送。故需要四步握手。
- 為什么TIME_WAIT狀態需要經過2MSL(最大報文段生存時間)才能返回到CLOSE狀態?
- 雖然按道理,四個報文都發送完畢,我們可以直接進入CLOSE狀態了,但是我們必須假象網絡是不可靠的,有可以最后一個ACK丟失。所以TIME_WAIT狀態就是用來重發可能丟失的ACK報文。在Client發送出最后的ACK回復,但該ACK可能丟失。Server如果沒有收到ACK,將不斷重復發送FIN片段。所以Client不能立即關閉,它必須確認Server接收到了該ACK。Client會在發送出ACK之后進入到TIME_WAIT狀態。Client會設置一個計時器,等待2MSL的時間。如果在該時間內再次收到FIN,那么Client會重發ACK並再次等待2MSL。所謂的2MSL是兩倍的MSL(Maximum Segment Lifetime)。MSL指一個片段在網絡中最大的存活時間,2MSL就是一個發送和一個回復所需的最大時間。如果直到2MSL,Client都沒有再次收到FIN,那么Client推斷ACK已經被成功接收,則結束TCP連接。
- 為什么連接的時候是三次握手,關閉的時候卻是四次握手?
- Socket
- socket通訊原理及例程(一看就懂)
- Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。
- 其實還是基於TCP、UDP協議的,實現了三次握手、四次揮手等過程
- 套接字(socket)是一個抽象層,應用程序可以通過它發送或接收數據,可對其進行像對文件一樣的打開、讀寫和關閉等操作。
- 在計算機通信領域,socket 被翻譯為“套接字”,它是計算機之間進行通信的一種約定或一種方式。通過 socket 這種約定,一台計算機可以接收其他計算機的數據,也可以向其他計算機發送數據
- socket起源於Unix,而Unix/Linux基本哲學之一就是“一切皆文件”,都可以用“打開open –> 讀寫write/read –> 關閉close”模式來操作。我的理解就是Socket就是該模式的一個實現:即socket是一種特殊的文件,一些socket函數就是對其進行的操作(讀/寫IO、打開、關閉)。
- 過程
- 服務器端先初始化Socket,然后與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端連接。在這時如果有個客戶端初始化一個Socket,然后連接服務器(connect),如果連接成功,這時客戶端與服務器端的連接就建立了。客戶端發送數據請求,服務器端接收請求並處理請求,然后把回應數據發送給客戶端,客戶端讀取數據,最后關閉連接,一次交互結束。
- TCP的擁塞窗口、滑動窗口。
- Http
- Http重定向的具體流程
- 301 redirect: 301 代表永久性轉移(Permanently Moved)
- 302 redirect: 302 代表暫時性轉移(Temporarily Moved )
- 多用於登陸失效,要跳轉到登陸頁面
- 比如項目中用302+location來判斷是不是需要重新登陸了(對應的第二次200請求是瀏覽器自動發起的,js/angular捕獲不到)
- 盡量使用301跳轉!301和302狀態碼都表示重定向,就是說瀏覽器在拿到服務器返回的這個狀態碼后會自動跳轉到一個新的URL地址,這個地址可以從響應的Location首部中獲取(用戶看到的效果就是他輸入的地址A瞬間變成了另一個地址B)——這是它們的共同點。他們的不同在於。301表示舊地址A的資源已經被永久地移除了(這個資源不可訪問了),搜索引擎在抓取新內容的同時也將舊的網址交換為重定向之后的網址;302表示舊地址A的資源還在(仍然可以訪問),這個重定向只是臨時地從舊地址A跳轉到地址B,搜索引擎會抓取新的內容而保存舊的網址。
- 轉發是服務器行為,重定向是客戶端行為。
- 轉發過程
- 客戶瀏覽器發送http請求——》web服務器接受此請求——》調用內部的一個方法在容器內部完成請求處理和轉發動作——》將目標資源發送給客戶;
- 重定向過程
- 重定向,其實是兩次request
- 客戶瀏覽器發送http請求——》web服務器接受后發送302狀態碼響應及對應新的location給客戶瀏覽器——》客戶瀏覽器發現是302響應,則自動再發送一個新的http請求(瀏覽器的自動行為),請求url是新的location地址——》服務器根據此請求尋找資源並發送給客戶。
- 轉發過程
- 一個完整的 HTTP 請求會涉及到哪些協議?
- 域名解析
- 瀏覽器搜索自己的DNS緩存,緩存中維護一張域名與IP地址的對應表;
- 若沒有,則搜索操作系統的DNS緩存;
- 若沒有,則操作系統將域名發送至本地域名服務器(遞歸查詢方式),本地域名服務器查詢自己的DNS緩存,查找成功則返回結果,否則,通過以下方式迭代查找:
- 本地域名服務器向根域名服務器發起請求,根域名服務器返回com域的頂級域名服務器的地址;
- 本地域名服務器向com域的頂級域名服務器發起請求,返回權限域名服務器地址;
- 本地域名服務器向權限域名服務器發起請求,得到IP地址;
- 本地域名服務器將得到的IP地址返回給操作系統,同時自己將IP地址緩存起來;
- 操作系統將IP地址返回給瀏覽器,同時自己也將IP地址緩存起來;
- 至此,瀏覽器已經得到了域名對應的IP地址。
- 發起http請求
- 傳輸層,選擇傳輸協議,TCP或者UDP,TCP是可靠的傳輸控制協議,對HTTP請求進行封裝,加入了端口號等信息。發起TCP的3次握手(底層在局域網還有arp等)建立TCP連接。
- 網絡層,通過IP協議將IP地址封裝為IP數據報;然后此時會用到ARP協議,主機發送信息時將包含目標IP地址的ARP請求廣播到網絡上的所有主機,並接收返回消息,以此確定目標的物理地址,找到目的MAC地址;
- 數據鏈路層,把網絡層交下來的IP數據報添加首部和尾部,封裝為MAC幀,現在根據目的mac開始建立TCP連接,三次握手,接收端在收到物理層上交的比特流后,根據首尾的標記,識別幀的開始和結束,將中間的數據部分上交給網絡層,然后層層向上傳遞到應用層;
- 服務器端響應http請求,瀏覽器得到html代碼
- 瀏覽器解析html代碼,並請求html代碼中的資源
- 瀏覽器對頁面進行渲染呈現給用戶。
- 域名解析
- http的返回碼/狀態碼
- 200 OK
- 201 Created
- 302 Redirect (Temporarily Moved)
- 304 Not Modified
- 401 Forbidden
- 404 Not Found
- 500 Internal Server Error
- 502 Bad Gateway
- 504
- http的header
- HTTP Header消息頭詳解
- Accept
- Accept-Language
- Accept-Encoding
- Authorization
- User-Agent
- Host
- Referer
- Connection
- Date
- If-Match
- 僅當客戶端提供的實體與服務器上對應的實體相匹配時,才進行對應的操作。主要用於像 PUT 這樣的方法中,僅當從用戶上次更新某個資源后,該資源未被修改的情況下,才更新該資源。
- If-Modified_Since
- 允許在對應的資源未被修改的情況下返回304未修改
- Cache-Control
- Cookie
- Content
- Content-Type
- Content-Length
- Allow
- Age
- http請求方法, 哪些請求方法是冪等的
- OPTIONS
- 返回服務器針對特定資源所支持的HTTP請求方法。也可以利用向Web服務器發送'*'的請求來測試服務器的功能性。
- HEAD
- 向服務器索要與GET請求相一致的響應,只不過響應體將不會被返回。這一方法可以在不必傳輸整個響應內容的情況下,就可以獲取包含在響應消息頭中的元信息。
- 這種方法可以用來獲取請求中隱含的元信息,而不用傳輸實體本身。也經常用來測試超鏈接的有效性、可用性和最近的修改。
- POST
- 向指定資源提交數據進行處理請求(例如提交表單或者上傳文件)。數據被包含在請求體中。POST請求可能會導致新的資源的建立和/或已有資源的修改。
- PUT
- 向指定資源位置上傳其最新內容。
- DELETE
- DELETE
- TRACE
- 回顯服務器收到的請求,主要用於測試或診斷。
- CONNECT
- HTTP/1.1協議中預留給能夠將連接改為管道方式的代理服務器。
- 冪等性
- Get具有冪等性,沒有副作用;
- POST不具有冪等性,有副作用;雖然說是用於創建資源,但PUT也可以,只不過。
- POST所對應的URI並非創建的資源本身,而是資源的接收者。比如:POST http://www.forum.com/articles的語義是在http://www.forum.com/articles下創建一篇帖子,HTTP響應中應包含帖子的創建狀態以及帖子的URI。兩次相同的POST請求會在服務器端創建兩份資源,它們具有不同的URI
- PUT具有冪等性,有副作用;雖然說是用於更新資源,但PUT也可以創建資源。
- UT所對應的URI是要創建或更新的資源本身。比如:PUT http://www.forum/articles/4231的語義是創建或更新ID為4231的帖子。對同一URI進行多次PUT的副作用和一次PUT是相同的
- Delete也應該具有冪等性,有副作用,
- OPTIONS
- https加密原理
- 為什么http證書不會被復制下來?瀏覽器里就能看到啊
- 因為瀏覽器會檢查域名是否和證書上的一致
- 同時證書中除了明文部分,還有加密了的摘要,可以用CA公鑰去解密,然后驗證摘要
- 過程
- https基於tls,然后同時使用了對稱和非對稱加密,引入了CA
- CA在判明申請者的身份之后,會對已申請的公開密鑰做數字簽名,然后將這個簽名的公開密鑰和放入公鑰證書分配給服務器公司
- 服務器會把這個由CA頒發的公鑰證書以及公開密鑰發送給客戶端,以此來和客戶端進行通信
- 接收到證書的客戶端可以使用CA的公開密鑰對證書上的簽名進行認證,一旦認證通過,客戶端就可以知道認證服務器公開密鑰的是真實有效的CA,並且服務器的公開密鑰是值得信賴的
- CA的公開密鑰已經事先植入到瀏覽器中,客戶端通過CA的公開密鑰向CA認證服務器的公鑰證書上的數字簽名的真實性。
- Https與http區別
- 協議基礎不同:HTTPS在HTTP的基礎上加入了SSL層
- 通訊方式不同:HTTPS在數據通訊之前需要客戶端、服務器進行握手(身份驗證),建立連接后,傳輸數據需要經過加密,通信端口為443
- HTTPS是怎么解決HTTP協議的三大缺點的?
- 防監聽:采用對稱加密對數據進行加密,采用非對稱加密對對稱加密的密鑰進行加密
- 防偽裝:通信雙方攜帶證書,證書有第三方頒發,很難偽造
- 防篡改:采用摘要算法(MD5或是SHA-1),同樣的數據由同樣的摘要,而只要有一點不同的數據,它的摘要往往不同,只要數據做了篡改,就會被感知到
- 為什么http證書不會被復制下來?瀏覽器里就能看到啊
- tls的加密流程
- 密鑰交換過程 (也可以稱握手過程, 但握手這個詞太泛太抽象)
- TLS的安全性主要是由這個階段決定的. 通過非對稱加密或其它的密鑰交換手段, 如DH, ECDH , 來交換對稱加密密鑰.
- 這些算法都非常耗CPU, 肯定不能全程用這些算法來加密所有的數據.
- 數據加密過程
- 雙方拿到密鑰之后, 使用對稱加密算法進行加解密, 因為對稱加密運算快.
- TLS加密協議完整流程
- 密鑰交換過程 (也可以稱握手過程, 但握手這個詞太泛太抽象)
- Http重定向的具體流程
- 安全
- sql注入
- 參數檢查(類型、長度、合法字符等)
- 使用參數化查詢、存儲過程等
- xss
- 前端安全系列(一):如何防止XSS攻擊?
- XSS 的本質是:惡意代碼未經過濾,與網站正常的代碼混在一起;瀏覽器無法分辨哪些腳本是可信的,導致惡意腳本被執行。
- 用戶故意提交到數據庫的數據含有腳本,在顯示到頁面上時運行出現問題(可能實在管理員查看時,不一定就是這個客戶)
- 這個腳本有可能惡意上傳別人的cookie信息(雖然可以使用httponly領js無法操作,但請求中會帶上)或其他資料
- 存儲型 XSS
- 反射型 XSS
- DOM 型 XSS
- 職責划分
- 防范存儲型和反射型 XSS 是后端 RD 的責任。而 DOM 型 XSS 攻擊不發生在后端,是前端 RD 的責任。防范 XSS 是需要后端 RD 和前端 RD 共同參與的系統工程。
- 轉義應該在輸出 HTML 時進行,而不是在提交用戶輸入時。
- 措施
- 輸入檢查,過濾特殊字符(有點難辦,因為不確定什么時候要轉義,有時一些字符就是正常的)
- 預防存儲型和反射型 XSS 攻擊
- 改成純前端渲染,把代碼和數據分隔開。(即不要在后端使用jsp等方式拼接HTML)
- 過程
- 瀏覽器先加載一個靜態 HTML,此 HTML 中不包含任何跟業務相關的數據。
- 然后瀏覽器執行 HTML 中的 JavaScript。
- JavaScript 通過 Ajax 加載業務數據,調用 DOM API 更新到頁面上。
- 在純前端渲染中,我們會明確的告訴瀏覽器:下面要設置的內容是文本(.innerText),還是屬性(.setAttribute),還是樣式(.style)等等。瀏覽器不會被輕易的被欺騙,執行預期外的代碼了。
- 但純前端渲染還需注意避免 DOM 型 XSS 漏洞(例如 onload 事件和 href 中的 javascript:xxx 等,請參考下文”預防 DOM 型 XSS 攻擊“部分)。
- 在很多內部、管理系統中,采用純前端渲染是非常合適的。但對於性能要求高,或有 SEO 需求的頁面,我們仍然要面對拼接 HTML 的問題。
- 過程
- 對 HTML 做充分轉義。
- HTML 的編碼是十分復雜的,在不同的上下文里要使用相應的轉義規則。
- 不同的上下文,如 HTML 屬性、HTML 文字內容、HTML 注釋、跳轉鏈接、內聯 JavaScript 字符串、內聯 CSS 樣式表等,所需要的轉義規則不一致。
- 業務 RD 需要選取合適的轉義庫,並針對不同的上下文調用不同的轉義規則。
- 改成純前端渲染,把代碼和數據分隔開。(即不要在后端使用jsp等方式拼接HTML)
- 預防 DOM 型 XSS 攻擊
- DOM 型 XSS 攻擊,實際上就是網站前端 JavaScript 代碼本身不夠嚴謹,把不可信的數據當作代碼執行了。
- 在使用 .innerHTML、.outerHTML、document.write() 時要特別小心,不要把不可信的數據作為 HTML 插到頁面上,而應盡量使用 .textContent、.setAttribute() 等。
- 如果用 Vue/React 技術棧,並且不使用 v-html/dangerouslySetInnerHTML 功能,就在前端 render 階段避免 innerHTML、outerHTML 的 XSS 隱患。
- DOM 中的內聯事件監聽器,如 location、onclick、onerror、onload、onmouseover 等,<a> 標簽的 href 屬性,JavaScript 的 eval()、setTimeout()、setInterval() 等,都能把字符串作為代碼運行。如果不可信的數據拼接到字符串中傳遞給這些 API,很容易產生安全隱患,請務必避免。
- Content Security Policy
- 禁止加載外域代碼,防止復雜的攻擊邏輯。
- 禁止外域提交,網站被攻擊后,用戶的數據不會泄露到外域。
- 禁止內聯腳本執行(規則較嚴格,目前發現 GitHub 使用)。
- 禁止未授權的腳本執行(新特性,Google Map 移動版在使用)。
- 合理使用上報可以及時發現 XSS,利於盡快修復問題。
- 輸入內容長度控制
- HTTP-only Cookie:
- 禁止 JavaScript 讀取某些敏感 Cookie,攻擊者完成 XSS 注入后也無法竊取此 Cookie。
- 驗證碼
- 防止腳本冒充用戶提交危險操作。
- csrf
- 攻擊者盜用了你的身份,以你的名義發送惡意請求,對服務器來說這個請求是完全合法的,但是卻完成了攻擊者所期望的一個操作,比如以你的名義發送郵件、發消息,盜取你的賬號,添加系統管理員,甚至於購買商品、虛擬貨幣轉賬等。
- 措施
- 驗證 HTTP Referer 字段
- 這種方法的顯而易見的好處就是簡單易行,網站的普通開發人員不需要操心 CSRF 的漏洞,只需要在最后給所有安全敏感的請求統一增加一個攔截器來檢查 Referer 的值就可以。特別是對於當前現有的系統,不需要改變當前系統的任何已有代碼和邏輯,沒有風險,非常便捷。
- 然而,這種方法並非萬無一失。Referer 的值是由瀏覽器提供的,雖然 HTTP 協議上有明確的要求,但是每個瀏覽器對於 Referer 的具體實現可能有差別,並不能保證瀏覽器自身沒有安全漏洞。使用驗證 Referer 值的方法,就是把安全性都依賴於第三方(即瀏覽器)來保障,從理論上來講,這樣並不安全。事實上,對於某些瀏覽器,比如 IE6 或 FF2,目前已經有一些方法可以篡改 Referer 值。如果 bank.example 網站支持 IE6 瀏覽器,黑客完全可以把用戶瀏覽器的 Referer 值設為以 bank.example 域名開頭的地址,這樣就可以通過驗證,從而進行 CSRF 攻擊。
- 即便是使用最新的瀏覽器,黑客無法篡改 Referer 值,這種方法仍然有問題。因為 Referer 值會記錄下用戶的訪問來源,有些用戶認為這樣會侵犯到他們自己的隱私權,特別是有些組織擔心 Referer 值會把組織內網中的某些信息泄露到外網中。因此,用戶自己可以設置瀏覽器使其在發送請求時不再提供 Referer。當他們正常訪問銀行網站時,網站會因為請求沒有 Referer 值而認為是 CSRF 攻擊,拒絕合法用戶的訪問。
- 在請求地址中添加 token 並驗證
- CSRF 攻擊之所以能夠成功,是因為黑客可以完全偽造用戶的請求,該請求中所有的用戶驗證信息都是存在於 cookie 中,因此黑客可以在不知道這些驗證信息的情況下直接利用用戶自己的 cookie 來通過安全驗證。要抵御 CSRF,關鍵在於在請求中放入黑客所不能偽造的信息,並且該信息不存在於 cookie 之中。可以在 HTTP 請求中以參數的形式加入一個隨機產生的 token,並在服務器端建立一個攔截器來驗證這個 token,如果請求中沒有 token 或者 token 內容不正確,則認為可能是 CSRF 攻擊而拒絕該請求。
- 這種方法要比檢查 Referer 要安全一些,token 可以在用戶登陸后產生並放於 session 之中,然后在每次請求時把 token 從 session 中拿出,與請求中的 token 進行比對,但這種方法的難點在於如何把 token 以參數的形式加入請求。對於 GET 請求,token 將附在請求地址之后,這樣 URL 就變成 http://url?csrftoken=tokenvalue。 而對於 POST 請求來說,要在 form 的最后加上 ,這樣就把 token 以參數的形式加入請求了。但是,在一個網站中,可以接受請求的地方非常多,要對於每一個請求都加上 token 是很麻煩的,並且很容易漏掉,通常使用的方法就是在每次頁面加載時,使用 javascript 遍歷整個 dom 樹,對於 dom 中所有的 a 和 form 標簽后加入 token。這樣可以解決大部分的請求,但是對於在頁面加載之后動態生成的 html 代碼,這種方法就沒有作用,還需要程序員在編碼時手動添加 token。
- 該方法還有一個缺點是難以保證 token 本身的安全。特別是在一些論壇之類支持用戶自己發表內容的網站,黑客可以在上面發布自己個人網站的地址。由於系統也會在這個地址后面加上 token,黑客可以在自己的網站上得到這個 token,並馬上就可以發動 CSRF 攻擊。為了避免這一點,系統可以在添加 token 的時候增加一個判斷,如果這個鏈接是鏈到自己本站的,就在后面添加 token,如果是通向外網則不加。不過,即使這個 csrftoken 不以參數的形式附加在請求之中,黑客的網站也同樣可以通過 Referer 來得到這個 token 值以發動 CSRF 攻擊。這也是一些用戶喜歡手動關閉瀏覽器 Referer 功能的原因。
- 在 HTTP 頭中自定義屬性並驗證
- 這種方法也是使用 token 並進行驗證,和上一種方法不同的是,這里並不是把 token 以參數的形式置於 HTTP 請求之中,而是把它放到 HTTP 頭中自定義的屬性里。通過 XMLHttpRequest 這個類,可以一次性給所有該類請求加上 csrftoken 這個 HTTP 頭屬性,並把 token 值放入其中。這樣解決了上種方法在請求中加入 token 的不便,同時,通過 XMLHttpRequest 請求的地址不會被記錄到瀏覽器的地址欄,也不用擔心 token 會透過 Referer 泄露到其他網站中去。
- 然而這種方法的局限性非常大。XMLHttpRequest 請求通常用於 Ajax 方法中對於頁面局部的異步刷新,並非所有的請求都適合用這個類來發起,而且通過該類請求得到的頁面不能被瀏覽器所記錄下,從而進行前進,后退,刷新,收藏等操作,給用戶帶來不便。另外,對於沒有進行 CSRF 防護的遺留系統來說,要采用這種方法來進行防護,要把所有請求都改為 XMLHttpRequest 請求,這樣幾乎是要重寫整個網站,這代價無疑是不能接受的。
- 驗證 HTTP Referer 字段
- 對稱性加密跟非對稱性加密的比較,使用場景
- 對稱加密效率高,但密鑰安全性低,常用於對內容加密。
- 非對稱加密效率低,但更安全,常用於對稱密鑰的交換過程中。需要生成公、私鑰。
- 加密算法RSA
- RSA是一種非對稱加密算法。
- 使用RSA一般需要產生公鑰和私鑰,當采用公鑰加密時,使用私鑰解密(只有私鑰擁有者可以解密,加密傳輸+防偽造);采用私鑰加密時,使用公鑰解密(只有私鑰擁有者可以加密,加密傳輸+防否認)。
- sql注入
- 操作系統
- Linux的Ctrl c發生了什么,與Ctrl z區別,說到了信號、管道、進程通信的方式,進程調度。
- linux 系統里,一個被打開的文件可以被另一個進程刪除嗎?
- epoll和poll,select的區別
- C++
- C++ 的動態多態怎么實現的?
- C++ 的構造函數可以是虛函數嗎?
- Python
- python協程原理/缺點
- python GIL, 為什么有GIL 還需要threading
- python gevent協程調度原理/缺點
- python的裝飾器
- 其他
- Hbase 底層原理
- dubbo的容錯機制有哪些、負載均衡機制有哪些
- redis和memcached