導航:首頁 > 軟體知識 > redis如何成為高效的程序員

redis如何成為高效的程序員

發布時間:2023-05-19 03:14:36

『壹』 redis怎樣解決高並發

1.Redis存取數據是基於內存的,而內存的讀寫速度非常快,這是前提。
2.Redis是單線程的,省去了很多切換線程的時間消耗。(PS:Redis單線程指的是網路請求模塊使用了一個線程(所以不需考慮並發安全性),即一個線程處理所有網路請求,其他模塊仍用了多線程。PPS:目前新版的Redis分支也在深入的探索多線程的方式,理論效率要高於目前市面上的穩定版本。)
3.Redis採用網路IO多路復用技術(非阻塞IO)來保證在多連接的時候,系統的高吞吐量。而非阻塞IO,其內部實現採用了epoll並結合了本身實現的簡單的事件框架,epoll中的讀、寫、關閉、連接都轉化成了事件,然後利用epoll的多路復用特性極大的節省了在處理IO上的時間。(PS:多路復用指的是多個socket連接復用一個線程。而epoll是目前最好的多路復用技術,所以使用epoll實現多路I/O復用技術可以讓單個線程高效的處理多個連接請求,從而達到盡量減少網路IO的時間消耗)

『貳』 redis源碼解讀:單線程的redis是如何實現高速緩存的

redis可能是最近幾年最火的緩存資料庫方案了,在各個高並發領域都有應用。

這篇文章,我們將從源代碼腔鉛攔的角度來分析一下,為何如此一個高性能,高應用的緩存,會是單線程的方案,當然一個方案的高性能,高並發是多方面的綜合因素,其它的因素我們將在後續解讀。後續分析主要以LINUX操作系統為基礎,這也是redis應用最廣的平台。

單線程最大的受限是什麼?就是CPU,現在伺服器一般已經是多CPU,激扒而單線程只能使用到其中的一個核。

redis作為一個網路內存緩存資料庫,在實現高性能時,主要有4個點。

1.網路高並發,高流量的數據處理。

一個非同步,高效,且對CPU要求不高的網路模型,這個模型主要是由OS來提供的,目前在LINUX最主流使用的是EPOLL,這個網上介紹很多,伍胡主要是基於事件驅動的一個非同步模型。

2.程序內部的合理構架,調用邏輯,內存管理。

redis在採用純C實現時,整體調用邏輯很短,但在內存方面,適當的合並了一些對象和對齊,比如sds等,在底層使用了內存池,在不同情況下使用的不太一樣。

但整體處理上沒有NGINX的內池設計巧妙,當然二者不太一樣,NGINX是基於請求釋放的邏輯來設計的,因此針對請求,可以一次申請大塊,分量使用,再最後統一釋放。

3.數據復制的代價,不管是讀取數據或是寫入數據,一般都是需要有數據復制的過程。

數據復制其實就是一次內存,真正的代價是在於存在大VALUE,當value值長度超過16KB時,性能會開始下降。因為單線程的原因,如果存在一個超大VALUE,比如20MB,則會因為這個請求卡住整個線程,導致後續的請求進不來,雖然後面的請求是能快速處理的小請求。

4.redis中數據結構中演算法的代價,有些結構在大數據量時,代價是很高的。

很多時間,大家忽略了演算法的運算代碼,因為像memcached等這類是完全的KV緩存,不存在什麼演算法,除了一個KEY的查找定位HASH演算法。

而redis不一樣,提供了不少高階的數據對象,這些對象具有上層的一些演算法能力,而這些能力是需要比如GEO模塊。

『叄』 程序員 redis都用什麼機制

