高效能資料庫Redis在Windows上的配置

Sunnier發表於2014-12-08

Redis簡介

redis是一個key-value儲存系統。和Memcached類似,它支援儲存的value型別相對更多,包括string(字串)、list(連結串列)、set(集合)、zset(sorted set –有序集合)和hash(雜湊型別)。這些資料型別都支援push/pop、add/remove及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,redis支援各種不同方式的排序。與memcached一樣,為了保證效率,資料都是快取在記憶體中。區別的是redis會週期性的把更新的資料寫入磁碟或者把修改操作寫入追加的記錄檔案,並且在此基礎上實現了master-slave(主從)同步。Redis一般部署在linux伺服器上,但是我們也可以在windows上學習他的安裝和配置。

Redis效能

下面是官方的bench-mark資料:
測試完成了50個併發執行100000個請求。
設定和獲取的值是一個256位元組字串。
Linux box是執行Linux 2.6,這是X3320 Xeon 2.5 ghz。
文字執行使用loopback介面(127.0.0.1)。
結果:讀的速度是110000次/s,寫的速度是81000次/s 。

Redis在windows上的安裝和配置

首先下載Redis完整包。

Redis完整包下載

解壓下載後的完整包,開啟MasterRedis資料夾(此資料夾為主Redis資料庫資料夾,後面會介紹)。

redis.conf檔案為Redis服務的配置檔案,可以根據需要進行配置。

下面是redis.conf的主要配置引數的意義:
daemonize:是否以後臺daemon方式執行
pidfile:pid檔案位置
port:監聽的埠號
timeout:請求超時時間
loglevel:log資訊級別
logfile:log檔案位置
databases:開啟資料庫的數量
save * *:儲存快照的頻率,第一個*表示多長時間,第二個*表示執行多少次寫操作。在一定時間內執行一定數量的寫操作時,自動儲存快照。可設定多個條件。
rdbcompression:是否使用壓縮
dbfilename:資料快照檔名(只是檔名,不包括目錄)
dir:資料快照的儲存目錄(這個是目錄)
appendonly:是否開啟appendonlylog,開啟的話每次寫操作會記一條log,這會提高資料抗風險能力,但影響效率。
appendfsync:appendonlylog如何同步到磁碟(三個選項,分別是每次寫都強制呼叫fsync、每秒啟用一次fsync、不呼叫fsync等待系統自己同步)

下面給出配置檔案的詳細說明:

daemonize  no

預設情況下,redis 不是在後臺執行的,如果需要在後臺執行,把該項的值更改為yes。
pidfile  /var/run/redis.pid

當Redis 在後臺執行的時候,Redis 預設會把pid 檔案放在/var/run/redis.pid,你可以配置到其他地址。當執行多個redis 服務時,需要指定不同的pid 檔案和埠
port

監聽埠,預設為6379
#bind 127.0.0.1

指定Redis 只接收來自於該IP 地址的請求,如果不進行設定,那麼將處理所有請求,在生產環境中為了安全最好設定該項。預設註釋掉,不開啟
timeout 0

設定客戶端連線時的超時時間,單位為秒。當客戶端在這段時間內沒有發出任何指令,那麼關閉該連線
tcp-keepalive 0

指定TCP連線是否為長連線,"偵探"訊號有server端維護。預設為0.表示禁用
loglevel notice

log 等級分為4 級,debug,verbose, notice, 和warning。生產環境下一般開啟notice
logfile stdout

配置log 檔案地址,預設使用標準輸出,即列印在命令列終端的視窗上,修改為日誌檔案目錄
databases 16

設定資料庫的個數,可以使用SELECT 命令來切換資料庫。預設使用的資料庫是0號庫。預設16個庫

save 900 1
save 300 10
save 60 10000

儲存資料快照的頻率,即將資料持久化到dump.rdb檔案中的頻度。用來描述"在多少秒期間至少多少個變更操作"觸發snapshot資料儲存動作

預設設定,意思是:

