Redis 新手入門指南 快速掌握數據存儲與管理技巧

文章最後更新於 2024 年 9 月 23 日

什麼是 Redis?

定義與背景

Redis(Remote Dictionary Server)是一個開源的、高性能的、基於記憶體的鍵值對數據庫系統。它由 Salvatore Sanfilippo 於 2009 年開發,最初是為了解決高並發、低延遲的網站應用問題而設計的。

Redis 使用 ANSI C 語言編寫,可以在大多數 POSIX 系統(如 Linux、*BSD、OS X)上運行,並且在大多數流行的編程語言中都有客戶端支持。

主要用途與應用場景

Redis 的主要用途包括:

  1. 快取系統:利用其高速讀寫能力,減輕後端數據庫的壓力。
  2. 會話管理:在分布式系統中存儲和管理用戶會話信息。
  3. 實時分析:利用其數據結構進行實時數據統計和分析。
  4. 消息隊列:作為輕量級消息代理,實現發布/訂閱模式。
  5. 排行榜系統:利用有序集合實現高效的排序功能。

常見的應用場景包括社交網絡、遊戲應用、實時廣告系統等。

Redis 的特點

高性能與高可用性

  • 高性能:Redis 是基於記憶體操作的,能夠提供極高的讀寫速度,通常可以達到 10 萬次/秒的處理能力。
  • 持久化:支持數據的持久化,可以將記憶體中的數據保存到磁盤中,確保數據不會因斷電等原因丟失。
  • 原子性操作:Redis 的所有操作都是原子性的,確保了數據的一致性。
  • 高可用性:通過主從複製、哨兵模式和集群模式,Redis 可以實現高可用性和可擴展性。

支持多種資料結構

Redis 支持豐富的數據結構,包括:

  1. 字符串(String):最基本的數據類型,可以存儲字符串、整數或浮點數。
  2. 列表(List):字符串列表,按照插入順序排序。可以用作隊列或堆棧。
  3. 集合(Set):無序的字符串集合,支持交集、並集、差集等操作。
  4. 有序集合(Sorted Set):類似集合,但每個元素都關聯一個分數,可以按分數排序。
  5. 哈希(Hash):鍵值對的集合,適合存儲對象。
  6. 位圖(Bitmap):可以對字符串進行位操作。
  7. HyperLogLog:用於基數統計的概率性數據結構。

這些數據結構使 Redis 能夠適應各種複雜的應用場景。

Redis 與其他數據庫的比較

與關聯型數據庫的區別

  1. 數據模型:Redis 是鍵值對數據庫,而關聯型數據庫使用表格模型。
  2. 查詢語言:Redis 使用簡單的命令集,而關聯型數據庫使用 SQL。
  3. 性能:Redis 基於記憶體,讀寫速度極快;關聯型數據庫通常基於磁盤,相對較慢。
  4. 事務支持:Redis 支持簡單的事務,而關聯型數據庫提供完整的 ACID 事務支持。
  5. 擴展性:Redis 更容易進行水平擴展,而關聯型數據庫的擴展相對複雜。

與其他 NoSQL 數據庫的對比

  1. 與 MongoDB 比較

    • MongoDB 是文檔型數據庫,支持複雜的查詢;Redis 主要用於簡單的鍵值對存儲。
    • Redis 完全基於記憶體,性能更高;MongoDB 可以處理更大的數據集。
  2. 與 Cassandra 比較

    • Cassandra 專注於高可用性和可擴展性,適合處理大規模數據;Redis 更適合需要快速響應的場景。
    • Redis 提供更豐富的數據結構;Cassandra 主要支持表格結構。
  3. 與 Memcached 比較

    • Redis 支持更多的數據類型;Memcached 只支持簡單的鍵值對。
    • Redis 支持數據持久化;Memcached 是純記憶體數據庫。
    • Redis 支持主從複製;Memcached 不支持。

Redis 的安裝與配置

安裝 Redis

支持的操作系統

