Redis 生產環境部署優化

爲了讓Redis在生產環境中發揮更高的性能,通常我們都會做一些優化配置。

一、優化內存相關參數

這裏使用快照還原到單機版redis。先查看下  /etc/sysctl.conf 文件。

     

注意:編輯 /etc/sysctl.conf 文件,添加 相關參數之後,必須使用  # sysctl -p 使新的參數生效。 

什麼是 Overcommit 和 OOM?

Linux的內存分配採取的是一種更加積極的分配策略,所以,Linux對大部分申請內存的請求都回復"yes",以便能跑更多更大的程序。因爲申請內存後,並不會馬上使用內存。這種技術叫做Overcommit。

當Linux發現內存不足時,會發生OOM killer(OOM=out-of-memory)。它會選擇殺死一些進程(用戶態進程,不是內核線程),以便釋放內存。

當oom-killer發生時,Linux會選擇殺死哪些進程?選擇進程的函數是oom_badness函數(在mm/oom_kill.c中),該函數會計算每個進程的點數(0~1000)。點數越高,這個進程越有可能被殺死。每個進程的點數跟oom_score_adj有關,而且oom_score_adj可以被設置(-1000最低,1000最高)。

1、設置 vm.overcommit_memory=1

編輯 /etc/sysctl.conf文件,在後面添加 vm.overcommit_memory=1   

使用 # sysctl -p 使新的參數生效。查看:# sysctl vm.overcommit_memory

       

overcommit_memory是一個內核對內存分配的一種內存分配策略,可選值:0、1、2。

0(默認值): 表示內核將檢查是否有足夠的可用內存供應用進程使用;如果有足夠的可用內存,內存申請允許;否則,內存申請失敗,並把錯誤返回給應用進程。

1(允許):表示內核允許分配所有的物理內存,而不管當前的內存狀態如何。當然這個也不是無限大,還受到尋址空間的限制,32位系統最大可能只有4G,64位系統大概16T左右。

2(允許但禁止超售):表示內核允許分配超過所有物理內存和交換空間總和的內存。系統能夠分配的內存不會超過 swap+實際物理內存*overcommit_ratio,該值可以通過 /proc/sys/vm/overcommit_ratio設置,默認50%。

分析:

在Redis的持久化時,這些數據就會寫入硬盤,如果OverCommit=0默認值時,它會檢查是否有足夠的空閒內存空間來複制父進程的所有內存頁,允許輕微的超售,相差太多就會報錯(OOM,Out Of Memery),所有我們需要改爲1。

2、設置 vm.swappiness=0  

編輯 /etc/sysctl.conf文件,在後面添加 vm.swappiness=0

使用 # sysctl -p 使新的參數生效。查看:# sysctl vm.swappiness

       

 使用 free -m 命令可查看內存使用情況和swap的大小:

      

分析:

swap分區的作用簡單理解:

swap 分區通常被稱爲交換分區,這是一塊特殊的硬盤空間,即當實際內存不夠用的時候,操作系統會從內存中取出一部分暫時不用的數據,放在交換分區中,從而爲當前運行的程序騰出足夠的內存空間。也就是說,當內存不夠用時,我們使用 swap 分區來臨時頂替。這種“拆東牆,補西牆”的方式應用於幾乎所有的操作系統中。

使用 swap 交換分區,顯著的優點是,通過操作系統的調度,應用程序實際可以使用的內存空間將遠遠超過系統的物理內存。由於硬盤空間的價格遠比 RAM 要低,因此這種方式無疑是經濟實惠的。當然,頻繁地讀寫硬盤,會顯著降低操作系統的運行速率,這也是使用 swap 交換分區最大的限制。

swappiness的值的大小對如何使用swap分區是有着很大的聯繫的。

當swappiness爲0的時候表示最大限度使用物理內存,然後纔是 swap空間,

當swappines爲100的時候,則表示積極的使用swap分區,並且把內存上的數據及時的搬運到swap空間裏面。

在 Linux中,可以通過修改swappiness內核參數,降低系統對swap分區的使用,從而提高系統的性能。在CentOS、Red Hat、ubuntu等系統中,swappiness的默認值爲60或者30,所以,修改swappiness=0,這樣Redis的高速處理能力才能更好的發揮。

3、設置transparent_hugepage=never

Linux7 默認是開啓透明大頁功能的。

查看transparent_hugepage:# cat /sys/kernel/mm/transparent_hugepage/enabled

    

編輯 /etc/default/grub 文件,添加 transparent_hugepage=never

    

執行生效命令 # grub2-mkconfig -o /boot/grub2/grub.cfg