if(在60 秒之內有10000 個keys 發生變化時){

進行映象備份

}else if(在300 秒之內有10 個keys 發生了變化){

進行映象備份

}else if(在900 秒之內有1 個keys 發生了變化){

進行映象備份

}
stop-writes-on-bgsave-error yes

當持久化出現錯誤時,是否依然繼續進行工作,是否終止所有的客戶端write請求。預設設定"yes"表示終止,一旦snapshot資料儲存故障,那麼此server為只讀服務。如果為"no",那麼此次snapshot將失敗,但下一次snapshot不會受到影響,不過如果出現故障,資料只能恢復到"最近一個成功點"
rdbcompression yes

在進行資料映象備份時,是否啟用rdb檔案壓縮手段,預設為yes。壓縮可能需要額外的cpu開支,不過這能夠有效的減小rdb檔案的大,有利於儲存/備份/傳輸/資料恢復
rdbchecksum yes

讀取和寫入時候,會損失10%效能
rdbchecksum yes

是否進行校驗和,是否對rdb檔案使用CRC64校驗和,預設為"yes",那麼每個rdb檔案內容的末尾都會追加CRC校驗和,利於第三方校驗工具檢測檔案完整性
dbfilename dump.rdb

映象備份檔案的檔名,預設為 dump.rdb
dir ./

資料庫映象備份的檔案rdb/AOF檔案放置的路徑。這裡的路徑跟檔名要分開配置是因為Redis 在進行備份時,先會將當前資料庫的狀態寫入到一個臨時檔案中,等備份完成時,再把該臨時檔案替換為上面所指定的檔案,而這裡的臨時檔案和上面所配置的備份檔案都會放在這個指定的路徑當中
# slaveof <masterip> <masterport>

設定該資料庫為其他資料庫的從資料庫,併為其指定master資訊。
masterauth

當主資料庫連線需要密碼驗證時,在這裡指定
slave-serve-stale-data yes

當主master伺服器掛機或主從複製在進行時,是否依然可以允許客戶訪問可能過期的資料。在"yes"情況下,slave繼續向客戶端提供只讀服務,有可能此時的資料已經過期;在"no"情況下,任何向此server傳送的資料請求服務(包括客戶端和此server的slave)都將被告知"error"
slave-read-only yes

slave是否為"只讀",強烈建議為"yes"
# repl-ping-slave-period 10

slave向指定的master傳送ping訊息的時間間隔(秒),預設為10
# repl-timeout 60

slave與master通訊中,最大空閒時間,預設60秒.超時將導致連線關閉
repl-disable-tcp-nodelay no

slave與master的連線,是否禁用TCP nodelay選項。"yes"表示禁用,那麼socket通訊中資料將會以packet方式傳送(packet大小受到socket buffer限制)。

可以提高socket通訊的效率(tcp互動次數),但是小資料將會被buffer,不會被立即傳送,對於接受者可能存在延遲。"no"表示開啟tcp nodelay選項,任何資料都會被立即傳送,及時性較好,但是效率較低,建議設為no
slave-priority 100

適用Sentinel模組(unstable,M-S叢集管理和監控),需要額外的配置檔案支援。slave的權重值,預設100.當master失效後,Sentinel將會從slave列表中找到權重值最低(>0)的slave,並提升為master。如果權重值為0,表示此slave為"觀察者",不參與master選舉
# requirepass foobared

設定客戶端連線後進行任何其他指定前需要使用的密碼。警告:因為redis 速度相當快,所以在一臺比較好的伺服器下,一個外部的使用者可以在一秒鐘進行150K 次的密碼嘗試,這意味著你需要指定非常非常強大的密碼來防止暴力破解。
# rename-command CONFIG 3ed984507a5dcd722aeade310065ce5d    (方式:MD5('CONFIG^!'))

重新命名指令,對於一些與"server"控制有關的指令,可能不希望遠端客戶端(非管理員使用者)連結隨意使用,那麼就可以把這些指令重新命名為"難以閱讀"的其他字串
# maxclients 10000

限制同時連線的客戶數量。當連線數超過這個值時,redis 將不再接收其他連線請求,客戶端嘗試連線時將收到error 資訊。預設為10000,要考慮系統檔案描述符限制,不宜過大,浪費檔案描述符,具體多少根據具體情況而定
# maxmemory <bytes>