Redis 可以在多種操作系統上運行,主要包括:

  • Linux(各種發行版,如 Ubuntu、CentOS、Fedora 等)
  • macOS
  • BSD 系統
  • Windows(通過 WSL 或特殊版本支持)

Redis 最初是為 POSIX 系統設計的,因此在 Linux 和 Unix 類系統上運行效果最佳。

安裝方法

使用包管理器安裝

在大多數 Linux 發行版中,可以使用包管理器快速安裝 Redis:

  • Ubuntu 或 Debian:
    sudo apt update
    sudo apt install redis-server

  • CentOS 或 RHEL:
    sudo yum install epel-release
    sudo yum install redis

  • macOS(使用 Homebrew):
    brew install redis

從源碼編譯安裝
  1. 下載最新的 Redis 源碼:
    wget https://download.redis.io/redis-stable.tar.gz

  2. 解壓並編譯:
    tar xzf redis-stable.tar.gz
    cd redis-stable
    make

  3. 安裝:
    sudo make install

基本配置

配置文件介紹

Redis 的主要配置文件是 redis.conf。在使用包管理器安裝時,此文件通常位於 /etc/redis/redis.conf。

配置文件包含多個部分,如網絡設置、一般設置、安全設置、限制、快照等。

常用配置參數與調整

  1. 端口設置:
    port 6379

  2. 綁定 IP:
    bind 127.0.0.1

  3. 最大內存使用:
    maxmemory 2gb

  4. 持久化設置:

    • RDB 持久化:
      save 900 1
      save 300 10
      save 60 10000
    • AOF 持久化:
      appendonly yes
  5. 安全設置:
    requirepass your_password

  6. 日誌級別:
    loglevel notice

調整這些參數時,請根據實際需求和系統資源進行設置。

啟動與停止 Redis

使用命令行啟動

  1. 直接啟動 Redis 服務器:
    redis-server

  2. 使用特定配置文件啟動:
    redis-server /path/to/redis.conf

進程管理與守護進程模式

  1. 以守護進程模式運行:
    在配置文件中設置 daemonize yes,然後啟動 Redis。

  2. 使用系統服務管理(以 systemd 為例):

    • 啟動:sudo systemctl start redis
    • 停止:sudo systemctl stop redis
    • 重啟:sudo systemctl restart redis
    • 查看狀態:sudo systemctl status redis
  3. 使用 Redis 命令行停止服務:
    redis-cli shutdown

  4. 使用信號停止 Redis:

    • 正常停止:kill -TERM
    • 強制停止:kill -9

注意:在生產環境中,建議使用系統服務管理來啟動和管理 Redis,以確保系統重啟後 Redis 能自動啟動,並且可以更好地進行日誌管理和監控。

Redis 的基本操作

連接 Redis

使用命令行工具(redis-cli)

redis-cli 是 Redis 自帶的命令行介面工具,可以直接與 Redis 服務器進行交互。

連接本地 Redis 服務器:
redis-cli

連接遠程 Redis 服務器:
redis-cli -h host -p port -a password

基本操作示例:
$ redis-cli
127.0.0.1:6379> SET mykey "Hello"
OK
127.0.0.1:6379> GET mykey
"Hello"

使用程式語言連接

不同的程式語言有對應的 Redis 客戶端庫。以下是幾種常見語言的連接示例:

Python(使用 redis-py):

import redis

r = redis.Redis(host='localhost', port=6379, db=0)
r.set('foo', 'bar')
value = r.get('foo')
print(value)  # 輸出: b'bar'

Node.js(使用 node-redis):
const redis = require("redis");
const client = redis.createClient();

client.on("connect", function() {
  console.log("Connected to Redis");
});

client.set("key", "value", redis.print);
client.get("key", function(error, result) {
  if (error) {
    console.log(error);
    return;
  }
  console.log('GET result ->', result);
});

常用命令介紹

字符串操作

SET key value:設置指定 key 的值
GET key:獲取指定 key 的值
DEL key:刪除指定 key
INCR key:將 key 中儲存的數字值增 1
DECR key:將 key 中儲存的數字值減 1