數據持久化通俗講就是把數據保存到磁碟上,保證不會因為斷電等因素丟失數據。
redis需要經常將內存中的數據同步到磁碟來保證持久化。redis支持兩種持久化方式,一種是 Snapshotting(快照)也是默認方式,另一種是Append-only file(縮寫aof)的方式
snapshotting(快照)方式:
這種方式是將內存中數據以快照的方式寫入到二進制文件中,默認的文件名為mp.rdb(redis的bin目錄下).
可以通過配置設置自動做快照持久化的方式.我們可以配置redis在n秒內如果超過M個KEY修改就自動做快照.
save 900 1 #900秒內如果超過1個KEY被修改,則發起快照.
save 300 10 #300秒內如果超過10個KEY被修改,則發起快照.
快照保存機制:
1: redis先調用fork子進程,
2: 子進程負責將內存內容寫入到臨時文件.
3: 當子進程將快照寫入臨時文件完畢時,替換原來快照文件.子進程退出大告.
快照是在一定間隔時間做一次的,如果redis意外down掉的話,就會丟失最後一次快照時所有修改.
每次快照持久化都是將內存數據完整寫入到磁碟一次,並不 是增量的只同步臟數據。如果數據量大的話,而且段源寫操作比較多,必然會引起大量的磁碟io操作,可能會嚴重影響性能
aof方式:
比快照有更好的持久性,是由於在使用aof時,redis會將每一個收到的寫命令都通過write函數追加到文件中,當redis重啟時會通過重新執行文件中保存的寫命令來在內存中重建整個資料庫的內容.
文件名為appendonly.aof (redis的bin目錄下,存的都是一些操作命令).
[root@localhost redis]# vi /usr/local/redis/etc/redis.conf
修改配置如下:
#開啟aof
appendonly no改為appendonly yes
在Redis的配置文件中存在三種同步方式,它們分別是:
#每次有數據修改發生時都會寫入AOF文件。效率最慢握仿態,但保證完全持久化.
appendfsync always
#每秒鍾同步一次,該策略為AOF的預設策略。 性能和持久做了很好折中.
appendfsync everysec
#從不同步。高效但是數據不會被持久化.持久化沒保證
appendfsync no

『肆』 如何編寫高效率c++ redis客戶端

include <stdio.h>int main(){ int arr1[20], arr2[20], sum[20] = {0}; int count = 0, a, b, i, temp; scanf("%d %d", &a, &b); while (a != 0 || b != 0) { arr1[count] = a % 10; arr2[count] = b % 10; a /= 10; b /= 10; count++; } for (i = 0; i < count; i++) { temp = arr1[i] + arr2[i]; sum[i] += temp % 10; sum[i + 1] = temp / 10; } if (sum[count]) printf("%d", sum[count]); else printf("%d", sum[count - 1]); return 0;}把兩個數拆開,每個位相加,處理進位。因為這是加法,所以結果的長度和兩數中最螞老長的那個相同,或者正罩比它大1,舉物鬧if判斷一下哪個是最高位就行了。sum存儲的是兩數和的每一位,它的所有元素的初始值都被置為0.

『伍』 程序員怎麼去提升自己能力

二:信息採集器和筆記本

首先你要給自己設定一個目標,就如同一個公司會設定它的Vision。

目標要夠大,這樣你才能看到更多的風景。

目標應該設定在解決哪一類問題,而不是精通哪一類技術。技術只是手段,不是目的。

例如,「我要成為iOS developer中的達人」這個目標,就遠不如「我要成為前端應用開發的專家」來得有意義。前者學到深處你可能會去鑽研iOS framework里各種奇技淫巧,而後者你會開始關注視覺與交互設計,研究各平台間的差異與共同趨勢。顯然,後者更有助於你的個人發展。

不過即便有了明確的目標,選擇哪一類技術學習,如何學習,在信息過載的今天依然是一個難題。常有的觀點是應該學習最新的技術,因為老的已經過時,而反對的觀點則是新技術還不成熟。我個人的觀點是,當初入一個領域時,選擇主流技術框架;當你有一定經驗後,選擇技術時更應該關注背後的推動者,我相信優秀的人和團隊總能打造優秀的產品,無論是商業公司還是開源社區。不必太在意技術的新舊,因為可能很快都會成為過去時。你真正要學習的是技術背後的思想。有不少語言與開源項目會寫它的Coding philosophy,這是很有意思的,你可以從它們的源代碼中去驗證這些編程理念。以Python為例,如果你執行import this就會看到它的理念,再如Python中一個著名的開源庫Celery,在它的文檔有專門一節講述它的編程理念。它們對你的影響會比這些技術本身來得更深遠,這是我給初學者們的一個忠告。

同理,我非常推薦讀一些優秀開源庫或是語言的源代碼,例如Python的標准庫絕大部分都是用Python實現的,而且可讀性非常好。如果學習一門技術僅僅停留在用的層面上,你就還沒有完全吸取其中的精華,而且學習的收益會隨著技術的過時而消失。

我的另一個學習原則是,在選擇學習一門新技術時,最大化它與你現有知識庫的差異性。讀起來可能有拗口,例如你會Django,接下去你應該去學習Ruby on Rails還是NodeJS?依據這個原則,你應該學NodeJS,因為它的非同步IO模型在理念上與Django的同步模型差異很大,而RoR則與Django更多相似之處。但更好的選擇是不要去學另一個Web framework,去學習ZeroMQ或是Redis,這兩者對於Web development也非常有幫助,這樣就做到了最大化差異。從構建一個程序員的技術理念角度,我會推薦每一個程序至少去了解Lisp或是一門Functional programming language,不管你是否會在可見的未來用到,它們能讓你從一個不同的角度看待編程。