[root@centos7 ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-3.10.0-957.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-957.el7.x86_64.img
Found linux image: /boot/vmlinuz-0-rescue-81e66b2a70194a61a16b51fd03e4c665
Found initrd image: /boot/initramfs-0-rescue-81e66b2a70194a61a16b51fd03e4c665.img
done

然後重啓:# reboot   查看transparent_hugepage,檢查是否生效:

    

分析:

內存有一個最小的存儲單位,大多數都是一個字節。內存用內存地址(memory address)來爲每個字節的數據順序編號。因此,內存地址說明了數據在內存中的位置。內存地址從0開始,每次增加1。這種線性增加的存儲器地址稱爲線性地址(linear address)。我們用十六進制數來表示內存地址,比如0x00000003。

內存的一項主要任務,就是存儲進程的相關數據。進程空間中主要的結構有:程序段、全局數據、棧和堆,這些存儲結構在進程運行中所起到的關鍵作用。儘管進程和內存的關係如此緊密,但是,進程並不能直接訪問內存。在Linux下,進程不能直接讀寫內存中地址爲0x1位置的數據。進程中能訪問的地址,只能是虛擬內存地址(virtual memory address)。操作系統會把虛擬內存地址翻譯成真實的內存地址。這種內存管理方式,稱爲虛擬內存(virtual memory)。

每個進程都有自己的一套虛擬內存地址,用來給自己的進程空間編號。進程空間的數據同樣以字節爲單位,依次增加。從功能上說,虛擬內存地址和物理內存地址類似,都是爲數據提供位置索引。進程的虛擬內存地址相互獨立。因此,兩個進程空間可以有相同的虛擬內存地址,如0x10001000。虛擬內存地址和物理內存地址又有一定的對應關係,對進程某個虛擬內存地址的操作,會被CPU翻譯成對某個具體內存地址的操作。虛擬內存地址和物理內存地址的分離,給進程帶來便利性和安全性。但是,虛擬內存地址和物理內存地址的翻譯,又會額外耗費計算機資源。

在多任務的現代計算機中,虛擬內存地址已經成爲必備的設計。那麼,操作系統必須要考慮清楚,如何能高效地翻譯虛擬內存地址。翻譯虛擬內存地址其實就是記錄對應關係,最簡單的辦法,就是把對應關係記錄在一張表中。爲了讓翻譯速度足夠地快,這個表必須加載在內存中。不過,這種記錄方式驚人地浪費內存。因此,Linux採用了分頁(paging)的方式來記錄對應關係。

分頁(paging):就是以更大尺寸的單位頁(page)來管理內存。在Linux中,通常每頁大小爲4KB。

查看頁的大小:# getconf PAGE_SIZE  

[root@centos7 ~]# getconf PAGE_SIZE
4096

Linux下的頁分爲兩種類型:標準大頁(Huge Pages)和透明大頁(Transparent Huge Pages)。

1)標準大頁(Huge Pages)是從Linux Kernel 2.6後被引入的。

    目的是使用更大的內存頁面(memory page size) 以適應越來越大的系統內存讓操作系統可以支持現代硬件架構的大頁面容量功能。

2)透明大頁(Transparent Huge Pages)縮寫爲THP,這個是RHEL 6開始引入的一個功能。

這兩者的區別在於大頁的分配機制,標準大頁管理是預分配的方式,而透明大頁管理則是動態分配的方式。

透明大頁塊(Transparent Huge Pages):可以動態將系統默認內存葉塊4kb,交換爲Huge pages,在這個過程中,對於操作系統的內存的各種分配活動,需要各種內存鎖,直接影響程序的內存訪問性能,所以我們建議關掉它。

 

二、網絡上的優化配置

1、設置net.core.somaxconn=65535

編輯 /etc/sysctl.conf文件,在後面添加:net.core.somaxconn=65535

使用 # sysctl -p 使新的參數生效。查看(默認值爲128):# sysctl net.core.somaxconn

分析:

簡單瞭解下TCP三次握手中 queue的知識:

    

第一次: 客戶端 - - > 服務器

此時服務器知道了客戶端要建立連接了

第二次: 客戶端 < - - 服務器

此時客戶端知道服務器收到連接請求了

第三次: 客戶端 - - > 服務器

此時服務器知道客戶端收到了自己的迴應

到這裏,,就可以認爲客戶端與服務器已經建立了連接。

在握手階段存在兩個隊列:syns queue(半連接隊列)和accept queue(全連接隊列)。

1)客戶端發送SYN給服務端進行第一次報文握手,

    此時服務端將此請求信息放在半連接隊列中並回復SYN+ACK給客戶端。

2)客戶端收到SYN+ACK,隨即發出ACK給服務端;

    此後,服務器收到ACK以後,這裏有兩種情況,如果:

        1. 全隊列未滿:從半連接隊列拿出此消息放入全隊列中。

        2. 全隊列已滿:處理方式和tcp_abort_on_overflow(cat /proc/sys/net/ipv4/tcp_abort_on_overflow)有關:

                      tcp_abort_on_overflow=0;表示丟棄該ACK;

                      tcp_abort_on_overflow=1;表示發送一個RST(重置標記Reset)給客戶端,直接廢棄掉這個握手過程。

   