redis-cache所能使用的最大記憶體(bytes),預設為0,表示"無限制",最終由OS實體記憶體大小決定(如果實體記憶體不足,有可能會使用swap)。此值儘量不要超過機器的實體記憶體尺寸,從效能和實施的角度考慮,可以為實體記憶體3/4。此配置需要和"maxmemory-policy"配合使用,當redis中記憶體資料達到maxmemory時,觸發"清除策略"。在"記憶體不足"時,任何write操作(比如set,lpush等)都會觸發"清除策略"的執行。在實際環境中,建議redis的所有物理機器的硬體配置保持一致(記憶體一致),同時確保master/slave中"maxmemory""policy"配置一致。

當記憶體滿了的時候,如果還接收到set 命令,redis 將先嚐試剔除設定過expire 資訊的key,而不管該key 的過期時間還沒有到達。在刪除時,

將按照過期時間進行刪除,最早將要被過期的key 將最先被刪除。如果帶有expire 資訊的key 都刪光了,記憶體還不夠用,那麼將返回錯誤。這樣,redis 將不再接收寫請求,只接收get 請求。maxmemory 的設定比較適合於把redis 當作於類似memcached的快取來使用。
# maxmemory-policy volatile-lru

記憶體不足"時,資料清除策略,預設為"volatile-lru"。

volatile-lru  ->對"過期集合"中的資料採取LRU(近期最少使用)演算法.如果對key使用"expire"指令指定了過期時間,那麼此key將會被新增到"過期集合"中。將已經過期/LRU的資料優先移除.如果"過期集合"中全部移除仍不能滿足記憶體需求,將OOM.
allkeys-lru ->對所有的資料,採用LRU演算法
volatile-random ->對"過期集合"中的資料採取"隨即選取"演算法,並移除選中的K-V,直到"記憶體足夠"為止. 如果如果"過期集合"中全部移除全部移除仍不能滿足,將OOM
allkeys-random ->對所有的資料,採取"隨機選取"演算法,並移除選中的K-V,直到"記憶體足夠"為止
volatile-ttl ->對"過期集合"中的資料採取TTL演算法(最小存活時間),移除即將過期的資料.
noeviction ->不做任何干擾操作,直接返回OOM異常
另外,如果資料的過期不會對"應用系統"帶來異常,且系統中write操作比較密集,建議採取"allkeys-lru"
# maxmemory-samples 3

預設值3,上面LRU和最小TTL策略並非嚴謹的策略,而是大約估算的方式,因此可以選擇取樣值以便檢查
appendonly no

預設情況下,redis 會在後臺非同步的把資料庫映象備份到磁碟,但是該備份是非常耗時的,而且備份也不能很頻繁。所以redis 提供了另外一種更加高效的資料庫備份及災難恢復方式。開啟append only 模式之後,redis 會把所接收到的每一次寫操作請求都追加到appendonly.aof 檔案中,當redis 重新啟動時,會從該檔案恢復出之前的狀態。但是這樣會造成appendonly.aof 檔案過大,所以redis 還支援了BGREWRITEAOF 指令,對appendonly.aof 進行重新整理。如果不經常進行資料遷移操作,推薦生產環境下的做法為關閉映象,開啟appendonly.aof,同時可以選擇在訪問較少的時間每天對appendonly.aof 進行重寫一次。

另外,對master機器,主要負責寫,建議使用AOF,對於slave,主要負責讀,挑選出1-2臺開啟AOF,其餘的建議關閉
# appendfilename appendonly.aof

aof檔名字,預設為appendonly.aof

# appendfsync always
appendfsync everysec
# appendfsync no

設定對appendonly.aof 檔案進行同步的頻率。always 表示每次有寫操作都進行同步,everysec 表示對寫操作進行累積,每秒同步一次。no不主動fsync,由OS自己來完成。這個需要根據實際業務場景進行配置
no-appendfsync-on-rewrite no