最後我建議每個程序員都應該經營一款自己的產品,它可以是一款app,一個網站或是一個開源軟體。除非你是一個創業公司的早期員工,不然你可能沒有機會將所有學到的技術或是理念都付之實踐,有很多人想成為全棧工程師,最快的捷徑就是打造一款自己的產品。任何一個設計師都會精心打造自己的Portfolio,但大部分程序員卻不會。當評估一個程序員的Coding能力時,我會去看他的Github上是否有出彩的項目,可惜國內絕大部分程序員的Github空空如也,或者只有一些非常簡單的程序。我建議大家好好經營自己Github上項目,這不但可以提高你的聲譽,對你將來的求職也非常有幫助。當你報怨求職面試時又被問到各種無厘頭的程序題時,有沒有想過面試官也很無奈,因為他沒有任何其他方法得知你的Coding能力究竟如何。如果每一個程序員都有自己的作品,我想程序員的面試會簡單許多。

重視溝通能力的培養

當被問到「你覺得Junior Developer和Senior Developer最大的差別是什麼」時,我最自然的反應是溝通與文檔。溝通包括程序員團隊內部的溝通,與其他團隊的溝通,與Manager的溝通等等。我不認為自己有能力把這些問題非常概括地說清楚,不過我可以給一條建議,那就是先學會和你的Manager溝通,讓他來教你其餘的部分。許多公司都會設置Manager與組員的1:1,一個有效率的1:1應該大部分時間有組員來主導。這需要你在1:1之前花足夠多的時間來考慮要問的問題,並且最好提前1天發給Manager,讓他有機會思考答案。許多人對此不太重視,或者只問非常具體的問題而不是一些開放性問題,這樣你很難在你的Manager身上學到東西。如果你漸漸懂得如何利用1:1的時間,它很會成為你在工作中單位時間投資回報率最高的活動。

累積你的人脈

每個人都明白人脈的重要性,但實際做起來卻不容易。參加一些線下的會議或是活動,可能是最直接的擴展人脈的方式之一。可惜大部分人似乎只是去聽了一場技術講座就回家了。當然,這和不少活動的時間安排也有關系,講座時間排得太滿,茶歇時間短,加上有時嘉賓遲到或是沒控制好時間,乾脆就把茶歇取消了。而實際上,結識一兩個同道中人遠比聽技術講座有價值。下次去參加這類會議,不妨給你自己設個目標,比如至少加兩個同行的微信。之後維系你的人脈可能需要花更多的時間,下了班或是周末找你的朋友們喝個咖啡吧?

另外我覺得每個人都需要一個職場導師,他可以是你第一份工作的導師或是Manager,也可以是你認識的其他前輩。你們需要維系一個非常長期的關系,不止於一家公司,最好貫穿你的整個職業生涯。每當你遇到疑惑時,都可以詢求他的建議,我覺得這將是你最寶貴的一筆人脈財富。

尋找發揮你才華的平台

最後也是最重要的一步,找到適合你的公司。做為求職者評估一家公司可以看三個方面:

公司的發展前景(大公司的話,看所在部門的發展前景)

你將要加入的團隊

薪資福利

所以在面試一家公司的時候,你要意識到面試是雙向的,公司在面試你的同時,你也在面試這家公司。面試前你應該對這家公司做足功課,准備好一些有質量的問題,比如指出產品中的問題,詢問開發流程或是如何做績效評估。到時你也可以檢驗一下你的面試官是否合格。

每次選擇公司對以上三個方面都應兼顧,但在職業生涯的不同階段,側重點不同。比如,在剛剛工作時,加入一個優秀的團隊最為重要,他們可以教會你很多東西,提升你的能力。工作5年之後,你需要一個平台施展你的才華,體現個人價值,公司發展前景的重要性迅速提升。當你做出一番成績,證明了自己的價值之後,逐漸進入收獲期,就有了與公司要價的資格。另一方面,團隊實力對公司的前景也有很大的影響。

對一個剛畢業,初入職場的同學,一個近幾年被問了無數次的問題「我的第一份工作是去創業公司還是大公司?」我的回答仍舊是「加入一個優秀的團隊最為重要」。一些知名的大公司,團隊的素質是有一定保證,但創業公司則不然,團隊素質參差不齊,所以如我前面所說你需要面試這個團隊,做出自己的判斷。不過除了團隊因素之外,我想提一下畢業生去創業公司的幾個好處。