在Redis配置文件中有一個tcp-backlog,默認值是511, tcp-backlog 就是影響這個accept queue隊列的大小的一個配置。

   

同時,accept queue 隊列的大小也受系統內核的 somaxconn 配置項影響,因爲服務器最終生效的那個值是取它們兩者的最小值。所以,我們必需修改內核的somaxconn值,讓它至少大於511。這裏把 somaxconn設置爲65535,後面我們只需要修改 Redis中的 tcp-backlog 大小即可。

2、設置fs.file-max = 2000000和 ulimit 65535

編輯 /etc/sysctl.conf文件,在後面添加:fs.file-max = 2000000

使用 # sysctl -p 使新的參數生效。查看:# sysctl fs.file-max

      

ulimit 限制一個用戶的所有shell能打開的最大數:

編輯  /etc/security/limits.conf文件,修改 * soft nofile/hard nofile 65535

    

在生產環境的服務器中,一般不用root啓動Redis,肯定會給 Redis配置專有的用戶(不設置密碼保證安全,客戶端不能登錄),所以,在生產環境下需要把 * 改成對應的用戶名,修改以後,需要重啓才能生效。

分析:    

操作系統中涉及到的參數有兩個:max-file 和 ulimit -n

max-file:表示系統級別的能夠打開的文件句柄的數量。是對整個系統的限制,並不是針對用戶的。

ulimit -n:控制進程級別能夠打開的文件句柄的數量。提供對shell及其啓動的進程的可用文件句柄的控制。這是進程級別的。    

對於服務器來說,file-max 和 ulimit都需要設置,否則會出現文件描述符耗盡的問題。

查看 max-file:# sysctl fs.file-max

查看 ulimit(Centos7默認是1024):# ulimit -Hn -Sn

#  未修改之前的默認值
[root@centos7 ~]# sysctl fs.file-max
fs.file-max = 95215
[root@centos7 ~]# ulimit -Hn -Sn
open files                      (-n) 1024
open files                      (-n) 1024

在Redis配置文件中有一個maxclients 配置項,默認值是10000,這裏把操作系統的max-file 和 ulimit -n設置比較大,後面我們只需要修改 Redis中的 maxclients 大小即可。

   

3、瞭解與客戶端相關的幾個重要配置參數

#表示服務器在客戶端空閒N秒後關閉鏈接。 0-表示不關
timeout  0 

#設置掛起套接字請求隊列大小,系統的somaxconn參數密切相關,tcp-backlog的值大小應該比somaxconn的值小,纔有意義。
tcp-backlog 511

#限制了來自客戶端的最大鏈接數,一旦超過了這個配置,客戶端會收到報錯信息
max-clients  10000

#設置爲非0值,服務器會根據配置的時間爲間隔,向網絡設備發送ack信息,保持網絡鏈接的活躍,如果沒收到網絡設備的響應,服務器會關閉對應的客戶端的鏈接。
#默認是300
tcp-keepalive  300

例如:Juniper防火牆在客戶端和它本身之間的連接空閒超過1800秒時會將連接切斷。但是,客戶端和服務器端都不會得到連接已斷開的通知。因此,

從服務器的角度來看,連接仍然是活躍的。如果我們禁用了tcp-keepalive選項,服務器就不會釋放連接。

從客戶端的角度來看,一旦它發現連接斷開就會重新連接到Redis服務器。這樣,會導致連接數持續增加。

所以,爲了避免此類連接問題的發生,保持默認的設置更爲可取。

硬限制:指對資源節點和數據塊的絕對限制,在任何情況下都不允許用戶超過這個限制;

軟限制:指用戶可以在一定時間範圍內超過軟限制的額度。

   

1172 # client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>

 這個三個配置,是Redis服務器對客戶端的輸出緩衝區的設置參數,有了它們,Redis不會直接把命令的輸出給客戶端傳過去,而是先放緩衝區裏,然後一次性的發送過去。具體參數含義如下:

class : 客戶端種類,normal、slave、pubsub。
       – mormal:普通的客戶端
       – slave: 從庫的複製客戶端
       – pub/sub: 發佈與訂閱的客戶端的
hard limit:緩衝區大小的硬性限制。
soft limit::緩衝區大小的軟性限制。
soft seconds: 緩衝區大小達到了(超過)soft limit 值的持續時間。

client-output-buffer-limit 參數限制分配的緩衝區的大小,防止內存無節制的分配。參數的默認值都爲0,意思是不做任何限制。

4、修改內存策略 memory_policy 的值

    查看memory 信息: >info memory 信息

    

關注這幾個值:

userd_memory:表示以字節爲單位的當前redis分配到的內存空間

maxmemory:表示redis獲取的內存的限制,0表示沒限制,即redis可使用主機上的所有可能的內存空間

maxmemory_policy:表示當redis的內存空間大小觸及maxmemory的時候,所採用的策略,默認爲noeviction。

我們把maxmemory的值設置得比userd_memory多一點的,用命令: >config set maxmemory  853450  

用set命令Rredis數據庫裏添加新值,看到有報錯 OOM。在添加新值,不報錯了,爲什麼?

這跟 Redis配置文件中設置maxmemory_policy策略值有關。

    

然後在配置文件中修改maxmemory_policy策略值爲: allkeys-lru ,就可以set新值了。

    

     

   

最後可以看到 allkeys-lru 策略,捨棄了最近不使用的key。

    

maxmemory不建議設置接近物理內存,因爲要給其他進程預留一部分空間。

Redis大多數時候是作爲緩存來用的,這時候務必要把memory_policy設置爲noeviction以外的值。

 

三、創建普通用作作爲啓動 Redis的專用用戶

在生產環境中,將系統中 root 運行的 Redis服務轉爲普通用戶(不設置密碼,不能客戶端登錄)啓動,來提高安全性。

1)新建一個用戶組和一個普通用戶,名字自定義

# 新建用戶組g_redis
[root@centos7 ~]# groupadd g_redis
# 新建用戶u_redis並加入g_redis組中
[root@centos7 ~]# useradd u_redis -g g_redis

2)將 Redis 的配置文件複製一份到 u_redis 用戶的家目錄下

# 強制複製redis的配置文件到u_redis用戶的家目錄下
[root@centos7 ~]# cp -rf /usr/local/redis/redis.conf /home/u_redis/

3)修改 Redis 配置文件及創建相應的目錄

     修改 Redis 配置文件

[root@centos7 ~]# vim /home/u_redis/redis.conf 

# 當 Redis 以守護進程方式運行時,Redis 默認會把 pid 寫入 /var/run/redis.pid 文件,可以通過 pidfile 指定
158 pidfile /home/u_redis/run/redis_6379.pid  

# 指定本地數據庫存放目錄
263 dir /home/u_redis/redis 

      創建相應的目錄

[root@centos7 ~]# mkdir -p /home/u_redis/run
[root@centos7 ~]# mkdir -p /home/u_redis/redis

4)將 u_redis 用戶家目錄下的所有文件所屬者與所屬組修改爲:u_redis:g_redis

[root@centos7 ~]# ll /home | grep u_redis
drwx------ 4 u_redis g_redis 104 5月  12 22:12 u_redis
[root@centos7 ~]# ll /home/u_redis/
總用量 64
drwxr-xr-x 2 root root     6 5月  12 22:12 redis
-rw-r--r-- 1 root root 61861 5月  12 22:11 redis.conf
drwxr-xr-x 2 root root     6 5月  12 22:12 run

[root@centos7 ~]# chown -R u_redis:g_redis /home/u_redis

[root@centos7 ~]# ll /home/u_redis/                     
總用量 64
drwxr-xr-x 2 u_redis g_redis     6 5月  12 22:12 redis
-rw-r--r-- 1 u_redis g_redis 61861 5月  12 22:11 redis.conf
drwxr-xr-x 2 u_redis g_redis     6 5月  12 22:12 run

5)修改 Redis 的安裝目錄權限

[root@centos7 ~]# ll /usr/local/ | grep redis
drwxr-xr-x  4 root root 84 5月  12 11:03 redis

# 變更redis的安裝目錄權限爲u_redis用戶所有
[root@centos7 ~]# chown -R u_redis:g_redis /usr/local/redis

[root@centos7 ~]# ll /usr/local/ | grep redis              
drwxr-xr-x  4 u_redis g_redis 84 5月  12 11:03 redis

6)切換普通用戶 u_redis啓動 Redis 服務 ok

[root@centos7 ~]# su u_redis

[u_redis@centos7 root]$ /usr/local/redis/bin/redis-server /home/u_redis/redis.conf

[u_redis@centos7 root]$ ps -ef | grep redis
root       7684   7189  0 22:16 pts/1    00:00:00 su u_redis
u_redis    7685   7684  0 22:16 pts/1    00:00:00 bash
u_redis    7705      1  0 22:17 ?        00:00:00 /usr/local/redis/bin/redis-server 192.168.198.20:6379
u_redis    7709   7685  0 22:17 pts/1    00:00:00 ps -ef
u_redis    7710   7685  0 22:17 pts/1    00:00:00 grep --color=auto redis

 

參考文章:

理解 Linux 的虛擬內存

TCP三次握手詳解

—— Stay Hungry. Stay Foolish. 求知若飢,虛心若愚。

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