1. 程式人生 > 其它 >Redis安裝及使用

Redis安裝及使用

本文記錄黑馬的redis課程筆記

1.單機安裝Redis

1.1.安裝Redis依賴

Redis是基於C語言編寫的,因此首先需要安裝Redis所需要的gcc依賴:

yum install -y gcc tcl

1.2.上傳安裝包並解壓

然後將Redis安裝包上傳到虛擬機器的任意目錄:

例如,我放到了/usr/local/src 目錄:

解壓縮:

tar -xzf redis-6.2.6.tar.gz

解壓後:

進入redis目錄:

cd redis-6.2.6

執行編譯命令:

make && make install

如果沒有出錯,應該就安裝成功了。

預設的安裝路徑是在 /usr/local/bin

目錄下:

該目錄以及預設配置到環境變數,因此可以在任意目錄下執行這些命令。其中:

  • redis-cli:是redis提供的命令列客戶端
  • redis-server:是redis的服務端啟動指令碼
  • redis-sentinel:是redis的哨兵啟動指令碼

1.3.啟動

redis的啟動方式有很多種,例如:

  • 預設啟動
  • 指定配置啟動
  • 開機自啟

1.3.1.預設啟動

安裝完成後,在任意目錄輸入redis-server命令即可啟動Redis:

redis-server

如圖:

這種啟動屬於前臺啟動,會阻塞整個會話視窗,視窗關閉或者按下CTRL + C則Redis停止。不推薦使用。

1.3.2.指定配置啟動

如果要讓Redis以後臺方式啟動,則必須修改Redis配置檔案,就在我們之前解壓的redis安裝包下(/usr/local/src/redis-6.2.6),名字叫redis.conf:

我們先將這個配置檔案備份一份:

cp redis.conf redis.conf.bck

然後修改redis.conf檔案中的一些配置:

# 允許訪問的地址,預設是127.0.0.1,會導致只能在本地訪問。修改為0.0.0.0則可以在任意IP訪問,生產環境不要設定為0.0.0.0
bind 0.0.0.0
# 守護程序,修改為yes後即可後臺執行
daemonize yes 
# 密碼,設定後訪問Redis必須輸入密碼
requirepass 123321

Redis的其它常見配置:

# 監聽的埠
port 6379
# 工作目錄,預設是當前目錄,也就是執行redis-server時的命令,日誌、持久化等檔案會儲存在這個目錄
dir .
# 資料庫數量,設定為1,代表只使用1個庫,預設有16個庫,編號0~15
databases 1
# 設定redis能夠使用的最大記憶體
maxmemory 512mb
# 日誌檔案,預設為空,不記錄日誌,可以指定日誌檔名
logfile "redis.log"

啟動Redis:

# 進入redis安裝目錄 
cd /usr/local/src/redis-6.2.6
# 啟動
redis-server redis.conf

停止服務:

# 利用redis-cli來執行 shutdown 命令,即可停止 Redis 服務,
# 因為之前配置了密碼,因此需要通過 -u 來指定密碼
redis-cli -u 123321 shutdown

1.3.3.開機自啟

我們也可以通過配置來實現開機自啟。

首先,新建一個系統服務檔案:

vi /etc/systemd/system/redis.service

內容如下:

[Unit]
Description=redis-server
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/bin/redis-server /usr/local/src/redis-6.2.6/redis.conf
PrivateTmp=true

[Install]
WantedBy=multi-user.target

然後過載系統服務:

systemctl daemon-reload

現在,我們可以用下面這組命令來操作redis了:

# 啟動
systemctl start redis
# 停止
systemctl stop redis
# 重啟
systemctl restart redis
# 檢視狀態
systemctl status redis

執行下面的命令,可以讓redis開機自啟:

systemctl enable redis

2.Redis客戶端

安裝完成Redis,我們就可以操作Redis,實現資料的CRUD了。這需要用到Redis客戶端,包括:

  • 命令列客戶端
  • 圖形化桌面客戶端
  • 程式設計客戶端

2.1.Redis命令列客戶端

Redis安裝完成後就自帶了命令列客戶端:redis-cli,使用方式如下:

redis-cli [options] [commonds]

其中常見的options有:

  • -h 127.0.0.1:指定要連線的redis節點的IP地址,預設是127.0.0.1
  • -p 6379:指定要連線的redis節點的埠,預設是6379
  • -a 123321:指定redis的訪問密碼