首先,在剛畢業的一段時間內,經濟壓力小,是最自由最能承受風險的時期,而這段時間往往不長,所以應把握好這個去創業公司的黃金時段。其次,所有的學生進入大公司後,都會擔任初級職位,某種程度上來講是學校學習的延續,規范有條理,但缺乏獨立性和創新性,而這正是中國大部分學生所欠缺的。這方面的能力在一家創業公司可以得到快速鍛煉,而在大公司可能要等升到中級職位後才有這方面的機會。個人觀點,僅供參考。

小結

我覺得步入職場的前3年對今後的發展尤其重要,希望此文能對年輕的程序員們有所幫助。歡迎評論?

如果想私信我的朋友可以加群,大家一起學習,一起學術分享,資料共享

『陸』 調研Redis高可用兩種方案

導讀:Redis是被廣泛使用的基礎軟體之一。對於工程師和,架構師,運維人員來說,了解Redis的高可用方案和背後的原理,是必備的基礎知識。本文作者深入分析了Redis高可用的方方面面,並且做了有效總結,相信對廣大讀者可以起到很好的領路作用。

作者 codemp codemp.info 博主,多年從事陸讓互聯網伺服器後台開發工作。可訪問作者博客閱讀 codemp 更多文章。

Redis中為了實現高可用(High Availability,簡稱HA),採用了如下兩個方式:

Redis中主從節點復制數據有全量復制和部分復制之分。

全量復制使用snyc命令來實現,其流程是:

舊版本全量復制功能,其最大的問題是從伺服器斷線重連時,即便在從伺服器上已經有一部分數據了,也需要進行全量復制,這樣做的效率很低,於是新版本的Redis在這部分做了改進。

新版本Redis使用psync命令來代替sync命令,該命令既可以實現完整全同步也可以實現部分同步。

執行復制的雙方,主從伺服器,分別會維護一個復制偏移量:

主伺服器內部維護了一個固定長度的先進先出隊列做為復制積壓緩沖區,其默認大小為1MB。

在主伺服器進行命令傳播時,不僅會將寫命令同步到從伺服器,還會將寫命令寫入復制積壓緩沖區。

每個Redis伺服器,都有其運行ID,運行ID由伺服器在啟動時自動生成,主伺服器會將自己的運行ID發送給從伺服器,而從伺服器會將主伺服器的運行ID保存起來。

從伺服器Redis斷線重連之後進行同液叢步時,就是根據運行ID來判斷同步的進度:

有了前面的准備,下面開始分析psync命令的流程:

前面兩種情況主伺服器收到psync命令之後,會出現以下三種可能:

Redis使用哨兵機制來實現高可用(HA),其大概工作原理鬧悉櫻是:

以上將Redis節點分為兩類:

以上是大體的流程,這個流程需要解決以下幾個問題:

以下來逐個回答這些問題。

哨兵節點通過三個定時監控任務監控Redis數據節點的服務可用性。

每隔10秒,每個哨兵節點都會向主、從Redis數據節點發送info命令,獲取新的拓撲結構信息。

Redis拓撲結構信息包括了:

這樣,哨兵節點就能從info命令中自動獲取到從節點信息,因此那些後續才加入的從節點信息不需要顯式配置就能自動感知。

這一操作實際上完成了兩件事情: * 發現新的哨兵節點:如果有新的哨兵節點加入,此時保存下來這個新哨兵節點的信息,後續與該哨兵節點建立連接。 * 交換主節點的狀態信息,作為後續客觀判斷主節點下線的依據。

每隔1秒,每個哨兵節點向主、從數據節點以及其他sentinel節點發送ping命令做心跳探測,這個心跳探測是後續主觀判斷數據節點下線的依據。

上面三個監控任務中的第三個探測心跳任務,如果在配置的down-after-milliseconds之後沒有收到有效回復,那麼就認為該數據節點「主觀下線(sdown)」。

為什麼稱為「主觀下線」?因為在一個分布式系統中,有多個機器在一起聯動工作,網路可能出現各種狀況,僅憑一個節點的判斷還不足以認為一個數據節點下線了,這就需要後面的「客觀下線」。

當一個哨兵節點認為主節點主觀下線時,該哨兵節點需要通過」sentinel is-master-down-by addr」命令向其他哨兵節點咨詢該主節點是否下線了,如果有超過半數的哨兵節點都回答了下線,此時認為主節點「客觀下線」。

