Java面試2-核心篇

核心篇

數據存儲

  • MySQL 索引使用的注意事項

    1.索引不會包含有NULL的列

     只要列中包含有NULL值,都將不會被包含在索引中,複合索引中只要有一列含有NULL值,那麼這一列對於此符合索引就是無效的。
    

    2.使用短索引

     對串列進行索引,如果可以就應該指定一個前綴長度。例如,如果有一個char255)的列,如果在前10個或20個字符內,多數值是唯一的,那麼就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁盤空間和I/O操作。
    

    3.索引列排序

     mysql查詢只使用一個索引,因此如果where子句中已經使用了索引的話,那麼order by中的列是不會使用索引的。因此數據庫默認排序可以符合要求的情況下不要使用排序操作,儘量不要包含多個列的排序,如果需要最好給這些列建複合索引。
    

    4.like語句操作

    一般情況下不鼓勵使用like操作,如果非使用不可,注意正確的使用方式。like ‘%aaa%’不會使用索引,而like ‘aaa%’可以使用索引。
    

    5.不要在列上進行運算
    6.不使用NOT IN 、<>、!=操作,但<,<=,=,>,>=,BETWEEN,IN是可以用到索引的
    7.索引要建立在經常進行select操作的字段上。

     這是因爲,如果這些列很少用到,那麼有無索引並不能明顯改變查詢速度。相反,由於增加了索引,反而降低了系統的維護速度和增大了空間需求。
    

    8.索引要建立在值比較唯一的字段上。
    9.對於那些定義爲text、image和bit數據類型的列不應該增加索引。因爲這些列的數據量要麼相當大,要麼取值很少。
    10.在where和join中出現的列需要建立索引。
    11.where的查詢條件裏有不等號(where column != …),mysql將無法使用索引。
    12.如果where字句的查詢條件裏使用了函數(如:where DAY(column)=…),mysql將無法使用索引。
    13.在join操作中(需要從多個數據表提取數據時),mysql只有在主鍵和外鍵的數據類型相同時才能使用索引,否則及時建立了索引也不會使用。

  • 說說反模式設計

    簡單的來說,反模式是指在對經常面對的問題經常使用的低效,不良,或者有待優化的設計模式/方法。甚至,反模式也可以是一種錯誤的開發思想/理念。在這裏我舉一個最簡單的例子:在面向對象設計/編程中,有一條很重要的原則, 單一責任原則(Single responsibility principle)。其中心思想就是對於一個模塊,或者一個類來說,這個模塊或者這個類應該只對系統/軟件的一個功能負責,而且該責任應該被該類完全封裝起來。當開發人員需要修改系統的某個功能,這個模塊/類是最主要的修改地方。相對應的一個反模式就是上帝類(God Class),通常來說,這個類裏面控制了很多其他的類,同時也依賴其他很多類。整個類不光負責自己的主要單一功能,而且還負責了其他很多功能,包括一些輔助功能。很多維護老程序的開發人員們可能都遇過這種類,一個類裏有幾千行的代碼,有很多功能,但是責任不明確單一。單元測試程序也變複雜無比。維護/修改這個類的時間要遠遠超出其他類的時間。很多時候,形成這種情況並不是開發人員故意的。很多情況下主要是由於隨着系統的年限,需求的變化,項目的資源壓力,項目組人員流動,系統結構的變化而導致某些原先小型的,符合單一原則類慢慢的變的臃腫起來。最後當這個類變成了維護的噩夢(特別是原先熟悉的開發人員離職後),重構該類就變成了一個不容易的工程。

  • 說說分庫與分表設計

    垂直分表在日常開發和設計中比較常見,通俗的說法叫做“大表拆小表”,拆分是基於關係型數據庫中的“列”(字段)進行的。通常情況,某個表中的字段比較多,可以新建立一張“擴展表”,將不經常使用或者長度較大的字段拆分出去放到“擴展表”中。在字段很多的情況下,拆分開確實更便於開發和維護(筆者曾見過某個遺留系統中,一個大表中包含100多列的)。某種意義上也能避免“跨頁”的問題(MySQL、MSSQL底層都是通過“數據頁”來存儲的,“跨頁”問題可能會造成額外的性能開銷,拆分字段的操作建議在數據庫設計階段就做好。如果是在發展過程中拆分,則需要改寫以前的查詢語句,會額外帶來一定的成本和風險,建議謹慎。
    垂直分庫在“微服務”盛行的今天已經非常普及了。基本的思路就是按照業務模塊來劃分出不同的數據庫,而不是像早期一樣將所有的數據表都放到同一個數據庫中。系統層面的“服務化”拆分操作,能夠解決業務系統層面的耦合和性能瓶頸,有利於系統的擴展維護。而數據庫層面的拆分,道理也是相通的。與服務的“治理”和“降級”機制類似,我們也能對不同業務類型的數據進行“分級”管理、維護、監控、擴展等。
    衆所周知,數據庫往往最容易成爲應用系統的瓶頸,而數據庫本身屬於“有狀態”的,相對於Web和應用服務器來講,是比較難實現“橫向擴展”的。數據庫的連接資源比較寶貴且單機處理能力也有限,在高併發場景下,垂直分庫一定程度上能夠突破IO、連接數及單機硬件資源的瓶頸,是大型分佈式系統中優化數據庫架構的重要手段。
    然後,很多人並沒有從根本上搞清楚爲什麼要拆分,也沒有掌握拆分的原則和技巧,只是一味的模仿大廠的做法。導致拆分後遇到很多問題(例如:跨庫join,分佈式事務等)。
    水平分表也稱爲橫向分表,比較容易理解,就是將表中不同的數據行按照一定規律分佈到不同的數據庫表中(這些表保存在同一個數據庫中),這樣來降低單表數據量,優化查詢性能。最常見的方式就是通過主鍵或者時間等字段進行Hash和取模後拆分。水平分表,能夠降低單表的數據量,一定程度上可以緩解查詢性能瓶頸。但本質上這些表還保存在同一個庫中,所以庫級別還是會有IO瓶頸。所以,一般不建議採用這種做法。
    水平分庫分表與上面講到的水平分表的思想相同,唯一不同的就是將這些拆分出來的表保存在不同的數據中。這也是很多大型互聯網公司所選擇的做法。某種意義上來講,有些系統中使用的“冷熱數據分離”(將一些使用較少的歷史數據遷移到其他的數據庫中。而在業務功能上,通常默認只提供熱點數據的查詢),也是類似的實踐。在高併發和海量數據的場景下,分庫分表能夠有效緩解單機和單庫的性能瓶頸和壓力,突破IO、連接數、硬件資源的瓶頸。當然,投入的硬件成本也會更高。同時,這也會帶來一些複雜的技術問題和挑戰(例如:跨分片的複雜查詢,跨分片事務等)。
    以上摘抄自: http://www.infoq.com/cn/articles/key-steps-and-likely-problems-of-split-table

  • 分庫與分錶帶來的分佈式困境與應對之策

    數據遷移與擴容問題
    前面介紹到水平分表策略歸納總結爲隨機分表和連續分表兩種情況。連續分表有可能存在數據熱點的問題,有些表可能會被頻繁地查詢從而造成較大壓力,熱數據的表就成爲了整個庫的瓶頸,而有些表可能存的是歷史數據,很少需要被查詢到。連續分表的另外一個好處在於比較容易,不需要考慮遷移舊的數據,只需要添加分表就可以自動擴容。隨機分表的數據相對比較均勻,不容易出現熱點和併發訪問的瓶頸。但是,分表擴展需要遷移舊的數據。
    針對於水平分表的設計至關重要,需要評估中短期內業務的增長速度,對當前的數據量進行容量規劃,綜合成本因素,推算出大概需要多少分片。對於數據遷移的問題,一般做法是通過程序先讀出數據,然後按照指定的分表策略再將數據寫入到各個分表中。
    表關聯問題
    在單庫單表的情況下,聯合查詢是非常容易的。但是,隨着分庫與分表的演變,聯合查詢就遇到跨庫關聯和跨表關係問題。在設計之初就應該儘量避免聯合查詢,可以通過程序中進行拼裝,或者通過反範式化設計進行規避。
    分頁與排序問題
    一般情況下,列表分頁時需要按照指定字段進行排序。在單庫單表的情況下,分頁和排序也是非常容易的。但是,隨着分庫與分表的演變,也會遇到跨庫排序和跨表排序問題。爲了最終結果的準確性,需要在不同的分表中將數據進行排序並返回,並將不同分表返回的結果集進行彙總和再次排序,最後再返回給用戶。
    分佈式事務問題
    隨着分庫與分表的演變,一定會遇到分佈式事務問題,那麼如何保證數據的一致性就成爲一個必須面對的問題。目前,分佈式事務並沒有很好的解決方案,難以滿足數據強一致性,一般情況下,使存儲數據儘可能達到用戶一致,保證系統經過一段較短的時間的自我恢復和修正,數據最終達到一致。
    分佈式全局唯一ID
    在單庫單表的情況下,直接使用數據庫自增特性來生成主鍵ID,這樣確實比較簡單。在分庫分表的環境中,數據分佈在不同的分表上,不能再借助數據庫自增長特性。需要使用全局唯一 ID,例如 UUID、GUID等。關於如何選擇合適的全局唯一 ID,我會在後面的章節中進行介紹。
    摘抄自:http://blog.csdn.net/jiangpingjiangping/article/details/78069480

  • 說說 SQL 優化之道

    一、一些常見的SQL實踐
    (1)負向條件查詢不能使用索引
    select from order where status!=0 and stauts!=1
    not in/not exists都不是好習慣
    可以優化爲in查詢:
    select 
    from order where status in(2,3)
    (2)前導模糊查詢不能使用索引
    select from order where desc like '%XX'
    而非前導模糊查詢則可以:
    select 
    from order where desc like 'XX%'
    (3)數據區分度不大的字段不宜使用索引
    select from user where sex=1
    原因:性別只有男,女,每次過濾掉的數據很少,不宜使用索引。
    經驗上,能過濾80%數據時就可以使用索引。對於訂單狀態,如果狀態值很少,不宜使用索引,如果狀態值很多,能夠過濾大量數據,則應該建立索引。
    (4)在屬性上進行計算不能命中索引
    select 
    from order where YEAR(date) < = '2017'
    即使date上建立了索引,也會全表掃描,可優化爲值計算:
    select from order where date < = CURDATE()
    或者:
    select 
    from order where date < = '2017-01-01'
    二、並非周知的SQL實踐
    (5)如果業務大部分是單條查詢,使用Hash索引性能更好,例如用戶中心
    select from user where uid=?
    select 
    from user where login_name=?
    原因:B-Tree索引的時間複雜度是O(log(n));Hash索引的時間複雜度是O(1)
    (6)允許爲null的列,查詢有潛在大坑
    單列索引不存null值,複合索引不存全爲null的值,如果列允許爲null,可能會得到“不符合預期”的結果集
    select from user where name != 'shenjian'
    如果name允許爲null,索引不存儲null值,結果集中不會包含這些記錄。
    所以,請使用not null約束以及默認值。
    (7)複合索引最左前綴,並不是值SQL語句的where順序要和複合索引一致
    用戶中心建立了(login_name, passwd)的複合索引
    select 
    from user where login_name=? and passwd=?
    select from user where passwd=? and login_name=?
    都能夠命中索引
    select 
    from user where login_name=?
    也能命中索引,滿足複合索引最左前綴
    select from user where passwd=?
    不能命中索引,不滿足複合索引最左前綴
    (8)使用ENUM而不是字符串
    ENUM保存的是TINYINT,別在枚舉中搞一些“中國”“北京”“技術部”這樣的字符串,字符串空間又大,效率又低。
    三、小衆但有用的SQL實踐
    (9)如果明確知道只有一條結果返回,limit 1能夠提高效率
    select 
    from user where login_name=?
    可以優化爲:
    select from user where login_name=? limit 1
    原因:你知道只有一條結果,但數據庫並不知道,明確告訴它,讓它主動停止遊標移動
    (10)把計算放到業務層而不是數據庫層,除了節省數據的CPU,還有意想不到的查詢緩存優化效果
    select 
    from order where date < = CURDATE()
    這不是一個好的SQL實踐,應該優化爲:
    $curDate = date('Y-m-d');
    $res = mysqlquery(
    'select from order where date < = $curDate');
    原因:
    釋放了數據庫的CPU
    多次調用,傳入的SQL相同,纔可以利用查詢緩存
    (11)強制類型轉換會全表掃描
    select 
    from user where phone=13800001234
    你以爲會命中phone索引麼?大錯特錯了,這個語句究竟要怎麼改?
    末了,再加一條,不要使用select *(潛臺詞,文章的SQL都不合格 =
    =),只返回需要的列,能夠大大的節省數據傳輸量,與數據庫的內存使用量喲。
    整理自:https://cloud.tencent.com/developer/article/1054203

  • MySQL 遇到的死鎖問題

    產生死鎖的四個必要條件:
    (1) 互斥條件:一個資源每次只能被一個進程使用。
    (2) 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
    (3) 不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。
    (4) 循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關係。
    這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會發生死鎖。
    下列方法有助於最大限度地降低死鎖:
    (1)按同一順序訪問對象。
    (2)避免事務中的用戶交互。
    (3)保持事務簡短並在一個批處理中。
    (4)使用低隔離級別。
    (5)使用綁定連接。
    整理自: http://onwise.xyz/2017/04/20/mysql-%E6%AD%BB%E9%94%81%E9%97%AE%E9%A2%98%E5%8F%8A%E8%A7%A3%E5%86%B3/

  • 存儲引擎的 InnoDB 與 MyISAM

      ◆1.InnoDB不支持FULLTEXT類型的索引。
      ◆2.InnoDB 中不保存表的具體行數,也就是說,執行select count() from table時,InnoDB要掃描一遍整個表來計算有多少行,但是MyISAM只要簡單的讀出保存好的行數即可。注意的是,當count()語句包含 where條件時,兩種表的操作是一樣的。
      ◆3.對於AUTO_INCREMENT類型的字段,InnoDB中必須包含只有該字段的索引,但是在MyISAM表中,可以和其他字段一起建立聯合索引。
      ◆4.DELETE FROM table時,InnoDB不會重新建立表,而是一行一行的刪除。
      ◆5.LOAD TABLE FROM MASTER操作對InnoDB是不起作用的,解決方法是首先把InnoDB表改成MyISAM表,導入數據後再改成InnoDB表,但是對於使用的額外的InnoDB特性(例如外鍵)的表不適用。
      另外,InnoDB表的行鎖也不是絕對的,假如在執行一個SQL語句時MySQL不能確定要掃描的範圍,InnoDB表同樣會鎖全表,例如update table set num=1 where name like “%aaa%”

  • 數據庫索引的原理

    數據庫索引,是數據庫管理系統中一個排序的數據結構,以協助快速查詢、更新數據庫表中數據。索引的實現通常使用B樹及其變種B+樹。

  • 爲什麼要用 B-tree

    一般來說,索引本身也很大,不可能全部存儲在內存中,因此索引往往以索引文件的形式存儲的磁盤上。這樣的話,索引查找過程中就要產生磁盤I/O消耗,相對於內存存取,I/O存取的消耗要高几個數量級,所以評價一個數據結構作爲索引的優劣最重要的指標就是在查找過程中磁盤I/O操作次數的漸進複雜度。換句話說,索引的結構組織要儘量減少查找過程中磁盤I/O的存取次數。

  • 聚集索引與非聚集索引的區別

    1).聚集索引一個表只能有一個,而非聚集索引一個表可以存在多個
    2).聚集索引存儲記錄是物理上連續存在,而非聚集索引是邏輯上的連續,物理存儲並不連續
     3).聚集索引:物理存儲按照索引排序;聚集索引是一種索引組織形式,索引的鍵值邏輯順序決定了表數據行的物理存儲順序
     非聚集索引:物理存儲不按照索引排序;非聚集索引則就是普通索引了,僅僅只是對數據列創建相應的索引,不影響整個表的物理存儲順序.
    4).索引是通過二叉樹的數據結構來描述的,我們可以這麼理解聚簇索引:索引的葉節點就是數據節點。而非聚簇索引的葉節點仍然是索引節點,只不過有一個指針指向對應的數據塊。

  • limit 20000 加載很慢怎麼解決

    mysql的性能低是因爲數據庫要去掃描N+M條記錄,然後又要放棄之前N條記錄,開銷很大
    解決思略:
    1、前端加緩存,或者其他方式,減少落到庫的查詢操作,例如某些系統中數據在搜索引擎中有備份的,可以用es等進行搜索
    2、使用延遲關聯,即先通用limit得到需要數據的索引字段,然後再通過原表和索引字段關聯獲得需要數據
    select a.* from a,(select id from table_1 where is_deleted='N' limit 100000,20) b where a.id = b.id
    3、從業務上實現,不分頁如此多,例如只能分頁前100頁,後面的不允許再查了
    4、不使用limit N,M,而是使用limit N,即將offset轉化爲where條件。

  • 選擇合適的分佈式主鍵方案

    1. 數據庫自增長序列或字段
    2. UUID
    3. 使用UUID to Int64的方法
    4. Redis生成ID
    5. Twitter的snowflake算法
    6. 利用zookeeper生成唯一ID
    7. MongoDB的ObjectId
  • 選擇合適的數據存儲方案

    關係型數據庫 MySQL
    MySQL 是一個最流行的關係型數據庫,在互聯網產品中應用比較廣泛。一般情況下,MySQL 數據庫是選擇的第一方案,基本上有 80% ~ 90% 的場景都是基於 MySQL 數據庫的。因爲,需要關係型數據庫進行管理,此外,業務存在許多事務性的操作,需要保證事務的強一致性。同時,可能還存在一些複雜的 SQL 的查詢。值得注意的是,前期儘量減少表的聯合查詢,便於後期數據量增大的情況下,做數據庫的分庫分表。
    內存數據庫 Redis
    隨着數據量的增長,MySQL 已經滿足不了大型互聯網類應用的需求。因此,Redis 基於內存存儲數據,可以極大的提高查詢性能,對產品在架構上很好的補充。例如,爲了提高服務端接口的訪問速度,儘可能將讀頻率高的熱點數據存放在 Redis 中。這個是非常典型的以空間換時間的策略,使用更多的內存換取 CPU 資源,通過增加系統的內存消耗,來加快程序的運行速度。
    在某些場景下,可以充分的利用 Redis 的特性,大大提高效率。這些場景包括緩存,會話緩存,時效性,訪問頻率,計數器,社交列表,記錄用戶判定信息,交集、並集和差集,熱門列表與排行榜,最新動態等。
    使用 Redis 做緩存的時候,需要考慮數據不一致與髒讀、緩存更新機制、緩存可用性、緩存服務降級、緩存穿透、緩存預熱等緩存使用問題。
    文檔數據庫 MongoDB
    MongoDB 是對傳統關係型數據庫的補充,它非常適合高伸縮性的場景,它是可擴展性的表結構。基於這點,可以將預期範圍內,表結構可能會不斷擴展的 MySQL 表結構,通過 MongoDB 來存儲,這就可以保證表結構的擴展性。
    此外,日誌系統數據量特別大,如果用 MongoDB 數據庫存儲這些數據,利用分片集羣支持海量數據,同時使用聚集分析和 MapReduce 的能力,是個很好的選擇。
    MongoDB 還適合存儲大尺寸的數據,GridFS 存儲方案就是基於 MongoDB 的分佈式文件存儲系統。
    列族數據庫 HBase
    HBase 適合海量數據的存儲與高性能實時查詢,它是運行於 HDFS 文件系統之上,並且作爲 MapReduce 分佈式處理的目標數據庫,以支撐離線分析型應用。在數據倉庫、數據集市、商業智能等領域發揮了越來越多的作用,在數以千計的企業中支撐着大量的大數據分析場景的應用。
    全文搜索引擎 ElasticSearch
    在一般情況下,關係型數據庫的模糊查詢,都是通過 like 的方式進行查詢。其中,like “value%” 可以使用索引,但是對於 like “%value%” 這樣的方式,執行全表查詢,這在數據量小的表,不存在性能問題,但是對於海量數據,全表掃描是非常可怕的事情。ElasticSearch 作爲一個建立在全文搜索引擎 Apache Lucene 基礎上的實時的分佈式搜索和分析引擎,適用於處理實時搜索應用場景。此外,使用 ElasticSearch 全文搜索引擎,還可以支持多詞條查詢、匹配度與權重、自動聯想、拼寫糾錯等高級功能。因此,可以使用 ElasticSearch 作爲關係型數據庫全文搜索的功能補充,將要進行全文搜索的數據緩存一份到 ElasticSearch 上,達到處理複雜的業務與提高查詢速度的目的。
    ElasticSearch 不僅僅適用於搜索場景,還非常適合日誌處理與分析的場景。著名的 ELK 日誌處理方案,由 ElasticSearch、Logstash 和 Kibana 三個組件組成,包括了日誌收集、聚合、多維度查詢、可視化顯示等。
    摘抄自:http://blog.720ui.com/2017/db_better_db_use/

  • ObjectId 規則

    [0,1,2,3] [4,5,6] [7,8] [9,10,11]
    時間戳 |機器碼 |PID |計數器
    前四位是時間戳,可以提供秒級別的唯一性。
    接下來三位是所在主機的唯一標識符,通常是機器主機名的散列值。
    接下來兩位是產生ObjectId的PID,確保同一臺機器上併發產生的ObjectId是唯一的。
    前九位保證了同一秒鐘不同機器的不同進程產生的ObjectId時唯一的。
    最後三位是自增計數器,確保相同進程同一秒鐘產生的ObjectId是唯一的。
    https://github.com/qianjiahao/MongoDB/wiki/MongoDB%E4%B9%8B_id%E7%94%9F%E6%88%90%E8%A7%84%E5%88%99

  • 聊聊 MongoDB 使用場景

    高伸縮性的場景
    MongoDB 非常適合高伸縮性的場景,它是可擴展性的表結構。基於這點,可以將預期範圍內,表結構可能會不斷擴展的 MySQL 表結構,通過 MongoDB 來存儲,這就可以保證表結構的擴展性。
    日誌系統的場景
    日誌系統數據量特別大,如果用 MongoDB 數據庫存儲這些數據,利用分片集羣支持海量數據,同時使用聚集分析和 MapReduce 的能力,是個很好的選擇。
    分佈式文件存儲
    MongoDB 還適合存儲大尺寸的數據,之前介紹的 GridFS 存儲方案,就是基於 MongoDB 的分佈式文件存儲系統。
    摘抄自: http://blog.720ui.com/2017/mongodb_core_use/

  • 倒排索引

    倒排索引(英語:Inverted index),也常被稱爲反向索引、置入檔案或反向檔案,是一種索引方法,被用來存儲在全文搜索下某個單詞在一個文檔或者一組文檔中的存儲位置的映射。它是文檔檢索系統中最常用的數據結構。
    有兩種不同的反向索引形式:
    一條記錄的水平反向索引(或者反向檔案索引)包含每個引用單詞的文檔的列表。
    一個單詞的水平反向索引(或者完全反向索引)又包含每個單詞在一個文檔中的位置。

  • 聊聊 ElasticSearch 使用場景

    全文搜索,這個是用的最多的。加上分詞插件、拼音插件什麼的可以做成強大的全文搜索引擎。
    數據庫,挺奇葩的用法,因爲ES存數相同數據,更費空間,不過確實不錯,因爲他的強大統計分析彙總能力,再加上分佈式P2P擴展能力,現在硬件又那麼便宜,所以就有人拿來當數據庫了。
    在線統計分析引擎,日誌系統。logstash,不用解釋了吧。可以實時動態分析數據,很是爽。