其中的commonds就是Redis的操作命令,例如:

  • ping:與redis服務端做心跳測試,服務端正常會返回pong

不指定commond時,會進入redis-cli的互動控制檯:

2. Redis概覽

2.1 特徵

  • 鍵值(key-value)型,value支援多種不同資料結構,功能豐富
  • 單執行緒,每個命令具備原子性
  • 低延遲,速度快(基於記憶體、IO多路複用、良好的編碼)。
  • 支援資料持久化支援主從叢集、分片叢集
  • 支援多語言客戶端

2.2 資料結構

Redis是一個 key-value的資料庫 , key一般是 String型別,不過 Value的型別多種多樣。

2.3 Reids的key結構分層

Redis的key允許有多個單詞形成層級結構,多個單詞之間用':'隔開,格式如下:

專案名:業務名:型別:id

例如: test:user:1 和 test:product:1

3. Redis命令

3.1 通用命令

- KEYS:檢視符合模板的所有 key,`不建議在生產環境裝置上使用`
- DEL:刪除一個指定的key
- EXISTS:判斷key是否存在
- EXPIRE:給一個key設定有效期,有效期到期時該key會被自動刪除
- TTL:檢視一個KEY的剩餘有效期

3.2 String 型別

字串型別,Redis中最簡單的儲存型別。value是字串。

  • String: 普通字串
  • int: 證書型別,可以坐自增和自減的操作
  • float: 浮點型別,可以坐自增和自減操作。

底層都是 位元組陣列 形式儲存,只是編碼不同。字串型別最大空間不能超過512m

常見命令:

- SET:新增或者修改已經存在的一個
- String型別的鍵值對
- GET:根據key獲取
- String型別的value
- MSET:批量新增多個String型別的鍵值對
- MGET:根據多個key獲取多個String型別的value
- INCR:讓一個整型的key自增1
- INCRBY:讓一個整型的key自增並指定步長,例如:incrby num 2 讓num值自增2
- INCRBYFLOAT:讓一個浮點型別的數字自增並指定步長
- SETNX:新增一個String型別的鍵值對,前提是這個key不存在,否則不執行
- SETEX:新增一個String型別的鍵值對,並且指定有效期

3.3 Hash型別

Hash型別,也叫雜湊,其value是一個無序字典,類似於Java中的HashMap結構。String結構是將物件序列化為JSON字串後儲存,當需要修改物件某個欄位時很不方便:

Hash結構可以將物件中的每個欄位獨立儲存,可以針對單個欄位做CRUD:

常見命令:

- HSET key field value:新增或者修改hash型別key的field的值
- HGET key field:獲取一個hash型別key的field的值
- HMSET:批量新增多個hash型別key的field的值
- HMGET:批量獲取多個hash型別key的field的值
- HGETALL:獲取一個hash型別的key中的所有的field和value
- HKEYS:獲取一個hash型別的key中的所有的field
- HVALS:獲取一個hash型別的key中的所有的value
- HINCRBY:讓一個hash型別key的欄位值自增並指定步長
- HSETNX:新增一個hash型別的key的field值,前提是這個field不存在,否則不執行

3.4 List型別

Redis中的List型別與Java中的LinkedList類似,可以看做是一個雙向連結串列結構。

既可以支援正向檢索和也可以支援反向檢索。

特徵也與LinkedList類似:有序元素可以重複插入和刪除快查詢速度一般常用來儲存一個有序資料,例如:朋友圈點贊列表,評論列表等。

常見命令:

- LPUSH key  element ... :向列表左側插入一個或多個元素
- LPOP key:移除並返回列表左側的第一個元素,沒有則返回nil
- RPUSH key  element ... :向列表右側插入一個或多個元素
- RPOP key:移除並返回列表右側的第一個元素
- LRANGE key star end:返回一段角標範圍內的所有元素
- BLPOP和BRPOP:與LPOP和RPOP類似,只不過在沒有元素時等待指定時間,而不是直接返回nil

如何利用List結構模擬一個棧?

  • 入口和出口在同一邊

如何利用List結構模擬一個佇列?

  • 入口和出口在不同邊

如何利用List結構模擬一個阻塞佇列?

  • 入口和出口在不同邊
  • 出隊時採用BLPOP或BRPOP

3.5 Set型別