當主節點客觀下線時,需要選舉出一個哨兵節點做為哨兵領導者,以完成後續選出新的主節點的工作。

這個選舉的大體思路是:

可以看到,這個選舉領導者的流程很像raft中選舉leader的流程。

在剩下的Redis從節點中,按照以下順序來選擇新的主節點:

選擇了新的主節點之後,還需要最後的流程讓該節點成為新的主節點:

原文地址:

https://www.codemp.info/post/20190409-redis-sentinel/

參考閱讀:

GIAC全球互聯網架構大會深圳站將於2019年6月舉行,掌閱資深架構師,暢銷圖書《Redis 深度歷險:核心原理與應用實踐》作者錢文品將作為資料庫專場的講師出席2019年GIAC深圳站,並做關於Redis高性能,高可用方面的的演講。本屆GIAC資料庫專場邀請阿里雲前資料庫總負責人余峰作為出品人,議題如下。

參加 GIAC,盤點2019年最新技術,目前 購買7.5折優惠 ,多人購買有更多優惠。識別二維碼 了解大會更多詳情。

『柒』 如何高效地向Redis寫入大量的數據

具體實現步驟如下:
1. 新建一個文本文件,包含redis命令
SET Key0 Value0
SET Key1 Value1
...
SET KeyN ValueN
如果有了原始數據,其實構造這個文件並不難,譬如shell,python都可以
2. 將這些命令轉化成Redis Protocol。
因為Redis管道功能支持的是Redis Protocol,而不是直接的Redis命令。
如何轉化,可參考後面的腳本。
3. 利用管道插入
cat data.txt | redis-cli --pipe
Shell VS Redis pipe
下面通過測試來具體看看Shell批量導入和Redis pipe之間的效率。
測試思路:分別通過shell腳本和Redis pipe向資料庫中插入10萬相同數據,查看各自所花費的時間。
Shell
腳本如下:
#!/bin/bash
for ((i=0;i<100000;i++))
do
echo -en "helloworld" | redis-cli -x set name$i >>redis.log
done
每次插入的值都是helloworld,但鍵不同,name0,name1...name99999。
Redis pipe
Redis pipe會稍微麻煩一點
1> 首先構造redis命令的文本文件
在這里,我選用了python
#!/usr/bin/python
for i in range(100000):
print 'set name'+str(i),'helloworld'
# python 1.py > redis_commands.txt
# head -2 redis_commands.txt
set name0 helloworld
set name1 helloworld
2> 將這些命令轉化成Redis Protocol
在這里,我利用了github上一個shell腳本,
#!/bin/bash
while read CMD; do
# each command begins with *{number arguments in command}\r\n
XS=($CMD); printf "*${#XS[@]}\r\n"
# for each argument, we append ${length}\r\n{argument}\r\n
for X in $CMD; do printf "\$${#X}\r\n$X\r\n"; done
done < redis_commands.txt
# sh 20.sh > redis_data.txt
# head -7 redis_data.txt
*3
$3
set
$5
name0
$10
helloworld
至此,數據構造完畢。
測試結果

『捌』 Redis的內存優化

一. redisObject對象
二. 縮減鍵值對象
三. 共享對象池
四. 字元串優化
五. 編碼優化
六. 控制key的數量

Redis存儲的所有值對象在內部定義為redisObject結構體,內部結構如下圖所示。

表示當前對象使用的數據類型,Redis主要支持5種數據類型:string,hash,list,set,zset。可以使用type {key}命令查純茄答看對象所屬類型,type命令返回的是值對象類型,鍵都是string類型。

表示Redis內部編碼類型,encoding在Redis內部使用,代表當前對象內部採用哪種數據結構實現。理解Redis內部編碼方式對於優化內存非常重要 ,同一個對象採用不同的編碼實現內存佔用存在明顯差異,具體細節見之後編碼優化部分。

記錄對象最後一次被訪問的時間,當配置了 maxmemory和maxmemory-policy=volatile-lru | allkeys-lru 時, 用於輔助LRU演算法刪除鍵數據。可以使用object idletime {key}命令在不更新lru欄位情況下查看當前鍵的空閑時間。

記錄當前對象被引用的次數,用於通過引用次數回收內存,當refcount=0時,可以安全回收當前對象空間。使用object refcount {key}獲取當前對象引用。當對象為整數且范圍在[0-9999]時,Redis可以使用共享對象的方式來節省內存。具體細節見之後共享對象池部分。