列表操作

LPUSH key value [value ...]:將一個或多個值插入到列表頭部
RPUSH key value [value ...]:將一個或多個值插入到列表尾部
LRANGE key start stop:獲取列表指定範圍內的元素
LPOP key:移除並獲取列表的第一個元素
RPOP key:移除並獲取列表的最後一個元素

集合操作

SADD key member [member ...]:向集合添加一個或多個成員
SMEMBERS key:返回集合中的所有成員
SREM key member [member ...]:移除集合中一個或多個成員
SINTER key [key ...]:返回給定所有集合的交集

散列操作

HSET key field value:將哈希表 key 中的字段 field 的值設為 value
HGET key field:獲取存儲在哈希表中指定字段的值
HGETALL key:獲取在哈希表中指定 key 的所有字段和值
HDEL key field [field ...]:刪除一個或多個哈希表字段

常用的查詢與修改命令

KEYS pattern:查找所有符合給定模式的 key
EXPIRE key seconds:為給定 key 設置過期時間
TTL key:查看 key 剩餘的過期時間
TYPE key:返回 key 所儲存的值的類型

事務與管道

事務的概念與使用

Redis 事務可以一次執行多個命令,事務中的所有命令都會被序列化並順序執行。事務的主要作用是保證一個客戶端發起的事務中的命令可以連續的執行,而不會被其他客戶端的命令插入。

基本事務操作:
MULTI:開始一個事務
EXEC:執行事務內的所有命令
DISCARD:取消事務,放棄執行事務內的所有命令

示例:

MULTI
OK
SET book-name "Mastering Redis"
QUEUED
GET book-name
QUEUED
SADD tag "Redis" "Database" "NoSQL"
QUEUED
SMEMBERS tag
QUEUED
EXEC
1) OK
2) "Mastering Redis"
3) (integer) 3
4) 1) "Redis"
2) "Database"
3) "NoSQL"

如何使用管道提高性能

Redis 管道(Pipelining)技術可以在一次交互中執行多條命令,從而大大減少網絡往返時間,提高性能。

Python 示例:

import redis

r = redis.Redis(host='localhost', port=6379, db=0)
pipe = r.pipeline()

pipe.set('foo', 'bar')
pipe.get('foo')

results = pipe.execute()
print(results)  # 輸出: [True, b'bar']

Node.js 示例:
const redis = require("redis");
const client = redis.createClient();

client.pipeline()
    .set('foo', 'bar')
    .get('foo')
    .exec((err, results) => {
        console.log(results);  // 輸出: [ [null, 'OK'], [null, 'bar'] ]
    });

使用管道可以顯著提高 Redis 的性能,特別是在需要執行大量命令的場景下。然而,需要注意的是,過大的管道請求可能會增加 Redis 服務器的內存使用,因此在使用時要根據實際情況進行權衡。

Redis 的高級特性

持久化機制

Redis 提供了兩種主要的持久化機制:RDB(Redis Database)和 AOF(Append Only File)。

RDB 和 AOF 的區別與選擇

RDB(快照):

  • 優點:
    • 壓縮的二進制文件,佔用空間小
    • 適合大規模數據恢復
    • 對性能影響較小
  • 缺點:
    • 可能丟失最後一次快照後的數據
    • 大數據集時,保存快照的過程可能會阻塞服務

AOF(日誌):

  • 優點:
    • 數據安全性高,支持秒級持久化
    • 可讀性高,便於分析
  • 缺點:
    • 文件體積較大
    • 恢復速度較慢

選擇建議:

  • 如果能承受數分鐘的數據丟失,可以只使用 RDB
  • 對數據安全性要求極高的場景,建議同時使用 RDB 和 AOF

設置持久化策略

RDB 配置:
save 900 1 # 900秒內至少有1個key被修改,則執行快照
save 300 10 # 300秒內至少有10個key被修改,則執行快照
save 60 10000 # 60秒內至少有10000個key被修改,則執行快照