在aof rewrite期間,是否對aof新記錄的append暫緩使用檔案同步策略,主要考慮磁碟IO開支和請求阻塞時間。預設為no,表示"不暫緩",新的aof記錄仍然會被立即同步
auto-aof-rewrite-percentage 100

當Aof log增長超過指定比例時,重寫log file, 設定為0表示不自動重寫Aof 日誌,重寫是為了使aof體積保持最小,而確保儲存最完整的資料。
auto-aof-rewrite-min-size 64mb

觸發aof rewrite的最小檔案尺寸
lua-time-limit 5000

lua指令碼執行的最大時間
slowlog-log-slower-than 10000

"慢操作日誌"記錄,單位:微秒(百萬分之一秒,1000 * 1000),如果操作時間超過此值,將會把command資訊"記錄"起來.(記憶體,非檔案)。其中"操作時間"不包括網路IO開支,只包括請求達到server後進行"記憶體實施"的時間."0"表示記錄全部操作
slowlog-max-len 128

"慢操作日誌"保留的最大條數,"記錄"將會被佇列化,如果超過了此長度,舊記錄將會被移除。可以通過"SLOWLOG <subcommand> args"檢視慢記錄的資訊(SLOWLOG get 10,SLOWLOG reset)

 hash-max-ziplist-entries 512

hash型別的資料結構在編碼上可以使用ziplist和hashtable。ziplist的特點就是檔案儲存(以及記憶體儲存)所需的空間較小,在內容較小時,效能和hashtable幾乎一樣.因此redis對hash型別預設採取ziplist。如果hash中條目的條目個數或者value長度達到閥值,將會被重構為hashtable。

這個引數指的是ziplist中允許儲存的最大條目個數,,預設為512,建議為128
hash-max-ziplist-value 64

ziplist中允許條目value值最大位元組數,預設為64,建議為1024

list-max-ziplist-entries 512
list-max-ziplist-value 64

對於list型別,將會採取ziplist,linkedlist兩種編碼型別。解釋同上。
set-max-intset-entries 512

intset中允許儲存的最大條目個數,如果達到閥值,intset將會被重構為hashtable

zset-max-ziplist-entries 128
zset-max-ziplist-value 64

zset為有序集合,有2中編碼型別:ziplist,skiplist。因為"排序"將會消耗額外的效能,當zset中資料較多時,將會被重構為skiplist。
activerehashing yes

是否開啟頂層資料結構的rehash功能,如果記憶體允許,請開啟。rehash能夠很大程度上提高K-V存取的效率

client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60

客戶端buffer控制。在客戶端與server進行的互動中,每個連線都會與一個buffer關聯,此buffer用來佇列化等待被client接受的響應資訊。如果client不能及時的消費響應資訊,那麼buffer將會被不斷積壓而給server帶來記憶體壓力.如果buffer中積壓的資料達到閥值,將會導致連線被關閉,buffer被移除。

buffer控制型別包括:normal -> 普通連線;slave ->與slave之間的連線;pubsub ->pub/sub型別連線,此型別的連線,往往會產生此種問題;因為pub端會密集的釋出訊息,但是sub端可能消費不足.
指令格式:client-output-buffer-limit <class> <hard> <soft> <seconds>",其中hard表示buffer最大值,一旦達到閥值將立即關閉連線;
soft表示"容忍值",它和seconds配合,如果buffer值超過soft且持續時間達到了seconds,也將立即關閉連線,如果超過了soft但是在seconds之後,buffer資料小於了soft,連線將會被保留.
其中hard和soft都設定為0,則表示禁用buffer控制.通常hard值大於soft.

hz 10

Redis server執行後臺任務的頻率,預設為10,此值越大表示redis對"間歇性task"的執行次數越頻繁(次數/秒)。"間歇性task"包括"過期集合"檢測、關閉"空閒超時"的連線等,此值必須大於0且小於500。此值過小就意味著更多的cpu週期消耗,後臺task被輪詢的次數更頻繁。此值過大意味著"記憶體敏感"性較差。建議採用預設值。

# include /path/to/local.conf
# include /path/to/other.conf