與對象的數據內容相關,如果是整數直接存儲數據,否則表示指向數據的指針。Redis在3.0之後對值對象是字元納爛串且長度<=39位元組的數據,內部編碼為embstr類型,字元串sds和redisObject一起分配,從而只要一次內存操作。

降低Redis內存使用最直接的方式就是縮減鍵(key)和值(value)的長度。

其中java-built-in-serializer表示JAVA內置序列化方式,更多數據見jvm-serializers項目: https://github.com/eishay/jvm-serializers/wiki,其它語言也有各自對應的高效序列化工具。

值對象除了存儲二進制數據之外,通常還會使用通用格式存儲數據比如:json,xml等作為字元串存儲在Redis中。這種方式優點是方便調試和跨語言,但是同樣的數據相比位元組數組所需的空間更大,在內存緊張的情況下,可以使用通用壓縮演算法壓縮json,xml後再存入Redis,從而降低內存佔用,例如使用GZIP壓縮後的json可降低約60%的空間。

對象共享池指Redis內部維護[0-9999]的整數對象池。創建大量的整數類型redisObject存在內存開銷,每個redisObject內部結構至少佔16位元組,甚至超過了整數自身空間消耗。所以Redis內存維護一個[0-9999]的整數對象池,用於節約內存。 除了整數值對象,其他類型如list,hash,set,zset內部元素也可以使用整數對象池。因此開發中在滿足需求的前提下,盡量使用整數對象以節省內存。
整數對象池在Redis中通過變數REDIS_SHARED_INTEGERS定義,不能通過配置修改。可以通過object refcount 命令查看對象引用數驗證是否啟用整數對象池技術,如下:

設置鍵foo等於100時,直接使用共享池內整數對象,因此引用數是2,再設置鍵bar等於100時,引用數又變為3,如下圖所示。

使用整數對象池究竟做慧能降低多少內存?讓我們通過測試來對比對象池的內存優化效果,如下表所示。

使用共享對象池後,相同的數據內存使用降低30%以上。可見當數據大量使用[0-9999]的整數時,共享對象池可以節約大量內存。需要注意的是對象池並不是只要存儲[0-9999]的整數就可以工作。當設置maxmemory並啟用LRU相關淘汰策略如:volatile-lru,allkeys-lru時,Redis禁止使用共享對象池,測試命令如下:

LRU演算法需要獲取對象最後被訪問時間,以便淘汰最長未訪問數據,每個對象最後訪問時間存儲在redisObject對象的lru欄位。對象共享意味著多個引用共享同一個redisObject,這時lru欄位也會被共享,導致無法獲取每個對象的最後訪問時間。如果沒有設置maxmemory,直到內存被用盡Redis也不會觸發內存回收,所以共享對象池可以正常工作。

綜上所述,共享對象池與maxmemory+LRU策略沖突,使用時需要注意。 對於ziplist編碼的值對象,即使內部數據為整數也無法使用共享對象池,因為ziplist使用壓縮且內存連續的結構,對象共享判斷成本過高,ziplist編碼細節後面內容詳細說明。

首先整數對象池復用的幾率最大,其次對象共享的一個關鍵操作就是判斷相等性,Redis之所以只有整數對象池,是因為整數比較演算法時間復雜度為O(1),只保留一萬個整數為了防止對象池浪費。如果是字元串判斷相等性,時間復雜度變為O(n),特別是長字元串更消耗性能(浮點數在Redis內部使用字元串存儲)。對於更復雜的數據結構如hash,list等,相等性判斷需要O(n2)。對於單線程的Redis來說,這樣的開銷顯然不合理,因此Redis只保留整數共享對象池。

字元串對象是Redis內部最常用的數據類型。所有的鍵都是字元串類型, 值對象數據除了整數之外都使用字元串存儲。比如執行命令:lpush cache:type 「redis」 「memcache」 「tair」 「levelDB」 ,Redis首先創建」cache:type」鍵字元串,然後創建鏈表對象,鏈表對象內再包含四個字元串對象,排除Redis內部用到的字元串對象之外至少創建5個字元串對象。可見字元串對象在Redis內部使用非常廣泛,因此深刻理解Redis字元串對於內存優化非常有幫助:

Redis沒有採用原生C語言的字元串類型而是自己實現了字元串結構,內部簡單動態字元串(simple dynamic string),簡稱SDS。結構下圖所示。

Redis自身實現的字元串結構有如下特點:

因為字元串(SDS)存在預分配機制,日常開發中要小心預分配帶來的內存浪費,例如下表的測試用例。