AOF 配置:
appendonly yes # 開啟AOF
appendfilename "appendonly.aof" # AOF文件名
appendfsync everysec # 每秒同步一次

缓存策略

常見的缓存過期策略

  1. 定時過期:為每個值設置一個過期時間
    SET key value EX seconds

  2. 訪問過期:在訪問時才檢查是否過期
    EXPIRE key seconds

  3. 定期清理:Redis 會定期清理過期的鍵

使用 LRU 淘汰機制

Redis 提供了多種內存淘汰策略,其中最常用的是 LRU(Least Recently Used):

配置 LRU:
maxmemory 100mb
maxmemory-policy allkeys-lru

淘汰策略選項:

  • volatile-lru:從已設置過期時間的數據集中淘汰最少使用的
  • allkeys-lru:從所有數據集中淘汰最少使用的
  • volatile-random:從已設置過期時間的數據集中隨機淘汰
  • allkeys-random:從所有數據集中隨機淘汰
  • volatile-ttl:從已設置過期時間的數據集中淘汰將要過期的
  • noeviction:不淘汰,當內存不足時返回錯誤

分佈式與高可用性

Redis Cluster 與主從複製的概念

Redis Cluster:

  • 自動分片機制,將數據自動分散到多個節點
  • 提供一定程度的可用性,部分節點失敗時,集群仍可繼續工作

主從複製:

  • 主節點負責讀寫,從節點負責讀和數據備份
  • 可以提高讀性能和數據安全性

設置主從複製:
在從節點配置文件中添加:
slaveof

Sentinel 監控與故障轉移

Sentinel 是 Redis 的高可用性解決方案:

  • 監控:持續監控主從節點是否正常運行
  • 通知:當被監控的服務出現問題時,通知管理員或其他應用程序
  • 自動故障轉移:當主節點失效時,自動將一個從節點升級為新的主節點

Sentinel 配置示例:
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1

使用 Sentinel:

  1. 啟動 Sentinel:
    redis-sentinel /path/to/sentinel.conf

  2. 在應用程序中使用 Sentinel:
    from redis.sentinel import Sentinel
    sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1)
    master = sentinel.master_for('mymaster', socket_timeout=0.1)
    slave = sentinel.slave_for('mymaster', socket_timeout=0.1)

通過使用 Redis Cluster、主從複製和 Sentinel,可以構建出高可用、可擴展的 Redis 架構,滿足大規模應用的需求。

Redis 的性能與優化

性能基準測試

如何測試 Redis 的性能

  1. 使用 redis-benchmark 工具:
    redis-benchmark -h localhost -p 6379 -c 50 -n 10000

    這個命令會模擬 50 個客戶端同時發送 10000 個請求。

  2. 自定義測試腳本:
    可以使用 Python 或其他語言編寫腳本,模擬實際業務場景下的負載。

  3. 使用專業的壓力測試工具:
    如 Apache JMeter 或 wrk。

常見的性能指標

  1. 吞吐量(Throughput):

    • 每秒處理的操作數(OPS)
    • 可以通過 redis-benchmark 的輸出直接獲得
  2. 延遲(Latency):

    • 單個請求的響應時間
    • 使用 redis-cli --latency 命令測量
  3. 記憶體使用:

    • 通過 INFO memory 命令查看
    • 關注 used_memory 和 used_memory_peak 等指標
  4. CPU 使用率:

    • 使用服務器監控工具如 top 或 htop 查看
  5. 網絡帶寬使用:

    • 使用 iftop 等工具監控網絡使用情況

2. 常見性能瓶頸

