---
# System prepended metadata

title: Redis (一) - 基本概念
tags: [Redis]

---

# Redis (一) - 基本概念
![image alt](https://miro.medium.com/max/1024/1*-AfwBaNoMNnIm5kSTID9mA.png)

Redis 全名為 Remote Dictinoary Server，是一個記憶體內(in-memory)的 key-value 資料庫，簡單來說就是把資料放在記憶體而不是像傳統資料庫放在硬碟，以提升存取的速度。

<!-- more -->

Redis 支援讓資料每隔一段時間儲存到硬碟，這樣可以讓電腦或伺服器重啟時還可以再從硬碟讀取出來。此外，也可以將每道指令寫進 log。Redis 還支援以 master-slave 的模式進行數據備份。

Redis 的所有操作都是原子操作(atomic operation)，原子操作是指這個操作不可以被中斷，一旦開始執行就會一直執行到結束而且指令的執行順序也不能被更改。而原子操作也保證了操作的結果是正確的，操作若是不成功就完全不執行。

## Redis 安裝
使用 [Docker](https://tienyulin.github.io/docker-concept/) 安裝並啟動 Redis
```bash=
docker run --name redis -d redis
```
啟動 Redis Container 之後，可以使用 docker exec 指令進到 Redis Container 內，再輸入 redis-cli 打開 Redis 終端。
```bash=
redis-cli
```

也可以使用 redis-cli 指令遠端連線開啟 Redis 終端，若是沒有設定密碼則不需要填。
```bash=
redis-cli -h host -p port -a password 
```

範例 : 
```bash=
redis-cli -h 127.0.0.1 -p 6379
```
Redis 預設的 port 是 6379，如果有改過 port 則填入你設定的 port。

打開的 Redis 終端如下 : 
```bash=
127.0.0.1:6379>
```

## Redis 連線
打開了 Redis 的終端後，我們要來看一些基本的連線操作。

### Ping
Ping 是用來在 Redis 終端上測試 Redis 是否有正常運作，若 Redis 有成功運作會回傳 PONG。
```bash=
127.0.0.1:6379> ping
PONG
```

### Echo
Echo 可以在 Redis 終端上印出文字。
```bash=
127.0.0.1:6379> echo <message>
```

範例 : 
```bash=
127.0.0.1:6379> echo helloworld
"helloworld"
```

### Select
Select 用來切換到指定的 DB。
```bash=
127.0.0.1:6379> select <index>
```
index 可以輸入指定的 DB。

範例 : 
```bash=
// 指定切換到 DB 1
127.0.0.1:6379> select 1
OK

// 切換到 DB 1 之後，命令列前面會多一個 [1]
127.0.0.1:6379[1]>

Redis 預設 DB 是 0，要切回來就指定切到 DB 0
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379>
```

### Quit
Quit 用於關閉 Redis 的連線。
```bash=
127.0.0.1:6379> quit
```

## Redis 的配置
Redis 的配置文件在 Redis 的安裝目錄下，文件名稱為 `redis.conf`，可以透過 `config` 指令來查看或修改配置。

### 查看配置
使用 `get` 指令來查看配置
```bash=
127.0.0.1:6379> config get <configsettingname>
```

範例 : 
```bash=
127.0.0.1:6379> config get port
1) "port"
2) "6379"
```

使用 `get ` 取得所有的設定
```bash=
127.0.0.1:6379> config get 
```

### 編輯配置
使用 `set` 指令來修改配置
```bash=
127.0.0.1:6379> config set <configname> <newconfigvalue>
```

範例 : 
```bash=
127.0.0.1:6379> config set loglevel "notice"
OK
127.0.0.1:6379> config get loglevel
1) "loglevel"
2) "notice"
```

### 設定密碼
上面 Redis 安裝有提到如果有設定密碼要輸入密碼，密碼就是在 `redis.conf` 裡的 `requirepass` 來設定。

我們先取得 requirepass 看看原本的值是什麼。 
```bash=
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) ""
```
可以看到 requirepass 預設是空字串，所以如果之後要取消密碼只要改回空字串即可。

接下來我們就來開始設定密碼，使用 config set 即可設定。
```bash=
127.0.0.1:6379> config set requirepass "123456"
OK
```

設定完密碼後我們使用 config get 來取得 requirepass 看看密碼現在是什麼。這時 Redis 會回傳 `(error) NOAUTH Authentication required.`。這是因為我們已經設定密碼了，所以需要先驗證密碼才可以查看設定。
```bash=
127.0.0.1:6379> config get requirepass
(error) NOAUTH Authentication required.
```

**Auth**
Auth 是用來向 Redis 驗證密碼的指令。
```bash=
127.0.0.1:6379> auth <password>
```

範例 : 
```bash=
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "123456"
127.0.0.1:6379> auth 123456
OK

// 取消設定密碼
127.0.0.1:6379> config set requirepass ""
OK
```

## Redis 的資料型態
Redis 支援以下幾種資料型態 : 
* String (字串)
* Hash (雜湊)
* List (清單)
* Set (集合)
* Sorted Set (有序集合)
* HyperLogLog
* Bitmap (點陣圖)

### String
String 是 Redis 最基本的類型，也就是一個 Key 對應一個 Value。這個 String 是 [Binary-safe](https://www.cnblogs.com/jing99/p/11687308.html) 的，所以可以包含任何數據，包括圖片或者是序列化過的文件等。而 String 的值最大儲存限制為 512 MB。

**set**
設定 String
```bash=
127.0.0.1:6379> set <key> <value>
```

**mset**
可以一次設定多個 String
```bash=
127.0.0.1:6379> mset <key1> <value1> <key2> <value2> ...
```

**get**
取得 String
```bash=
127.0.0.1:6379> get <key>
```

範例 : 
```bash=
127.0.0.1:6379> set sampleKey sampleValue
OK
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> get sampleKey
"sampleValue"
```

### Hash
Hash 是一個 Key-Value 的集合，也就是所謂的 Hash Table。前面定義了 String 是一組 Key-Value，所以簡單來說一個 Hash 會儲存多個 String。

**hset**
設定 Hash
```bash=
127.0.0.1:6379> hset <hashKey> <field1> <value1> <field2> <value2> ... 
```
hashKey 就是這張 Hash 的名稱，field、value 是一組 String，可以連續輸入多組 String 一次完成建立 Hash。

如果設定的 field 已經存在則會覆蓋 vaule 內容，也就是要修改內容也是使用 `hset` 指令來修改

**hget** 
取得 hash
```bash=
// 取得 Hash 中的所有 String
127.0.0.1:6379> hgetall <hashKey>

// 指定 Hash 中的一個 Key 來獲取 Value
127.0.0.1:6379> hget <hashKey> <field>
```


範例 : 
```bash=
127.0.0.1:6379> hset Kevin sex male phone 0912345678
(integer) 2
127.0.0.1:6379> hgetall Kevin
1) "sex"
2) "male"
3) "phone"
4) "0912345678"
127.0.0.1:6379> hget Kevin sex
"male"
```

### List
List 是一個簡單的字串清單，依照插入的順序排序，可以從左邊或右邊新增元素。

**lpush**
從左邊插入元素
```bash=
lpush <key> <element1> <element2> ...
```
這裡要注意的是會依照 element1、element2 的輸入順序依序從左邊插入，也就是完成後 List 會跟輸入的順序相反，變成 [element2, element1, ...]。

**rpush**
從右邊插入元素
```bash=
rpush <key> <element1> <element2> ...
```

**lpop**
從左邊移除元素
```bash=
lpop <key>
```

**rpop**
從右邊移除元素
```bash=
rpop <key>
```

**lrange**
輸出指定區間的元素
```bash=
lrange <key> <start> <stop>
```
start 和 stop 可以指定輸出的區間，0表示第一個元素，1表示第二個元素，以此類推。-1表示最後一個元素，-2表示倒數第二個元素，以此類推。

**lindex**
輸出指定索引的元素內容
```bash=
lindex <key> <index>
```

範例 : 
```bash=
127.0.0.1:6379> lpush sampleList two three
(integer) 2
127.0.0.1:6379> lrange sampleList 0 -1
1) "three"
2) "two"
127.0.0.1:6379> rpush sampleList one
(integer) 3
127.0.0.1:6379> lrange sampleList 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lindex sampleList 2
"one"
127.0.0.1:6379> lpop sampleList
"three"
127.0.0.1:6379> lrange sampleList 0 -1
1) "two"
2) "one"
127.0.0.1:6379> rpop sampleList
"one"
127.0.0.1:6379> lrange sampleList 0 -1
1) "two"
```

### Set
Set 是一個沒有排序的字串集合，一個集合可以對應到多個字串，但是集合內的值不會重複。

**sadd**
加入元素到集合裡，可以一次加入多個
```bash=
sadd <key> <member1> <member2> ... 
```
key 是集合的名稱，member 是集合內的值。如果加入成功會回傳 1，若已經在集合裡則會回傳 0。

**smembers**
列出集合內的所有元素
```bash=
smembers <key>
```

範例 : 
```bash=
127.0.0.1:6379> sadd week sunday monday tuesday
(integer) 3
127.0.0.1:6379> smembers week
1) "tuesday"
2) "monday"
3) "sunday"
127.0.0.1:6379> sadd week sunday
(integer) 0
127.0.0.1:6379> smembers week
1) "tuesday"
2) "monday"
3) "sunday"
```

### Sorted Set (zset)
Sorted Set 又稱 ZSet，是有序的集合，基本概念和 Set 一樣。差別在於會給每個元素一個 Score，用 Score 來做排序，而 Score 是可以重複的。

**zadd**
加入元素到集合裡，並指定 Score
```bash=
zadd <key> <score1> <member1> <score2> <member2> ...
```
score 可以指定元素的順序，可以重複。集合的排序是依照 score 由小排到大。

**zrange**
輸出指定區間的元素
```bash=
zrange <key> <start> <stop> withscores
```
指定輸出的區間概念和 lrange 一樣，withscores 可以選擇要不要加，有加會輸出 Score。

**zrangebyscore**
輸出指定 Score 區間的元素
```bash=
// 輸出 min <= score <= max 區間的元素
zrangebyscore <key> <min> <max> withscores
```
min 和 max 可以輸入 Score 的區間，withscores 一樣是可以選擇要不要加，有加會輸出 Score。

如果在 min 或 max 前面加上 `(` 代表開放區間，該 Score 會被排除。
```bash=
// 輸出 min < score <= max 區間的元素
zrangebyscore <key> (<min> <max> withscores

// 輸出 min <= score < max 區間的元素
zrangebyscore <key> <min> (<max> withscores

// 輸出 min < score < max 區間的元素
zrangebyscore <key> (<min> (<max> withscores
```

指定 -inf 到 +inf 可以輸出所有元素，代表 Score 從負無限到正無限。
```bash=
// 輸出所有元素
zrangebyscore <key> -inf +inf withscores
```

**zrange 和 zrangebyscore 的差別**

zrange 是指定元素的索引值，例如 : 0 是第一個值，1 是第二個值。

zrangebyscore 指定的是 Score，所以若是要輸出全部並不是像 range 傳 0 到 -1，而是傳 -inf 到 +inf，代表 Score 從負無限到正無限。

範例 : 
```bash=
127.0.0.1:6379> zadd week 0 sunday 2 tuesday 1 monday
(integer) 3 
127.0.0.1:6379> zrange week 0 -1 withscores          
1) "sunday"                                          
2) "0"                                               
3) "monday"                                          
4) "1"                                               
5) "tuesday"                                         
6) "2"                                               
127.0.0.1:6379> zrange week 0 -1                     
1) "sunday"                                          
2) "monday"                                          
3) "tuesday" 
127.0.0.1:6379> zrangebyscore week 0 3 withscores    
1) "sunday"                                          
2) "0"                                               
3) "monday"                                          
4) "1"                                               
5) "tuesday"                                         
6) "2"                                                             
127.0.0.1:6379> zrangebyscore week -inf +inf         
1) "sunday"                                          
2) "monday"                                          
3) "tuesday"    
127.0.0.1:6379> zrangebyscore week (0 2 withscores
1) "monday"
2) "1"
3) "tuesday"
4) "2"
127.0.0.1:6379> zrangebyscore week 0 (2 withscores
1) "sunday"
2) "0"
3) "monday"
4) "1"
127.0.0.1:6379> zrangebyscore week (0 (2 withscores
1) "monday"
2) "1"
```

### HyperLogLog
HyperLogLog 是用來做基數統計的，會統計不重複的元素有多少，例如統計頁面的訪問人數。

**pfadd**
加入元素到 HyperLogLog
```bash=
pfadd <key> <element1> <element2> ...
```

**pfcount**
統計 HyperLogLog 的基數預估值
```bash=
pfcount <key>
```
pfcount 會統計不重複的元素有多少並回傳輸出。

範例 : 
```bash=
127.0.0.1:6379> pfadd collection 1 1 2 2 3 3 3 4 4 5 5
(integer) 1
127.0.0.1:6379> pfcount collection
(integer) 5
```

### Bitmap
Bitmap 可以用一個 bit 來表示某個元素的值，即 0 和 1。透過這樣的方式組成的字串可以大大的節省儲存空間。

**setbit**
設定指定位元的值
```bash=
setbit <key> <offset> <value>
```
key 是這個 Bitmap 的名稱，offset 稱作位移量，是相對於讀取檔案的游標所在的位置的位移。在這裡只要想成是設定第幾個位元就好。而 value 只能是 1 或是 0。

setbit 指令完成後會回傳該位元被修改前的值。

**getbit**
取得指定位元的值
```bash=
getbit <key> <offset>
```

範例 : 
```bash=
127.0.0.1:6379> setbit testBit1 125 1
(integer) 0
127.0.0.1:6379> setbit testBit1 125 0
(integer) 1
127.0.0.1:6379> getbit testBit1 125
(integer) 0
127.0.0.1:6379> getbit testBit1 100
(integer) 0
127.0.0.1:6379> setbit testBit1 100 2
(error) ERR bit is not an integer or out of range
```
範例最後刻意測試輸入 value 不是 1 或 0，可以看到會報錯。


## Summary
以上僅列出了部分常用的資料型態操作，想要了解更多操作指令請參考 [Redis 官網](https://redis.io/topics/data-types-intro#hyperloglogs)。

[下一篇](https://tienyulin.github.io/redis-key-command/)我們將介紹如何管理和操作這些建立出來的 Key。

## 參考
[1] [讓網站速度飛快的秘密，你了解什麼是網頁快取(Cache)嗎?](https://progressbar.tw/posts/93)
[2] [資料庫的好夥伴：Redis](https://blog.techbridge.cc/2016/06/18/redis-introduction/)
[3] [深入理解Atomic原子操作和volatile非原子性](https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/436750/)
[4] [Java 原子操作的實現原理](https://twgreatdaily.com/MV61j2wBvvf6VcSZXwkq.html)
[5] [Redis 教程 | RUNOOB](https://www.runoob.com/redis/redis-tutorial.html)
[6] [An introduction to Redis data types and abstractions](https://redis.io/topics/data-types-intro#hyperloglogs)

###### tags: `Redis`