從測試數據可以看出,同樣的數據追加後內存消耗非常嚴重,下面我們結合圖來分析這一現象。階段1每個字元串對象空間佔用如下圖所示。

階段1插入新的字元串後,free欄位保留空間為0,總佔用空間=實際佔用空間+1位元組,最後1位元組保存『\0』標示結尾,這里忽略int類型len和free欄位消耗的8位元組。在階段1原有字元串上追加60位元組數據空間佔用如下圖所示。

追加操作後字元串對象預分配了一倍容量作為預留空間,而且大量追加操作需要內存重新分配,造成內存碎片率(mem_fragmentation_ratio)上升。直接插入與階段2相同數據的空間佔用,如下圖所示。

階段3直接插入同等數據後,相比階段2節省了每個字元串對象預分配的空間,同時降低了碎片率。
字元串之所以採用預分配的方式是防止修改操作需要不斷重分配內存和位元組數據拷貝。但同樣也會造成內存的浪費。字元串預分配每次並不都是翻倍擴容,空間預分配規則如下:

字元串重構:指不一定把每份數據作為字元串整體存儲,像json這樣的數據可以使用hash結構,使用二級結構存儲也能幫我們節省內存。同時可以使用hmget,hmset命令支持欄位的部分讀取修改,而不用每次整體存取。例如下面的json數據:

分別使用字元串和hash結構測試內存表現,如下表所示。

根據測試結構,第一次默認配置下使用hash類型,內存消耗不但沒有降低反而比字元串存儲多出2倍,而調整hash-max-ziplist-value=66之後內存降低為535.60M。因為json的videoAlbumPic屬性長度是65,而hash-max-ziplist-value默認值是64,Redis採用hashtable編碼方式,反而消耗了大量內存。調整配置後hash類型內部編碼方式變為ziplist,相比字元串更省內存且支持屬性的部分操作。下一節將具體介紹ziplist編碼優化細節。

Redis對外提供了string,list,hash,set,zet等類型,但是Redis內部針對不同類型存在編碼的概念,所謂編碼就是具體使用哪種底層數據結構來實現。編碼不同將直接影響數據的內存佔用和讀寫效率。使用object encoding {key}命令獲取編碼類型。如下:

Redis針對每種數據類型(type)可以採用至少兩種編碼方式來實現,下表表示type和encoding的對應關系。

了解編碼和類型對應關系之後,我們不禁疑惑Redis為什麼需要對一種數據結構實現多種編碼方式?
主要原因是Redis作者想通過不同編碼實現效率和空間的平衡。比如當我們的存儲只有10個元素的列表,當使用雙向鏈表數據結構時,必然需要維護大量的內部欄位如每個元素需要:前置指針,後置指針,數據指針等,造成空間浪費,如果採用連續內存結構的壓縮列表(ziplist),將會節省大量內存,而由於數據長度較小,存取操作時間復雜度即使為O(n2)性能也可滿足需求。

Redis內存優化

編碼類型轉換在Redis寫入數據時自動完成,這個轉換過程是不可逆的,轉換規則只能從小內存編碼向大內存編碼轉換。例如:

以上命令體現了list類型編碼的轉換過程,其中Redis之所以不支持編碼回退,主要是數據增刪頻繁時,數據向壓縮編碼轉換非常消耗CPU,得不償失。以上示例用到了list-max-ziplist-entries參數,這個參數用來決定列表長度在多少范圍內使用ziplist編碼。當然還有其它參數控制各種數據類型的編碼,如下表所示:

掌握編碼轉換機制,對我們通過編碼來優化內存使用非常有幫助。下面以hash類型為例,介紹編碼轉換的運行流程,如下圖所示。

理解編碼轉換流程和相關配置之後,可以使用config set命令設置編碼相關參數來滿足使用壓縮編碼的條件。對於已經採用非壓縮編碼類型的數據如hashtable,linkedlist等,設置參數後即使數據滿足壓縮編碼條件,Redis也不會做轉換,需要重啟Redis重新載入數據才能完成轉換。

ziplist編碼主要目的是為了節約內存,因此所有數據都是採用線性連續的內存結構。ziplist編碼是應用范圍最廣的一種,可以分別作為hash、list、zset類型的底層數據結構實現。首先從ziplist編碼結構開始分析,它的內部結構類似這樣:<….>。一個ziplist可以包含多個entry(元素),每個entry保存具體的數據(整數或者位元組數組),內部結構如下圖所示。

ziplist結構欄位含義:

根據以上對ziplist欄位說明,可以分析出該數據結構特點如下:

下面通過測試展示ziplist編碼在不同類型中內存和速度的表現,如下表所示。