錯誤的使用模式

  1. 大量使用 KEYS 命令:

    • 在大型數據庫中,KEYS 命令可能導致服務器阻塞
    • 替代方案:使用 SCAN 命令進行增量式迭代
  2. 使用過大的集合:

    • 過大的 Hash、Set 或 List 可能導致操作變慢
    • 解決方案:將大集合拆分為多個小集合
  3. 頻繁執行耗時命令:

    • 如 SORT、SUNION、ZUNIONSTORE 等
    • 解決方案:考慮在應用層實現或使用 Lua 腳本優化
  4. 不恰當的數據過期策略:

    • 大量 key 同時過期可能導致性能抖動
    • 解決方案:為 key 設置隨機過期時間

網絡延遲與 I/O 性能

  1. 網絡延遲:

    • 確保 Redis 客戶端和服務器在同一數據中心
    • 使用連接池減少連接建立的開銷
  2. 磁盤 I/O:

    • 使用 SSD 可以顯著提升持久化性能
    • 調整 AOF 重寫和 RDB 快照的頻率
  3. 網絡帶寬限制:

    • 使用管道(Pipeline)減少網絡往返
    • 壓縮數據以減少網絡傳輸量

3. 性能優化技巧

合理選擇資料結構

  1. 字符串(String):

    • 適用於簡單的 key-value 存儲
    • 對於數字值,可以使用 INCR、DECR 等原子操作
  2. 哈希(Hash):

    • 適用於存儲對象,比多個單獨的 string 更節省內存
    • 例:HMSET user:1000 username antirez birthyear 1977 verified 1
  3. 列表(List):

    • 適用於消息隊列或最新動態等場景
    • 例:LPUSH mylist "world"; LPUSH mylist "hello"
  4. 集合(Set):

    • 適用於存儲唯一元素集合,支持交集、並集操作
    • 例:SADD myset "Hello"
  5. 有序集合(Sorted Set):

    • 適用於排行榜等需要排序的場景
    • 例:ZADD leaderboard 1000 user1

使用 Lua 腳本來減少往返次數

Lua 腳本可以在 Redis 服務器端執行複雜的操作,減少網絡往返,提高性能。

  1. 基本用法:
    EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 mykey myvalue

  2. 將腳本載入到 Redis:
    SCRIPT LOAD "return redis.call('SET', KEYS[1], ARGV[1])"

  3. 執行已載入的腳本:
    EVALSHA "腳本的 SHA1 校驗和" 1 mykey myvalue

  4. 複雜操作示例(原子計數器):

    local current = redis.call("GET", KEYS[1])
    if current == false then
     redis.call("SET", KEYS[1], 1)
     return 1
    else
     redis.call("SET", KEYS[1], current + 1)
     return current + 1
    end

    使用:EVAL "上述Lua腳本" 1 mycounter
    通過使用 Lua 腳本,可以將多個 Redis 操作組合成一個原子操作,不僅能提高性能,還能確保操作的一致性。

Redis 的實際應用案例

1. 網站會話管理

如何使用 Redis 儲存會話資料

  1. 設置會話數據:

    SET session:user123 "{\"user_id\": 123, \"username\": \"john_doe\", \"login_time\": 1620000000}"
    EXPIRE session:user123 1800  # 設置30分鐘過期
  2. 獲取會話數據:

    GET session:user123
  3. 更新會話數據:

    HSET session:user123 last_activity 1620001000
  4. 刪除會話:

    DEL session:user123

使用 Python 和 Flask 的示例:

from flask import Flask, session
import redis

app = Flask(__name__)
app.secret_key = 'your_secret_key'
r = redis.Redis(host='localhost', port=6379, db=0)

@app.route('/login')
def login():
    user_id = 123  # 假設用戶已驗證
    session['user_id'] = user_id
    r.setex(f"session:{user_id}", 1800, "logged_in")
    return "Logged in"

@app.route('/check')
def check_login():
    user_id = session.get('user_id')
    if user_id and r.get(f"session:{user_id}"):
        return "User is logged in"
    return "User is not logged in"

優勢分析

  1. 高性能:Redis 的讀寫速度非常快,可以大幅提升網站響應速度。
  2. 分佈式支持:易於在多個服務器間共享會話數據。
  3. 靈活的過期策略:可以輕鬆設置和更新會話的過期時間。
  4. 數據持久化:Redis 的持久化機制可以防止服務器重啟導致的會話丟失。
  5. 豐富的數據結構:可以存儲複雜的會話數據,如使用 Hash 存儲用戶的多個屬性。