Redis的Set結構與Java中的HashSet類似,可以看做是一個value為null的HashMap。

因為也是一個hash表,因此具備與HashSet類似的特徵:

  • 無序
  • 元素不可重複
  • 查詢快
  • 支援交集、並集、差集等功能

常見命令:

- SADD key member ... :向set中新增一個或多個元素
- SREM key member ... : 移除set中的指定元素
- SCARD key: 返回set中元素的個數
- SISMEMBER key member:判斷一個元素是否存在於set中
- SMEMBERS:獲取set中的所有元素
- SINTER key1 key2 ... :求key1與key2的交集
- SDIFF key1 key2 ... :求key1與key2的差集
- SUNION key1 key2 ..:求key1和key2的並集

3.6 SortedSet型別

Redis的SortedSet是一個可排序的set集合,與Java中的TreeSet有些類似,但底層資料結構卻差別很大。

SortedSet中的每一個元素都帶有一個score屬性,可以基於score屬性對元素排序,底層的實現是一個跳錶(SkipList)加 hash表。

SortedSet具備下列特性:

  • 可排序
  • 元素不重複
  • 查詢速度快

因為SortedSet的可排序特性,經常被用來實現排行榜這樣的功能。

常見命令:

- ZADD key score member:新增一個或多個元素到sorted set ,如果已經存在則更新其score值
- ZREM key member:刪除sorted set中的一個指定元素
- ZSCORE key member : 獲取sorted set中的指定元素的score值
- ZRANK key member:獲取sorted set 中的指定元素的排名
- ZCARD key:獲取sorted set中的元素個數
- ZCOUNT key min max:統計score值在給定範圍內的所有元素的個數
- ZINCRBY key increment member:讓sorted set中的指定元素自增,步長為指定的increment值
- ZRANGE key min max:按照score排序後,獲取指定排名範圍內的元素
- ZRANGEBYSCORE key min max:按照score排序後,獲取指定score範圍內的元素
- ZDIFF、ZINTER、ZUNION:求差集、交集、並集

**注意:所有的排名預設都是升序,如果要降序則在命令的Z後面新增REV即可**

4. Redis的Java客戶端

4.1 Jedis

Jedis的官網地址: https://github.com/redis/jedis

4.1.1 引入依賴

<!--Jedis客戶端-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.6.3</version>
</dependency>

4.1.2 程式碼

import com.codertl.jedis.util.JedisConnectionFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;

import java.util.Map;


public class JedisTest {

    private Jedis jedis;

    @Before
    public void setUp(){
        // 建立連線
        jedis = new Jedis("192.168.100.100", 6379);
        //// 設定密碼
        jedis.auth("redis123");
        //// 設定資料庫
        jedis.select(0);
    }

    @Test
    public void testString() {
        // 新增資料
        String result = jedis.set("name", "虎哥");
        System.out.println("result = " + result);
        // 獲取資料
        String name = jedis.get("name");
        System.out.println("name = " + name);
    }


    @Test
    public void testHash() {
        // 新增資料
        jedis.hset("user:1", "name", "Jack");
        jedis.hset("user:1", "age", "21");

        // 獲取資料
        Map<String, String> map = jedis.hgetAll("user:1");
        System.out.println("map = " + map);
    }

    @After
    public void tearDown() throws Exception {
        // 關閉連線
        if (jedis != null) {
            jedis.close();
        }
    }
}

4.1.3 Jedis連線池

Jedis本身是執行緒不安全的,並且頻繁的建立和銷燬連線會有效能損耗,因此我們推薦大家使用Jedis連線池代替Jedis的直連方式

package com.codertl.jedis.util;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @Author tl
 * @Date 2022/5/5
 */
public class JedisConnectionFactory {

    private static final JedisPool jedisPool;

    static {
        // 配置連線池
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(8);
        jedisPoolConfig.setMaxIdle(8);
        jedisPoolConfig.setMinIdle(0);
        jedisPoolConfig.setMaxWaitMillis(10000);

        jedisPool = new JedisPool(jedisPoolConfig,
                "192.168.100.100", 6379, 10000, "redis123", 0);
    }

    public static Jedis getJedis() {
        return jedisPool.getResource();
    }
}

4.2 SpringDataRedis

SpringData是Spring中資料操作的模組,包含對各種資料庫的整合,其中對Redis的整合模組就叫做SpringDataRedis