緩存使用

  • Redis 有哪些類型

    在Redis中有五種數據類型
    String----------字符串
    Hash------------字典
    List-------------列表
    Set--------------集合
    Sorted Set------有序集合

  • Redis 內部結構

    Redis 內部使用一個 redisObject 對象來表示所有的 key 和 value。type :代表一個 value 對象具體是何種數據類型。
    encoding :是不同數據類型在 redis 內部的存儲方式,比如:type=string 代表 value 存儲的是一個普通字符串,那麼對應的 encoding 可以是 raw 或者是 int,如果是 int 則代表實際 redis 內部是按數值型類存儲和表示這個字符串的,當然前提是這個字符串本身可以用數值表示,比如:"123" "456"這樣的字符串。
    vm 字段:只有打開了 Redis 的虛擬內存功能,此字段纔會真正的分配內存,該功能默認是關閉狀態的。 Redis 使用 redisObject 來表示所有的 key/value 數據是比較浪費內存的,當然這些內存管理成本的付出主要也是爲了給 Redis 不同數據類型提供一個統一的管理接口,實際作者也提供了多種方法幫助我們儘量節省內存使用。
    作者:zhanglbjames
    鏈接:https://www.jianshu.com/p/f09480c05e42
    來源:簡書
    著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

  • 聊聊 Redis 使用場景

    1. 緩存
    2. 會話緩存
    3. 時效性
    4. 訪問頻率
    5. 計數器
    6. 社交列表
    7. 記錄用戶判定信息
    8. 交集、並集和差集
    9. 熱門列表與排行榜
    10. 最新動態
    11. 消息隊列
      摘抄自:http://blog.720ui.com/2017/redis_core_use/
  • Redis 持久化機制

    redis有兩種持久化機制RDB與AOF。
    摘抄自: http://shanks.leanote.com/post/Untitled-55ca439338f41148cd000759-22

  • Redis 如何實現持久化

    RDB持久化方式會在一個特定的間隔保存那個時間點的一個數據快照。
    AOF持久化方式則會記錄每一個服務器收到的寫操作。在服務啓動時,這些記錄的操作會逐條執行從而重建出原來的數據。寫操作命令記錄的格式跟Redis協議一致,以追加的方式進行保存。
    Redis的持久化是可以禁用的,就是說你可以讓數據的生命週期只存在於服務器的運行時間裏。
    兩種方式的持久化是可以同時存在的,但是當Redis重啓時,AOF文件會被優先用於重建數據。

  • Redis 集羣方案與實現

    客戶端分片
    基於代理的分片
    路由查詢
    客戶端分片
    由客戶端決定key寫入或者讀取的節點。
    包括jedis在內的一些客戶端,實現了客戶端分片機制。