額外載入配置檔案。

點選展開說明

開啟Redis服務

開啟命令列進入MasterRedis資料夾目錄。比如我下載的資料夾路徑為 E:\Redis\RedisFull,就在命令列輸入:

接著輸入:

此時會看到程式裡多了一個

說明Redis服務開啟成功。

Redis主從服務簡介

Redis主從服務原理:

在Slave啟動並連線到Master之後,它將主動傳送一個SYNC命令。此後Master將啟動後臺存檔程式,同時收集所有接收到的用於修改資料集的命令,在後臺程式執行完畢後,Master將傳送整個資料庫檔案到Slave,以完成一次完全同步。而Slave伺服器在接收到資料庫檔案資料之後將其存檔並載入到記憶體中。此後,Master繼續將所有已經收集到的修改命令,和新的修改命令依次傳送給Slaves,Slave將在本次執行這些資料修改命令,從而達到最終的資料同步。如果Master和Slave之間的連結出現斷連現象,Slave可以自動重連Master,但是在連線成功之後,一次完全同步將被自動執行。

Redis主從服務注意事項:

1). 同一個Master可以同步多個Slaves。
2). Slave同樣可以接受其它Slaves的連線和同步請求,這樣可以有效的分載Master的同步壓力。
3). Master Server是以非阻塞的方式為Slaves提供服務,所以在Master-Slave同步期間,客戶端仍然可以提交查詢或修改請求。
4). Slave Server同樣是以非阻塞的方式完成資料同步。在同步期間,如果有客戶端提交查詢請求,Redis則返回同步之前的資料。
5). 為了分載Master的讀操作壓力,Slave伺服器可以為客戶端提供只讀操作的服務,寫服務仍然必須由Master來完成。
6). Master可以將資料儲存操作交給Slaves完成,從而避免了在Master中要有獨立的程式來完成此操作。

Redis主從服務配置

開啟SlaveRedis資料夾,修改redis.conf檔案的port項,修改值任意,但是不能和主服務的port相同(下載的完整包中我已經修改過),bind項改為主伺服器的IP(本地測試改為127.0.0.1),slaveof項改為127.0.0.1 6379(已改)。

開啟命令列輸入命令:

會發現程式中有2個redis-server.exe程式。

Redis主從同步C#程式碼測試

首先下載Redis的C#元件,用於操作Redis資料庫。

Redis4C#包下載

新建一個控制檯專案。

新增Redis4C#包內3個程式集的引用。

新增測試程式碼。

 1        var client = new RedisClient("127.0.0.1", 6379);
 2             client.Set<string>("6379_1", "test");
 3             client.Set<string>("6379_2", "test");
 4  5             string name = client.Get<string>("6379");
 6             Console.WriteLine(name);
 7 
 8             //Redis佇列
 9             client.EnqueueItemOnList("jayjay", "12345");
10             client.EnqueueItemOnList("jayjay", "12345");
11 12 
13             int length1 = client.GetListCount("jayjay");
14             for (int i = 0; i < length1; i++)
15             {
16                 Console.WriteLine(client.DequeueItemFromList("jayjay"));
17             }
18 
19 
20             //Redis壓棧出棧
21             client.PushItemToList("name", "jayjay6379");
22             client.PushItemToList("name", "jayjay6379");
23 
24             int length2 = client.GetListCount("name");
25             for (int i = 0; i < length2; i++)
26             {
27                 Console.WriteLine(client.PopItemFromList("name"));
28             }
29 
30             Console.ReadKey();

執行程式碼。

將主服務埠6379改為從服務埠6381,讀出剛剛放入的資料,例如:

1             var client = new RedisClient("127.0.0.1", 6381);
2             string name = client.Get<string>("6379_1");
3             Console.WriteLine(name);

能讀到說明主服務儲存的資料已經同步到從服務中。

1.Redis資料夾路徑中不能出現中文(命令列採用的是ANSIC編碼,中文會出錯)!

2.dump.rdb是資料快照檔案,主服務的資料快照並不會立即同步到從服務的資料快照中!

相關文章