---
# System prepended metadata

title: Redis了解與應用
tags: [RD1]

---

# Redis了解與應用

###### tags: `RD1`

:::spoiler 目錄
[TOC]
:::

## 什麼是 Redis？

Redis是完全開源免費的，遵守BSD協議，是一個高性能的key-value數據庫。
Redis與其他key - value緩存產品有以下三個特點：
1. Redis支持數據的持久化，可以將內存中的數據保持在磁盤中，重啟的時候可以再次加載進行使用。
2. Redis不僅僅支持簡單的key-value類型的數據，同時還提供list，set，zset，hash等數據結構的存儲。
3. Redis支持數據的備份，即master-slave模式的數據備份。

### Redis數據類型

1. String（字符串）二進制安全的意思是redis的string可以包含任何數據。比如jpg圖片或者序列化的對象。
string類型是Redis最基本的數據類型，一個鍵最大能存儲512MB。
2.  Hash（哈希）
Redis hash是一個鍵值對集合。
Redis hash是一個string類型的field和value的映射表，hash特別適合用於存儲對象。每個hash可以存儲2^32 - 1鍵值對（40多億）。
3. List（列表）
Redis列表是簡單的字符串列表，按照插入順序排序。你可以添加一個元素導列表的頭部（左邊）或者尾部（右邊）。
4. Set（集合）
Redis的Set是string類型的無序集合。
集合是通過哈希表實現的，所以添加，刪除，查找的複雜度都是O(1)。
5. zset(sorted set：有序集合)
Redis zset和set一樣也是string類型元素的集合,且不允許重複的成員。
不同的是每個元素都會關聯一個double類型的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。zset的成員是唯一的,但分數(score)卻可以重複。
:angry: 
## Redis與關聯式資料庫差異
<br>

### SQL (關聯式) 與NoSQL (非關聯式) 資料庫的比較

<br>

幾十年來，用於開發應用程式的主要資料模型，均是諸如 Oracle、DB2、SQL Server、MySQL 和 PostgreSQL 等關聯式資料庫所採用的關聯式資料模型。一直到 2000 年中後期，其他資料模型才開始廣受採納和運用。為了區分和歸類這些新類型的資料庫和資料模組，因此創造了「NoSQL」這個名詞。「NoSQL」這個名詞常常與「非關聯式」互換使用。

雖然 NoSQL 資料類型多樣且功能各異，但您可從以下表格瞭解 SQL 和 NoSQL 資料庫的一些差異性


<br>
<br>
<br>


| 差異 | 關聯式資料庫  | NoSQL 資料庫  |
| -------- | -------- | -------- |
| 最佳工作負載     | 關聯式資料庫專門用於交易性以及高度一致性的線上交易處理 (OLTP) 應用程式，並且非常適合於線上分析處理 (OLAP) 使用。     | NoSQL 資料庫專門用於包含低延遲應用程式的多樣資料存取模式。NoSQL 搜尋資料庫專門用於進行半結構資料的分析。     |
| 資料模型     | 關聯式模型將資料標準化，成為由列和欄組成的表格。結構描述嚴格定義表格、列、欄、索引、表格之間的關係，以及其他資料庫元素。此類資料庫強化資料庫表格間的參考完整性。      | NoSQL 資料庫提供鍵值、文件和圖形等多種資料模型，具有最佳化的效能與規模。      |
| ACID 屬性     | 關聯式資料庫則提供單元性、一致性、隔離性和耐用性 (ACID) 的屬性<li>單元性要求交易完整執行或完全不執行。</li><li>一致性要求進行交易時資料就必須符合資料庫結構描述。</li><li>隔離性要求並行的交易必須分開執行。</li><li> 耐用性要求從意外的系統故障還原成上個已知狀態的能力</li>|  NoSQL 資料庫通常透過鬆綁部分關聯式資料庫的 ACID 屬性來取捨，以達到能夠橫向擴展的更彈性化資料模型。這使得 NoSQL 資料庫成為橫向擴展超過單執行個體上限的高吞吐量、低延遲使用案例的最佳選擇|
| 效能     | 一般而言，效能取決於磁碟子系統。若要達到頂級效能，通常必須針對查詢、索引及表格結構進行優化。     | 效能通常會受到基礎硬體叢集大小、網路延遲，以及呼叫應用程式的影響。     |
| 擴展     | 	關聯式資料庫通常透過增加硬體運算能力向上擴展，或以新增唯讀工作負載複本的方式向外擴展。     | NoSQL 資料庫通常可分割，因為存取模式可透過使用分散式架構來向外擴展，以近乎無限規模的方式提供一致效能來增加資料吞吐量。     |
| API     | 	存放和擷取資料的請求是透過符合結構式查詢語言 (SQL) 的查詢進行通訊。這些查詢是由關聯式資料庫剖析和執行。     | 以物件為基礎的 API 讓應用程式開發人員可輕鬆存放和擷取資料結構。應用程式可透過分區索引鍵查詢鍵值組、欄集，或包含序列化應用程式物件與屬性的半結構化文件。     |