官網地址:https://spring.io/projects/spring-data-redis

- 提供了對不同Redis客戶端的整合(Lettuce和Jedis)
- 提供了RedisTemplate統一API來操作Redis
- 支援Redis的釋出訂閱模型
- 支援Redis哨兵和Redis叢集
- 支援基於Lettuce的響應式程式設計
- 支援基於JDK、JSON、字串、Spring物件的資料序列化及反序列化
- 支援基於Redis的JDKCollection實現

SpringDataRedis中提供了RedisTemplate工具類,其中封裝了各種對Redis的操作。並且將不同資料型別的操作API封裝到了不同的型別中:

4.2.1 引入依賴

<!--redis依賴-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--連線池依賴-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

4.2.2 配置檔案

spring:
  redis:
    host: 192.168.100.100
    port: 6379
    password: redis100
    database: 0
    lettuce:
      pool:
        # 最大連線
        max-active: 10
        # 連線等待
        max-wait: 60000
        # 最大空閒等待時間
        max-idle: 10
        # 最小空閒時間
        min-idle: 0

4.2.3 注入RedisTemplate

@Autowired
private RedisTemplate redisTemplate;

4.2.4 測試

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
class RedisSpringBootDemoApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void testString() {
        // 儲存字串
        redisTemplate.opsForValue().set("name", "虎哥");
        // 獲取字串
        Object name = redisTemplate.opsForValue().get("name");
        System.out.println("name = " + name);
    }

}

4.3. SpringDataRedis序列化方式

4.3.1 自定義序列化器

RedisTemplate可以接收任意Object作為值寫入Redis,只不過寫入前會把Object序列化為位元組形式,預設是採用JDK序列化,得到的結果是這樣的:

缺點:可讀性差、記憶體佔用較大

我們可以自定義RedisTemplate的序列化方式,程式碼如下:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;


@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 建立 RedisTemplate 物件
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();

        // 設定連線工廠
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // 建立Json序列化器
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        // 設定key的序列化器
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        // 設定value的序列化器
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(genericJackson2JsonRedisSerializer);

        // 返回
        return redisTemplate;
    }
}

4.3.2 StringRedisTemplate

儘管JSON的序列化方式可以滿足我們的需求,但依然存在一些問題

為了在反序列化時知道物件的型別,JSON序列化器會將類的class型別寫入json結果中,存入Redis,會帶來額外的記憶體開銷。

為了節省記憶體空間,我們並不會使用JSON序列化器來處理value,而是統一使用String序列化器,要求只能儲存String型別的key和value。當需要儲存Java物件時,手動完成物件的序列化和反序列化。

Spring預設提供了一個StringRedisTemplate類,它的key和value的序列化方式預設就是String方式。省去了我們自定義RedisTemplate的過程:

import com.codertl.pojo.User;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

@SpringBootTest
class RedisStringTests {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    void testString() {
        // 儲存字串
        stringRedisTemplate.opsForValue().set("name", "虎哥");
        // 獲取字串
        Object name = stringRedisTemplate.opsForValue().get("name");
        System.out.println("name = " + name);
    }

    public static final ObjectMapper mapper = new ObjectMapper();

    @Test
    void testSaveUser() throws JsonProcessingException {
        // 建立物件
        User user = new User("虎哥", 21);
        // 手動序列化
        String json = mapper.writeValueAsString(user);
        // 寫入資料
        stringRedisTemplate.opsForValue().set("user:200", json);
        // 獲取資料
        String jsonUser =  stringRedisTemplate.opsForValue().get("user:200");
        // 手動反序列化
        User user1 = mapper.readValue(jsonUser, User.class);
        System.out.println("user1 = " + user1);
    }
    
    @Test
    void testHash() {
        stringRedisTemplate.opsForHash().put("user:400", "name", "虎哥400");
        stringRedisTemplate.opsForHash().put("user:400", "age", "21");

        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries("user:400");
        System.out.println("entries = " + entries);
    }
}

4.4 RedisTemplate 序列化總結

4.4.1 方案一:

  1. 自定義RedisTemplate
  2. 修改RedisTemplate的序列化器為GenericJackson2JsonRedisSerializer

4.4.2 方案二

  1. 使用StringRedisTemplate
  2. 寫入Redis時,手動把物件序列化為JSON
  3. 讀取Redis時,手動把讀取到的JSON反序列化為物件