路由查詢
將請求發送到任意節點,接收到請求的節點會將查詢請求發送到正確的節點上執行。
開源方案

  • Redis 爲什麼是單線程的

    因爲CPU不是Redis的瓶頸。Redis的瓶頸最有可能是機器內存或者網絡帶寬。(以上主要來自官方FAQ)既然單線程容易實現,而且CPU不會成爲瓶頸,那就順理成章地採用單線程的方案了。

  • 緩存奔潰

    1. 碰到這種情況,一般併發量不是特別多的時候,使用最多的解決方案是加鎖排隊。
    2. 加鎖排隊只是爲了減輕數據庫的壓力,並沒有提高系統吞吐量。假設在高併發下,緩存重建期間key是鎖着的,這是過來1000個請求999個都在阻塞的。同樣會導致用戶等待超時,這是個治標不治本的方法。
  • 緩存降級

    頁面降級:在大促或者某些特殊情況下,某些頁面佔用了一些稀缺服務資源,在緊急情況下可以對其整個降級,以達到丟卒保帥;
    頁面片段降級:比如商品詳情頁中的商家部分因爲數據錯誤了,此時需要對其進行降級;
    頁面異步請求降級:比如商品詳情頁上有推薦信息/配送至等異步加載的請求,如果這些信息響應慢或者後端服務有問題,可以進行降級;
    服務功能降級:比如渲染商品詳情頁時需要調用一些不太重要的服務:相關分類、熱銷榜等,而這些服務在異常情況下直接不獲取,即降級即可;
    讀降級:比如多級緩存模式,如果後端服務有問題,可以降級爲只讀緩存,這種方式適用於對讀一致性要求不高的場景;
    寫降級:比如秒殺搶購,我們可以只進行Cache的更新,然後異步同步扣減庫存到DB,保證最終一致性即可,此時可以將DB降級爲Cache。
    爬蟲降級:在大促活動時,可以將爬蟲流量導向靜態頁或者返回空數據,從而保護後端稀缺資源。
    自動開關降級
    自動降級是根據系統負載、資源使用情況、SLA等指標進行降級。
    超時降級
    當訪問的數據庫/http服務/遠程調用響應慢或者長時間響應慢,且該服務不是核心服務的話可以在超時後自動降級;比如商品詳情頁上有推薦內容/評價,但是推薦內容/評價暫時不展示對用戶購物流程不會產生很大的影響;對於這種服務是可以超時降級的。如果是調用別人的遠程服務,和對方定義一個服務響應最大時間,如果超時了則自動降級。
    摘抄自: http://jinnianshilongnian.iteye.com/blog/2306477

  • 使用緩存的合理性問題

    1. 熱點數據,緩存纔有價值
    2. 頻繁修改的數據,看情況考慮使用緩存
    3. 數據不一致性
    4. 緩存更新機制
    5. 緩存可用性
    6. 緩存服務降級
    7. 緩存預熱
    8. 緩存穿透
      摘抄自: http://blog.720ui.com/2016/redis_action_01_use_core/