測試數據採用100W個36位元組數據,劃分為1000個鍵,每個類型長度統一為1000。從測試結果可以看出:

intset編碼是集合(set)類型編碼的一種,內部表現為存儲有序,不重復的整數集。當集合只包含整數且長度不超過set-max-intset-entries配置時被啟用。執行以下命令查看intset表現:

以上命令可以看出intset對寫入整數進行排序,通過O(log(n))時間復雜度實現查找和去重操作,intset編碼結構如下圖所示。

intset的欄位結構含義:

根據以上測試結果發現intset表現非常好,同樣的數據內存佔用只有不到hashtable編碼的十分之一。intset數據結構插入命令復雜度為O(n),查詢命令為O(log(n)),由於整數佔用空間非常小,所以在集合長度可控的基礎上,寫入命令執行速度也會非常快,因此當使用整數集合時盡量使用intset編碼。上表測試第三行把ziplist-hash類型也放入其中,主要因為intset編碼必須存儲整數,當集合內保存非整數數據時,無法使用intset實現內存優化。這時可以使用ziplist-hash類型對象模擬集合類型,hash的field當作集合中的元素,value設置為1位元組佔位符即可。使用ziplist編碼的hash類型依然比使用hashtable編碼的集合節省大量內存。

當使用Redis存儲大量數據時,通常會存在大量鍵,過多的鍵同樣會消耗大量內存。Redis本質是一個數據結構伺服器,它為我們提供多種數據結構,如hash,list,set,zset 等結構。使用Redis時不要進入一個誤區,大量使用get/set這樣的API,把Redis當成Memcached使用。對於存儲相同的數據內容利用Redis的數據結構降低外層鍵的數量,也可以節省大量內存。如下圖所示,通過在客戶端預估鍵規模,把大量鍵分組映射到多個hash結構中降低鍵的數量。

hash結構降低鍵數量分析:

通過這個測試數據,可以說明:

關於hash鍵和field鍵的設計:

使用hash結構控制鍵的規模雖然可以大幅降低內存,但同樣會帶來問題,需要提前做好規避處理。如下:

本文主要講解Redis內存優化技巧,Redis的數據特性是」ALL IN MEMORY」,優化內存將變得非常重要。對於內存優化建議讀者先要掌握Redis內存存儲的特性比如字元串,壓縮編碼,整數集合等,再根據數據規模和所用命令需求去調整,從而達到空間和效率的最佳平衡。建議使用Redis存儲大量數據時,把內存優化環節加入到前期設計階段,否則數據大幅增長後,開發人員需要面對重新優化內存所帶來開發和數據遷移的雙重成本。當Redis內存不足時,首先考慮的問題不是加機器做水平擴展,應該先嘗試做內存優化。當遇到瓶頸時,再去考慮水平擴展。即使對於集群化方案,垂直層面優化也同樣重要,避免不必要的資源浪費和集群化後的管理成本。

『玖』 redis原理,單線程怎麼做到高並發的

但線程,只能靠單個處理器速度,內存速度,蠢毀處理器上的緩存速度,匯流排傳輸速度。餘下的是你的網路IO。但線程高並發完全依賴程序的運行速度。redis這種東西肯定不是但線程的。一個連接就是一個線帶歷備程,你這樣理解應該不準爛拿確。

閱讀全文

與redis如何成為高效的程序員相關的資料

熱點內容
光儲存技術用於哪些領域 瀏覽:860
深圳怡寶總代理怎麼樣 瀏覽:418
c程序輸出如何四行星號 瀏覽:659
實型數據關鍵字有哪些 瀏覽:735
aiot上哪些交易所 瀏覽:245
奶茶店有什麼可以代理的 瀏覽:296
技術大牛需要什麼條件 瀏覽:379
東莞黃江哪個市場比較旺 瀏覽:146
m4運動手環怎麼信息推送 瀏覽:721
虛擬貨幣交易的錢怎麼提出來 瀏覽:785
北方人才市場是什麼性質的單位 瀏覽:414
劍魂怎麼交易元寶 瀏覽:164
鎮賚縣南市場是哪個社區 瀏覽:37
股市交易之前如何入市 瀏覽:374
買產品送股權有什麼好處 瀏覽:367
從哪裡可以學到股票交易 瀏覽:216
繪本代理商怎麼做 瀏覽:539
醫院糾紛法律程序走了怎麼辦 瀏覽:22
微信有未讀信息怎麼生成 瀏覽:144
神魔大陸手游怎麼交易 瀏覽:794