### SQL 與NoSQL 術語的比較



| SQL | MongoDB | Couchbase |
| -------- | -------- | ----|
| 表     | 集  | 資料儲存貯體  |
| 列     | 列     | 	文件   |
| 欄	     | 欄	|    欄位   |
| 主索引鍵| 物件 ID  | 文件 ID |
| 索引     | 索引     | 索引  |
| 檢視     | 檢視     | 檢視  |
| 巢狀表格或物件| 	內嵌文件|對應 |
| 陣列     | 陣列     | 清單  |

由於 NoSQL 的種類很多，而技術的成熟度與使用場景不一，所以目前業界還是以 SQL 資料庫佔大多數。在 [Stackoverflow 2018 年的調查結果](https://insights.stackoverflow.com/survey/2019#technology-_-most-loved-dreaded-and-wanted-web-frameworks)，關聯式資料庫系統還是最多開發者使用的資料庫管理系統，佔前五名中的首四名！

![](https://i.imgur.com/l2BIHey.png)


### Redis 的優勢

 > 下方來源 [Redis](https://aws.amazon.com/tw/redis/)



#### 記憶體內資料存放區
所有 Redis 資料都放在伺服器的主要記憶體內，與 PostgreSQL、Cassandra、MongoDB 和其他將大多數資料存放在磁碟或 SSD 的資料庫並不相同。使用傳統磁碟的資料庫需要在磁碟來回處理才能執行大多數的操作，而 Redis 這類記憶體內資料存放區則不受此限制。這樣它們便能夠支援更大規模的操作，而且回應時間更快。這項優勢提供超快速的效能，平均讀取和寫入操作時間低於一毫秒，並支援每秒百萬個操作。

#### 彈性的資料結構
簡易的鍵值資料存放區提供的資料結構有限，而 Redis 則提供多樣化的資料結構以滿足應用程式的需要。Redis 資料類型包括：

* 字串 – 文字或二進位資料，最大 512 MB
* 清單 – 按新增順序排列的字串集合
* 資料集 – 未排序的字串集合，可與其他資料集類型交叉、合併和區分
* 排序資料集 – 依數值排列的資料集
* 雜湊 – 存放欄位和值清單的資料結構
* 點陣圖 – 提供位元層級操作的資料類型
* HyperLogLogs – 用來評估資料集獨特項目的概率資料結構'





## Redis基本操作方法 常用指令
 > 下方指令來源 [資料庫的好夥伴：Redis](https://blog.techbridge.cc/2016/06/18/redis-introduction/)



### SET, GET
```redis
redis> SET mykey "Hello"
redis> GET mykey
"Hello"
```
前面有提到說 Redis 是一個 key-value pair 的資料庫，因此最簡單的 SET 就是設定某個 key 的值是多少，要取出來的話就用 GET 就好。

### INCR, DECR
```redis
redis> SET mykey "10"
redis> DECR mykey
(integer) 9
redis> INCR mykey
(integer) 10
```
顧名思義就是針對某個 key 加一或減一的意思，像是程式語言裡面的```mykey++```跟```mykey--```。
還有 ```INCRBY``` 與 ```DECRBY```，可以指定你要加減的數量是多少。

### HSET, HGET

```redis
redis> HSET mydata name "nick"
redis> HSET mydata nickname "nicknick"
redis> HGET mydata name
"nick"
```
H 就是 Hashmap 的意思，所以你可以存取一個 value 底下的 field，讓你可以更多元的使用，例如說你可以定義 key 的規則是：POST + 文章 id，裡面就可以存這篇文章的讚數、回覆數等等，就不用每一次都去 Database 裡面重新抓取。

### SADD, SCARD
```redis
redis> SADD myset "nick"
redis> SADD myset "peter"
redis> SADD myset "nick"
redis> SCARD myset
(integer) 2
```
SADD 的 S 就是 ```Set``` 的意思，這邊的 ```Set``` 指的是資料結構學過的那個 ```Set```，裡面不會有重複的內容，第二次插入的元素會被忽略。

### ZADD
`zadd key score member `
和 set 一樣是string類型元素的集合，不允許重複的key
每個元素都會關聯一個 double 分數，會通過該分數為該集合排序
```redis
127.0.0.1:6379> ZADD myset 0 hi
(integer) 1
127.0.0.1:6379> ZADD myset 1.3 hi
(integer) 0
127.0.0.1:6379> ZADD myset 1.3 hey
(integer) 1
127.0.0.1:6379> ZADD myset 22 fine
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE myset 0 20
1) "hey"
2) "hi"
```

### LPUSH, RPUSH, LSET, LRANGE
```redis
redis> LPUSH mylist "a"
redis> LPUSH mylist "b"
redis> RPUSH mylist "c"
redis> LRANGE mylist 0 -1
1) "b"
2) "a"
3) "c"
redis> LSET mylist 0 "d"
redis> LRANGE mylist 0 -1
1) "d"
2) "a"
3) "c"
```
這邊的資料結構是 ```List```，你可以選擇從左邊或是右邊 push 值進去，對應到的指令就是 ```LPUSH``` 與 ```RPUSH```，```LSET``` 則是指定某個 index 的 value 是多少。

```LRANGE```可以印出指定範圍的值，支援```-1```這種形式，表示最後一個值。

### hash (HMSET,HGET)
hash 是 string 類型的 field-value 的映射表
```redis
127.0.0.1:6379> HMSET myset time 20:00 des "hi" type 1
OK
127.0.0.1:6379> HGETALL myset
1) "time"
2) "20:00"
3) "des"
4) "hi"
5) "type"
6) "1"
127.0.0.1:6379> HGET myset time
"20:00"
```
## Redis Commander

這裡我還沒看 先貼

[How To Configure Redis + Redis Commander + Docker](https://hackernoon.com/how-to-configurate-redis-redis-commander-docker-616136f2)

[redis-commander](https://hub.docker.com/r/rediscommander/redis-commander)




## Redis與資料庫資料同步解決方案

### 資料庫同步到Redis
我們大多傾向於使用這種方式，也就是將資料庫中的變化同步到Redis，這種更加可靠。Redis在這裡只是做緩存。

#### 方案1

做緩存，就要遵循緩存的語義規定：
讀：讀緩存redis，沒有，讀mysql，並將mysql的值寫入到redis。
寫：寫mysql，成功後，更新或者失效掉緩存redis中的值。

![](https://i.imgur.com/pCaQLtj.png)

對於一致性要求高的，從資料庫中讀，比如金融，交易等資料。其他的從Redis讀。

這種方案的好處是由mysql，常規的關係型資料庫來保證持久化，一致性等，不容易出錯。

#### 方案2

這裡還可以基於binlog使用mysql_udf_redis，將資料庫中的資料同步到Redis。

但是很明顯的，這將整體的複雜性提高了，而且本來我們在系統代碼中能很輕易完成的功能，現在需要依賴第三方工具，而且系統的整個邊界擴大了，變得更加不穩定也不好管理了。

![](https://i.imgur.com/KThiiQr.png)

### Redis同步到資料庫

也就是說將Redis中的資料變化同步到資料庫，那麼這裡是將Redis做為db，而真的db，資料庫只作為備份。（注意，這裡是一種不同看待事物的方式）。

這樣做的好處是：大大減小了資料庫的壓力，但是用redis做內存資料庫，狀態很不穩定。

雖然redis也有持久化機制，但是redis集群宕機後的重啟，資料加熱都很耗時。

另一方面，隨著大量插入或者更新導致redis持久化操作會嚴重拖累作為內存KV資料庫的優勢。

#### 方案1

將redis變更複制一份，丟到隊列中，給mysql消費。

![](https://i.imgur.com/yzDKVnB.png)

很明顯這種方案，只能保證最終一致性，而且變更資料複製，隊列維護，這些雜七雜八的東西太複雜，拋棄。

具體做法是：寫redis時，同時將資料寫到redis維護的另外一個隊列中，但這樣又要增加內存消耗了。

其實還有一種方式是使用redis的pipeline通知機制，但是redis是不保證的一定通知到的（得到被通知方的ack）。

#### 方案2

定時刷新redis中的最新資料到mysql。

很明顯的，無論定時任務的間距有多小，都會留下時間縫隙，如果發生宕機，故障等都會造成資料的不一致性。

雖然可以通過：比較redis和資料庫中的資料，同步那些需要同步的變化資料，但是會加大計算量和程序的複雜度。

## 參考資料

[該用 MySQL 或 MongoDB？選擇資料庫前你該了解的事](https://tw.alphacamp.co/blog/mysql-and-mongodb-comparison)

[什麼是 NoSQL？](https://aws.amazon.com/tw/nosql)

[Redis](https://aws.amazon.com/tw/redis/)

[SQL/NoSQL是什麼？認識資料庫管理系統DBMS](https://tw.alphacamp.co/blog/sql-nosql-database-dbms-introduction)

[10 分鐘徹底理解 Redis 的持久化和主從複製](https://iter01.com/437667.html)

[Redis 设计与实现-事务](https://redisbook.readthedocs.io/en/latest/feature/transaction.html)

[redis語法](https://redis.io/commands)