消息隊列

  • 消息隊列的使用場景

    校驗用戶名等信息,如果沒問題會在數據庫中添加一個用戶記錄
    如果是用郵箱註冊會給你發送一封註冊成功的郵件,手機註冊則會發送一條短信
    分析用戶的個人信息,以便將來向他推薦一些志同道合的人,或向那些人推薦他
    發送給用戶一個包含操作指南的系統通知

  • 消息的重發補償解決思路

    可靠消息服務定時查詢狀態爲已發送並超時的消息
    可靠消息將消息重新投遞到 MQ 組件中
    下游應用監聽消息,在滿足冪等性的條件下,重新執行業務。
    下游應用通知可靠消息服務該消息已經成功消費。
    通過消息狀態確認和消息重發兩個功能,可以確保上游應用、可靠消息服務和下游應用數據的最終一致性。

  • 消息的冪等性解決思路

    1. 查詢操作
      查詢一次和查詢多次,在數據不變的情況下,查詢結果是一樣的。select是天然的冪等操作
    2. 刪除操作
      刪除操作也是冪等的,刪除一次和多次刪除都是把數據刪除。(注意可能返回結果不一樣,刪除的數據不存在,返回0,刪除的數據多條,返回結果多個)
      3.唯一索引,防止新增髒數據
      比如:支付寶的資金賬戶,支付寶也有用戶賬戶,每個用戶只能有一個資金賬戶,怎麼防止給用戶創建資金賬戶多個,那麼給資金賬戶表中的用戶ID加唯一索引,所以一個用戶新增成功一個資金賬戶記錄
    3. token機制,防止頁面重複提交
    4. 悲觀鎖
      獲取數據的時候加鎖獲取
      select * from table_xxx where id='xxx' for update;
      注意:id字段一定是主鍵或者唯一索引,不然是鎖表,會死人的
      悲觀鎖使用時一般伴隨事務一起使用,數據鎖定時間可能會很長,根據實際情況選用
    5. 樂觀鎖
      樂觀鎖只是在更新數據那一刻鎖表,其他時間不鎖表,所以相對於悲觀鎖,效率更高。
    6. 分佈式鎖
      還是拿插入數據的例子,如果是分佈是系統,構建全局唯一索引比較困難,例如唯一性的字段沒法確定,這時候可以引入分佈式鎖,通過第三方的系統(redis或zookeeper),在業務系統插入數據或者更新數據,獲取分佈式鎖,然後做操作,之後釋放鎖,這樣其實是把多線程併發的鎖的思路,引入多多個系統,也就是分佈式系統中得解決思路。
    7. select + insert
      併發不高的後臺系統,或者一些任務JOB,爲了支持冪等,支持重複執行,簡單的處理方法是,先查詢下一些關鍵數據,判斷是否已經執行過,在進行業務處理,就可以了
      注意:核心高併發流程不要用這種方法
    8. 狀態機冪等
      在設計單據相關的業務,或者是任務相關的業務,肯定會涉及到狀態機(狀態變更圖),就是業務單據上面有個狀態,狀態在不同的情況下會發生變更,一般情況下存在有限狀態機,這時候,如果狀態機已經處於下一個狀態,這時候來了一個上一個狀態的變更,理論上是不能夠變更的,這樣的話,保證了有限狀態機的冪等。
    9. 對外提供接口的api如何保證冪等
      如銀聯提供的付款接口:需要接入商戶提交付款請求時附帶:source來源,seq序列號
      source+seq在數據庫裏面做唯一索引,防止多次付款,(併發時,只能處理一個請求)
      摘抄自: http://825635381.iteye.com/blog/2276077
  • 消息的堆積解決思路

    如果還沒開始投入使用kafka,那應該在設計分區數的時候,儘量設置的多點(當然也不要太大,太大影響延遲,具體可以參考我前面提到的文章),從而提升生產和消費的並行度,避免消費太慢導致消費堆積。
    增大批次
    瓶頸在消費吞吐量的時候,增加批次也可以改善性能
    增加線程數
    如果一些消費者組中的消費者線程還是有1個消費者線程消費多個分區的情況,建議增加消費者線程。儘量1個消費者線程對應1個分區,從而發揮現有分區數下的最大並行度。
    摘抄自: https://kaimingwan.com/post/framworks/kafka/kafkaxiao-xi-dui-ji-chu-li

  • 自己如何實現消息隊列

    大體上的設計是由一條線程1執行從等待列表中獲取任務插入任務隊列再由線程池中的線程從任務隊列中取出任務去執行.
    添加一條線程1主要是防止在執行耗時的任務時阻塞主線程.當執行耗時任務時,添加的任務的操作快於取出任務的操作,
    當任務隊列長度達到最大值時,線程1將被阻塞,等待線程2,3...從任務隊列取出任務執行。
    作者:DrJasonZhang
    鏈接:https://www.jianshu.com/p/2d2271ecc64d
    來源:簡書
    著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

  • 如何保證消息的有序性

    通過輪詢所有隊列的方式來確定消息被髮送到哪一個隊列(負載均衡策略)。訂單號相同的消息會被先後發送到同一個隊列中,
    在獲取到路由信息以後,會根據算法來選擇一個隊列,同一個OrderId獲取到的肯定是同一個隊列。

【轉載自:】http://www.spring4all.com/article/929

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