2. 即時數據分析

實時統計與數據流處理

  1. 計數器實現:

    INCR pageviews  # 增加頁面瀏覽次數
    INCRBY sales 100  # 增加銷售額
  2. 時間序列數據:

    ZADD hourly_sales 1620000000 100  # 記錄某個時間點的銷售額
  3. 滑動窗口統計:

import time

def log_event(event_name):
    now = int(time.time())
    pipe = r.pipeline()
    pipe.zadd(event_name, {now: 1})
    pipe.zremrangebyscore(event_name, 0, now - 3600)  # 移除一小時前的數據
    pipe.execute()

def get_events_last_hour(event_name):
    now = int(time.time())
    return r.zcount(event_name, now - 3600, now)

使用 Pub/Sub 實現消息推送

  1. 訂閱頻道:

    SUBSCRIBE news_channel
  2. 發布消息:

    PUBLISH news_channel "Breaking news: Redis 6.0 released!"
  3. Python 實現示例:

import redis

r = redis.Redis(host='localhost', port=6379, db=0)

def message_handler(message):
    print(f"Received: {message['data'].decode()}")

pubsub = r.pubsub()
pubsub.subscribe(**{'news_channel': message_handler})
pubsub.run_in_thread(sleep_time=0.001)

在另一個進程或終端中發布消息

r.publish('news_channel', 'Hello, Redis!')

3. 遊戲數據儲存

使用 Redis 儲存玩家狀態與分數

  1. 儲存玩家基本信息:

    HMSET player:1000 username "hero123" level 10 health 100 mana 50
  2. 更新玩家狀態:

    HINCRBY player:1000 health -10  # 減少生命值
    HINCRBY player:1000 level 1  # 升級
  3. 記錄玩家分數:

    ZADD game_scores 5000 player:1000
  4. 獲取玩家信息:

    HGETALL player:1000

實現排行榜功能

  1. 添加分數:

    ZADD leaderboard 5000 player:1000
    ZADD leaderboard 6000 player:1001
    ZADD leaderboard 4500 player:1002
  2. 獲取前 N 名玩家:

    ZREVRANGE leaderboard 0 9 WITHSCORES  # 獲取前10名
  3. 獲取玩家排名:

    ZREVRANK leaderboard player:1000
  4. 獲取某個範圍的玩家:

    ZREVRANGEBYSCORE leaderboard 6000 4000 WITHSCORES
  5. Python 實現示例:

import redis

r = redis.Redis(host='localhost', port=6379, db=0)

def update_score(player_id, score):
    r.zadd('leaderboard', {f'player:{player_id}': score})

def get_top_players(n):
    return r.zrevrange('leaderboard', 0, n-1, withscores=True)

def get_player_rank(player_id):
    return r.zrevrank('leaderboard', f'player:{player_id}')

# 使用示例
update_score(1000, 5000)
update_score(1001, 6000)
update_score(1002, 4500)

print(get_top_players(3))
print(get_player_rank(1000))

這些應用案例展示了 Redis 在實際場景中的靈活性和強大功能。Redis 不僅可以作為快速的數據存儲解決方案,還能處理複雜的數據結構和實時操作,使其成為構建高性能、可擴展應用的理想選擇。

關於作者

Carger
Carger
我是Oscar (卡哥),前Yahoo Lead Engineer、高智商同好組織Mensa會員,超過十年的工作經驗,服務過Yahoo關鍵字廣告業務部門、電子商務及搜尋部門,喜歡彈吉他玩音樂,也喜歡投資美股、虛擬貨幣,樂於與人分享交流!

如果對文章內容有任何問題,歡迎在底下留言讓我知道。
如果你喜歡我的文章,可以按分享按鈕,讓更多的人看見我的文章。