# DB相關

# 【Redis】相關

 0 - 15 共16個資料庫 0為預設，使用select 切換

Redis 為何快

使用記憶體，單執行序，多路io複用

- NIO
- BIO
- AIO

[![image-1674755191601.png](https://bookstack.treemanou.com/uploads/images/gallery/2023-01/scaled-1680-/ssDFjB4pfwvxJCyb-image-1674755191601.png)](https://bookstack.treemanou.com/uploads/images/gallery/2023-01/ssDFjB4pfwvxJCyb-image-1674755191601.png)

[![image-1674754995171.png](https://bookstack.treemanou.com/uploads/images/gallery/2023-01/scaled-1680-/BVBaSynCiqGt7KKB-image-1674754995171.png)](https://bookstack.treemanou.com/uploads/images/gallery/2023-01/BVBaSynCiqGt7KKB-image-1674754995171.png)

【尚硅谷】2021最新Redis 6教程分布式锁，秒杀实战

- youtube  
    [https://www.youtube.com/playlist?list=PLmOn9nNkQxJHsATQDTW7Ci6X-Rcypgk2G](https://www.youtube.com/playlist?list=PLmOn9nNkQxJHsATQDTW7Ci6X-Rcypgk2G)
- 相關筆記  
    [https://s8jl-my.sharepoint.com/personal/atguigu\_s8jl\_onmicrosoft\_com/\_layouts/15/onedrive.aspx?id=%2Fpersonal%2Fatguigu%5Fs8jl%5Fonmicrosoft%5Fcom%2FDocuments%2F%E5%B0%9A%E7%A1%85%E8%B0%B7Redis6%E8%A7%86%E9%A2%91%E8%AF%BE%E7%A8%8B&amp;ga=1](https://s8jl-my.sharepoint.com/personal/atguigu_s8jl_onmicrosoft_com/_layouts/15/onedrive.aspx?id=%2Fpersonal%2Fatguigu%5Fs8jl%5Fonmicrosoft%5Fcom%2FDocuments%2F%E5%B0%9A%E7%A1%85%E8%B0%B7Redis6%E8%A7%86%E9%A2%91%E8%AF%BE%E7%A8%8B&ga=1)

# 【Redis】相關指令

### 相關指令

連線

```shell
# redis-cli -h {$host} -p {$port} -a password
# ping 檢測服務正常
$redis-cli -h 127.0.0.1 -p 6379 -a "mypass"
redis 127.0.0.1:6379>
redis 127.0.0.1:6379> PING

PONG
```

使用redis-cli 進入redis

```shell
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> 
```

進入db (具有16個

數據庫（DB）。這些數據庫編號從0到15，用於存儲不同的數據)

```shell
root@62952ea40eb3:/data# redis-cli
127.0.0.1:6379> 
```

查詢key (線上環境勿用， 請用scan)

```shell
#keys pattern
127.0.0.1:6379[2]> keys *
1) "v5.25.1_autocompleteKeywordV3_ASU"
2) "v5.25.1_autocompleteKeywordV3_AU"
3) "v5.25.1_autocompleteKeywordV3_ASUS"
4) "v5.25.1_autocompleteKeywordV3_AUS"
5) "v5.25.1_autocompleteKeywordV3_AS"
6) "v5.25.1_autocompleteKeywordV3_A"
7) "v5.25.1_autocompleteKeywordV3_AUU"
127.0.0.1:6379[2]> 

######################################
127.0.0.1:6379[2]> keys *autocompleteKeywordV3*
1) "v5.25.1_autocompleteKeywordV3_\xe9\x80\x81\xe5\xa5\xbd\xe7\xa6\xae"
2) "v5.25.1_autocompleteKeywordV3_\xe5\xb8\xb8\xe6\x98\xa5"
3) "v5.25.1_autocompleteKeywordV3_ASUS"
4) "v5.25.1_autocompleteKeywordV3_\xe5\xb8\xb8"
5) "v5.25.1_autocompleteKeywordV3_\xe7\x9b\xb4\xe6\x92\xad"
6) "v5.25.1_autocompleteKeywordV3_\xe7\x9b\xb4"
7) "v5.25.1_autocompleteKeywordV3_\xe9\x80\x81\xe5\xa5\xbd"
8) "v5.25.1_autocompleteKeywordV3_\xe7\x9b\xb4\xe6\x92\xad\xe6\xb8\x85\xe5\x96\xae"
9) "v5.25.1_autocompleteKeywordV3_\xe9\x80\x81"
```

查詢 sacn

```shell
# scan {inidex} match {pattern}
#返回分为两个部分如上面的代码中， 1）代表下一次迭代的游标，2）代表本次迭代的结果集
#，注意如果返回游标为0就代表全部匹配完成。
127.0.0.1:6379> scan 0 MATCH tony* 
1) "42"
2)  1) "tony25"
    2) "tony2519"
    3) "tony2529"
    4) "tony2510"
    5) "tony2523"
    6) "tony255"
    7) "tony2514"
    8) "tony256"
    9) "tony2511"
   10) "tony15"
127.0.0.1:6379> scan 42 MATCH tony* COUNT 1000
1) "0"
2)  1) "tony3513"
    2) "tony359"
    3) "tony4521"
    4) "tony356"
    5) "tony30"
    6) "tony320"
    7) "tony3"
    8) "tony312"
    
#批次刪除key
#redis-cli --scan --pattern "key前缀*" | xargs -L {筆數} redis-cli del
redis-cli --scan --pattern "tony*" | xargs -L 1000 redis-cli del
```

查詢資料

```shell
#get {key}
127.0.0.1:6379[2]> get v5.25.1_autocompleteKeywordV3_ASUS
"\x1f\x8b\b\x00\x00\x00\x00\x00\x00\x00\xc4W\xfdo\xdbD\x18\xfeW,\xf7\xd7.\xb9;;\x8e]\t\xa1.t\xa5b\x0b\xd5R`R\xd5\x1f\\\xe7\xe6Z\x8d}\xc6\x1fm\xd6(R\xf9\x81\x8d\xa8\xa0ubSUm\x141\x01b\x821Mh\xab`|\xfc1[\xbe\xfe\x0b\xce\x8e\xed\xd8m\xb2\xe4\xba\xc2\xaaV\xba\xdc{y\xde\xe7\xb9{\xde\xf7\xae\r\xde\xc5\xce\x16v\x96\x96\xf99\x1e\x82\x1cD\xf4O\xa0\xbf\xfc,\xef`\xd7\xafy%R\xc54\x86\x00H\xa6\xae`\xd7U\xf5`6\x99Z\xa8k\xd8\xf6\x0cb\r&?\xf5\xb1s\xe3}\xd5\xddH\xbe\x8d\x04P\x04\x12\x94\x10\x02\x05ID\x17.(\xc5\"(\x14\x059\x80u}M\xa3\x98\xfc\x9c\xe7\xf8x\x96\xc7u\xac\xf9\x01\xd8\x8aa\xd2o\xcbq\x92\xcb\x86\xeb\xf1s\xab\r^S=\\V\x83\x18?_\xf9\xa8\xc2\xf5n?\xe9>|D\x81\xb6\x89S5,\x9d\xce\xb7\xbf\xfe\xac\xdb\xfa\xb2sp\xd0k\xfd\xd8\xfe\xe3`\x18+\x91\x1aq\xe8\x82\x19A\x90\xa4K\x97h\xc0\xd0B\xda\x1b\x9eg\xbbs\xf9\xbca\xeab\xce$&q7\x88\x9d\xd3\x88\x99\xf3\xb6\xf3X3\x83@^3\xcd\xfc\xba\xa3Z\xd5|\x8d\xe8$\x8f \x10\x05\x10\xfc\xe4lK\x7f\xd7{\aJ\xb2,\t\xb0\x88\xa8\xc8@\x99\xaa\r6\xa5\x11\x8dVn\xd8\x945\x04q\xe4c\xb5\xe6\x87\x1b\x94\x00\xf1\xcd\xe6\xeci\x85\x9d\xd6^\xe7\xd1\xb7oE!\x92\x94\xf3Q\x18\x03\x8dVx\xf5\xc3\xc5\xfe\xfd\x17\xdd\x9f\x9f\x9f\x9fHaz\x91\x82\\<\x1f\x911\xd0@\xa4\xee\x10\xdf.\x13\x1a\x01\x90\xc2x\xb8N\r\xcc\xab\xae\xefr[\xc6\x16Y'd\x93\xb3\x1d\xc2A\x89#5\\\xa5K4\x8b\xaeP\x8at@\xfc`\xc8+\xc5\xb1\xf9Ex2\xffx\xe4fPe\xba\x8e\xdd`9\xad\xb4\xd5\xb5\t\xfc\xa0\x98\xa1\x04%0\xe4D?\xb0\x92\x8a\xe1N\xf1h\xc4Y?1\xac*\xd9\xa6+\xe1Y\xb1\x83\xce\xe19j\x14k\xf0T\xbf\x8d\x1d\xcf\xc0\x83<\xe6\xbc\xe7]\xa6C\xfe\xd5_\x0f:?<\xee\xfe\xf6\xa2\xfb\xeci\xd0}\xa2\xf9\xd54\x85\xb5Y\xde\"#\x0233\xf3\x00@e\x91\x1e1*\xd1\x91\xc4\xaf5\xd7\x9a\xe1aG:\x96,\x0f\xd7\xb8\x12q0g0\x9f\x1d\xab\x94\xde\xe1\xbd\xee\xfe\xcd\xf6\xe1O\x19\x1dY\nY)\x99X\xa8FL\xd4\x80B\xa4\xe65\xe6\xe8\x7f\xf5\xac{\xff\xfbW\xff|\xd3{|\xdc\xbb\xf9\x80\x0bi{\xb6\x1f\x1bE\x90R\xe6\xa5\x1f\x18v`,\xf4x\xd3,\xec\xd09m\xf3<\x92\x9c\xdc\xf2\xb0?\x0c\x92\x85\xc3\xa8Y\r3\x0efIx\xbdAz\x99!\x01\xd2\x0b\x0e\n\xb4\x97\xf3\x93v1\xcc\xba\xae\x88\x05p]Mv\x0e\xa56\x0e\xb1:'\x06\x1b\xbfW\x05\x00\x16/\x9e\x15vZCV*\xef\xb5\x7f\xfd\xbd\x7f\xebv\xc6\x90\x83\xd4Y#\x86s\xa1\x01%\x14\x18\x10R\x03\"q\xb2\x01CZ&\xbdJL-iNJ\xaa7)\xac\x12#,\xe6\x0e\xb9\x83\xad\xb0\xcd\xd2\x9a\xf5\xeb\x82\b\xd0Nr\x94b\xaa\x06D\xe6&0\n\x98\x85\x9d\x8d\xf5\x81\xab\x1d\xd5\xa8q\"\xa7{\xf5\x98\x18\x92\x87\xc4\x90\xcc@l\x14\xe6x\xab\x95\x97>X\xe0z\xbb\xfb\xed\xfd\xd6\x1b\xe6\x98\xb2*\xb3\t3\x95)\x01\x19\x06\xf5)#\xa5\x00&W\xe6\xcb\xdd}\xcb\xd8\xc4\x11\xd8\xcb\xdd;\x9d\xcf\x1f\xf6\x8e\xef\xf4\x8f\xf68\xd5p\xb8\x1dBL.\"\x9a\x14\xae\x90ny\x02\xcby\xb3dc6\xa8Ct\xce\xde \x16Nl)I)_J\x12\xab1\x87\x80\xcc\\<\xff:\xa7\xabf\xf0\xaa\x8b\xc9\xc80EFf\xbe\xf5S\x88\xe3\x8dxu\xe5\x1a'\x02z\xd5\xb7\xbf8x\x83\x04\xd3v\xbf\xce\xe1\xf3n\xebV\xa6\xf5e\x18d;`:\x146BA\x8eob\x05\x9c~W,W\xfe\a\x05\xfd\xef\x8e\xdaG{\xd9\xd7\x04M|\xe2\r\xb1\\\t\xf9\x02H\xf9B\x10\xbc\x1c\xe0\xe4\xc6\xcd\xe0u.|u\x0fz;*\xa4\x9a;*\xfc7\xc5\x15$d\xf6t\xef\xf8I\xf7\xee\xd3\x84(\x14RD\xc3\xff\xa0\x99\x0e+B{MS=\x9b\x8d\x13\x96SZ\xf8\x97\xbf\xdb\x7ffo\xef\xf2H\xf7\x96S\xc6\x05\x89q\x13#\x0c\x8d;_z[\xc4\x93\xccY\xe6\xf1\xf4I\xea(\xf10]\x9f9\xf7\xe6\xbf\x00\x00\x00\xff\xff\x03\x00\xd6\xdc\x935(\x11\x00\x00"

# 設定ttl
# expire {key} {秒數}
expire v5.25.1_autocompleteKeywordV3_ASUS 60

#獲得值的ttl
#ttl {key}
ttl v5.25.1_autocompleteKeywordV3_ASUS

```

查詢該DB 資料總數

```shell
# dbsize 該db儲存數
127.0.0.1:6379[2]> dbsize
(integer) 9
```

刪除該DB 所有資料

```shell
#清除目前db 所有資料
# flushdb

# 原本db有8筆資料
127.0.0.1:6379[2]> dbsize
(integer) 8

# 清除db
127.0.0.1:6379[2]> flushdb
OK

# 重新查詢已無資料
127.0.0.1:6379[2]> dbsize
(integer) 0
```

刪除該db資料

```shell
# del key1 key2 ... 
127.0.0.1:6379[2]> keys *
1) "v5.25.1_autocompleteKeywordV3_\xe9\x80\x81\xe5\xa5\xbd\xe7\xa6\xae"
2) "v5.25.1_autocompleteKeywordV3_\xe5\xb8\xb8\xe6\x98\xa5"
3) "v5.25.1_autocompleteKeywordV3_ASUS"
4) "v5.25.1_autocompleteKeywordV3_\xe5\xb8\xb8"
5) "v5.25.1_autocompleteKeywordV3_\xe7\x9b\xb4\xe6\x92\xad"
6) "v5.25.1_autocompleteKeywordV3_\xe7\x9b\xb4"
7) "v5.25.1_autocompleteKeywordV3_\xe9\x80\x81\xe5\xa5\xbd"
8) "v5.25.1_autocompleteKeywordV3_\xe7\x9b\xb4\xe6\x92\xad\xe6\xb8\x85\xe5\x96\xae"
9) "v5.25.1_autocompleteKeywordV3_\xe9\x80\x81"
127.0.0.1:6379[2]> del v5.25.1_autocompleteKeywordV3_ASUS
(integer) 1
127.0.0.1:6379[2]> keys *
1) "v5.25.1_autocompleteKeywordV3_\xe9\x80\x81\xe5\xa5\xbd\xe7\xa6\xae"
2) "v5.25.1_autocompleteKeywordV3_\xe5\xb8\xb8\xe6\x98\xa5"
3) "v5.25.1_autocompleteKeywordV3_\xe5\xb8\xb8"
4) "v5.25.1_autocompleteKeywordV3_\xe7\x9b\xb4\xe6\x92\xad"
5) "v5.25.1_autocompleteKeywordV3_\xe7\x9b\xb4"
6) "v5.25.1_autocompleteKeywordV3_\xe9\x80\x81\xe5\xa5\xbd"
7) "v5.25.1_autocompleteKeywordV3_\xe7\x9b\xb4\xe6\x92\xad\xe6\xb8\x85\xe5\x96\xae"
8) "v5.25.1_autocompleteKeywordV3_\xe9\x80\x81"
127.0.0.1:6379[2]> 
```

刪除所有redis資料

```shell
# 清除整台redis 資料
127.0.0.1:6379[2]> flushall
OK
```

---

#### 寫入資料(字串)

[![image-1698484020280.png](https://bookstack.treemanou.com/uploads/images/gallery/2023-10/scaled-1680-/FoLdeenEnVJKiJTT-image-1698484020280.png)](https://bookstack.treemanou.com/uploads/images/gallery/2023-10/FoLdeenEnVJKiJTT-image-1698484020280.png)

```
set <key> <value>: 寫入資料
NX：當數據庫中的鍵不存在時，可以將鍵-值添加到數據庫
XX：當數據庫中的鍵存在時，可以將鍵-值添加到數據庫，與 NX 參數互斥
EX：鍵的超時秒數
PX：鍵的超時毫秒數，與 EX 互斥
get <key>：查詢對應鍵值
append <key> <value>：將給定的 <value> 追加到原值的末尾
strlen <key>：獲得值的長度
setnx <key> <value>：只有在鍵不存在時設置鍵的值

incr <key>：將鍵中儲存的數字值增加1
只能對數字值操作，如果為空，新增值為1
decr <key>：將鍵中儲存的數字值減少1
只能對數字值操作，如果為空，新增值為-1
incrby / decrby <key> <步長>：將鍵中儲存的數字值增減。自定義步長。

mset <key1> <value1> <key2> <value2> ...：同時設置一個或多個鍵-值對
mget <key1> <key2> <key3> ...：同時獲取一個或多個值
msetnx <key1> <value1> <key2> <value2> ...：同時設置一個或多個鍵-值對，當且僅當所有給定的鍵都不存在。原子性，有一個失敗則都失敗

getrange <key> <起始位置> <結束位置>：獲得值的範圍，類似於 Java 中的 substring，前包，後包
setrange <key> <起始位置> <value>：用 <value> 覆蓋 <key> 所儲存的字符串值，從 <起始位置> 開始（索引從0開始）。

setex <key> <過期時間> <value>：設置鍵值的同時，設置過期時間，單位秒。
getset <key> <value>：以新換舊，設置了新值同時獲得舊值。
```

#### 寫入資料(List)

```
lpush/rpush <key> <value1> <value2> <value3> ...：從左邊/右邊插入一個或多個值。
lpop/rpop <key>：從左邊/右邊吐出一個值。值在鍵在，值光鍵亡。

rpoplpush <key1> <key2>：從 <key1> 列表右邊吐出一個值，插到 <key2> 列表左邊。

lrange <key> <start> <stop>：按照索引下標獲得元素（從左到右）。
lrange mylist 0 -1：0 左邊第一個，-1 右邊第一個，（0-1 表示獲取所有）。
lindex <key> <index>：按照索引下標獲得元素（從左到右）。
llen <key>：獲得列表長度。

linsert <key> before <value> <newvalue>：在 <value> 的後面插入 <newvalue>。
lrem <key> <n> <value>：從左邊刪除 n 個 value（從左到右）。
lset <key> <index> <value>：將列表 key 下標為 index 的值替換成 value。
```

#### 寫入資料(Set)

```
sadd <key> <value1> <value2> ...：將一個或多個 member 元素加入到集合 key 中，已經存在的 member 元素將被忽略。
smembers <key>：取出該集合的所有值。
sismember <key> <value>：判斷集合 <key> 是否含有該 <value> 值，有1，沒有0。
scard <key>：返回該集合的元素個數。
srem <key> <value1> <value2> ...：刪除集合中的某個元素。
spop <key>：隨機從該集合中吐出一個值。
srandmember <key> <n>：隨機從該集合中取出 n 個值。不會從集合中刪除。
smove <source> <destination> <value>：將集合中一個值從一個集合移動到另一個集合。
sinter <key1> <key2>：返回兩個集合的交集元素。
sunion <key1> <key2>：返回兩個集合的聯集元素。
sdiff <key1> <key2>：返回兩個集合的差集元素（key1 中的，不包含 key2 中的）。
```

#### 寫入資料(Zset)

```
zadd <key> <score1> <value1> <score2> <value2>…：將一個或多個 member 元素及其 score 值加入到有序集 key 當中。
zrange <key> <start> <stop> [WITHSCORES]：返回有序集 key 中，下標在 <start> 到 <stop> 之間的元素。帶有 WITHSCORES，可以讓分數一起和值返回到結果集。
zrangebyscore <key> <minmax> [withscores] [limit offset count]：返回有序集 key 中，所有 score 值介於 min 和 max 之間（包括等於 min 或 max）的成員。有序集成員按 score 值遞增（從小到大）次序排列。
zrevrangebyscore <key> <maxmin> [withscores] [limit offset count]：同上，改為從大到小排列。
zincrby <key> <increment> <value>：為元素的 score 加上增量。
zrem <key> <value>：刪除該集合下，指定值的元素。
zcount <key> <min> <max>：統計該集合，分數區間內的元素個數。
zrank <key> <value>：返回該值在集合中的排名，從0開始。
```

#### 寫入資料(Hash)

```
hset <key> <field> <value>：給 <key> 集合中的 <field> 鍵賦值 <value>
hget <key1> <field>：從 <key1> 集合 <field> 取出 value
hmset <key1> <field1> <value1> <field2> <value2>...：批量設置 hash 的值
hexists <key1> <field>：查看哈希表 key 中，給定域 field 是否存在。
hkeys <key>：列出該 hash 集合的所有 field
hvals <key>：列出該 hash 集合的所有 value
hincrby <key> <field> <increment>：為哈希表 key 中的域 field 的值加上增量 1 或 -1
hsetnx <key> <field> <value>：將哈希表 key 中的域 field 的值設置為 value，當且僅當域 field 不存在。
```

### 相關資源

- [在線練習 https://try.redis.io/](https://try.redis.io/)
- [文檔總覽 http://www.redis.cn/documentation.html](http://www.redis.cn/documentation.html)
- [命令文檔 http://www.redis.cn/commands.html](http://www.redis.cn/commands.html)
- [docker中安装并配置redis https://cloud.tencent.com/developer/article/1670205](https://cloud.tencent.com/developer/article/1670205)
- [【Redis】用Docker-Compose搭建Redis Sentinel](https://lipeng1667.github.io/2021/06/21/set-up-redis-with-replication-and-sentinel-using-docker-compose/ "【Redis】用Docker-Compose搭建Redis Sentinel")
- [【Redis】用Docker-Compose搭建Redis主从复制](https://lipeng1667.github.io/2021/06/20/install-redis-with-docker-compose-and-configurate-as-raplication/ "【Redis】用Docker-Compose搭建Redis主从复制")

# 資料庫型態比較

**DBMS DATA TYPE**

<table id="bkmrk-data-type-sql-92-ora" style="font-weight: 400;"><tbody><tr><td width="115">**DATA TYPE**</td><td width="144">**SQL 92**</td><td width="144">**Oracle**</td><td width="144">**MySQL**</td><td width="144">**POSTGRE**</td><td width="144">**M$ SQL**</td><td width="144">**Common**</td></tr><tr><td width="115">**BOOLEAN**</td><td width="144">BOOLEAN</td><td width="144"> </td><td width="144"> </td><td width="144">BOOL</td><td width="144"> </td><td width="144"> </td></tr><tr><td width="115">**CHARACTER**</td><td width="144">CHAR  
VARCHAR</td><td width="144">CHAR  
VARCHAR2 NVARCHAR2  
LONG  
TEXT  
ENUM  
SET</td><td width="144">CHAR  
VARCHAR  
BLOB  
TEXT  
ENUM  
SET</td><td width="144">CHAR  
VARCHAR  
TEXT  
NAME  
CHARACTER</td><td width="144">CHAR  
VARCHAR  
TEXT</td><td width="144">CHAR  
VARCHAR  
TEXT</td></tr><tr><td width="115">**BIT**</td><td width="144">BIT  
BIT VARYING</td><td width="144"> </td><td width="144"> </td><td width="144"> </td><td width="144">BIT</td><td width="144"> </td></tr><tr><td width="115">**EXACT NUMERIC**</td><td width="144">INTEGER  
SMALLINT  
DECIMAL  
NUMERIC</td><td width="144">INTEGER  
NUMBER  
NUMERIC  
SMALLINT  
DECIMAL</td><td width="144">INTEGER  
SMALLINT  
DECIMAL NUMERIC</td><td width="144">INTEGER  
DECIMAL  
INT2  
INT4  
INT8  
NUMERIC  
SERIAL</td><td width="144">INTEGER  
BIGINT  
SMALLINT  
TINYINT  
NUMERIC  
DECIMAL</td><td width="144">INTEGER  
NUMERIC  
DECIMAL</td></tr><tr><td width="115">**APPROXIMATE NUMERIC**</td><td width="144">FLOAT  
REAL  
DOUBLE PRECISION</td><td width="144">FLOAT  
REAL  
DOUBLE  
PRECISION</td><td width="144">FLOAT  
REAL  
DOUBLE  
PRECISION</td><td width="144">FLOAT  
REAL  
DOUBLE PRECISION  
FLOAT4  
FLOAT8</td><td width="144">FLOAT  
REAL  
DOUBLE PRECISION</td><td width="144">FLOAT  
REAL  
DOUBLE RECISION</td></tr><tr><td width="115">**DATETIME**</td><td width="144">DATE  
TIME  
TIMESTAMP</td><td width="144">DATE  
TIME  
DATETIME  
TIMESTAMP  
YEAR</td><td width="144">DATE  
TIME  
DATETIME  
TIMESTAMP  
YEAR</td><td width="144">DATE  
TIME  
TIMETZ  
TIMESTAMP  
CURRENT  
EPOCH  
INFINITY  
INVALID  
NOW  
TODAY  
TOMORROW  
YESTERDAY</td><td width="144">DATE  
TIME  
SMALLDATATIME  
TIMESTAMP  
UNIQUEIDENTIFIER</td><td width="144">TIMESTAMP  
DATE</td></tr><tr><td width="115">**INTERVAL**</td><td width="144">INTERVAL</td><td width="144"> </td><td width="144"> </td><td width="144">INTERVAL</td><td width="144"> </td><td width="144"> </td></tr><tr><td width="115">**BINARY**</td><td width="144"> </td><td width="144">ROWID  
NROWID  
RAW  
LONG RAW</td><td width="144"> </td><td width="144"> </td><td width="144">BINARY  
VARBINARY  
IMAGE</td><td width="144"> </td></tr><tr><td width="115">**LARGE OBJECTS**</td><td width="144">CHARACTER LARGE OBJECTBINARY LARGE OBJECT</td><td width="144">CHARACTER LARGE OBJECT BINARY LARGE OBJECT NATIONAL LARGE OBJECT</td><td width="144"> </td><td width="144"> </td><td width="144"> </td><td width="144"> </td></tr><tr><td width="115">**NET**</td><td width="144"> </td><td width="144"> </td><td width="144"> </td><td width="144">CIDR  
INET</td><td width="144"> </td><td width="144"> </td></tr><tr><td width="115">**Geometric**</td><td width="144"> </td><td width="144"> </td><td width="144"> </td><td width="144">BOX  
CIRCLE  
LINE  
LSEG  
PATH  
POINT  
POLYGON  
SERIAL</td><td width="144"> </td><td width="144"> </td></tr></tbody></table>

[來源：資料型態整理 Oracle, MySQL, PostgreSQL, M$ SQL - DBMS DATA TYPE - Soul &amp; Shell Blog (toright.com)](https://blog.toright.com/posts/27/%E8%B3%87%E6%96%99%E5%9E%8B%E6%85%8B%E6%95%B4%E7%90%86-oraclemysqlpostgrem-sql-dbms-data-type.html)

# DB  相關資源

- [ DB 排名 https://db-engines.com/en/ranking](https://db-engines.com/en/ranking)

# 【Debian】連線MariaDB

在Debian上安裝ODBC連接MariaDB，您可以按照以下步驟進行操作：

1. 確保您的Debian系統已連接到互聯網，並且具有管理員權限（或以root用戶身份登錄）。
2. 打開終端，執行以下命令更新匯總列表：
    
    <div><div></div><div>`sudo apt <span class="hljs-keyword">update</span>`</div></div>
3. 安裝ODBC驅動程序及相關工具：<button class="flex ml-auto gap-2"></button>
    
    <div><div>`sudo apt install unixODBC unixODBC-dev`</div></div>
4. 下載並安裝MariaDB的ODBC驅動程序。首先，訪問MariaDB的官方下載頁面（[https://downloads.mariadb.org/connector-odbc/）並選擇適合您Debian版本的驅動程序。然後使用wget命令下載驅動程序的安裝包。請選擇以下命令中的URL替換為您的驅動程序的下載鏈接：](https://downloads.mariadb.org/connector-odbc/%EF%BC%89%E5%B9%B6%E9%80%89%E6%8B%A9%E9%80%82%E5%90%88%E6%82%A8Debian%E7%89%88%E6%9C%AC%E7%9A%84%E9%A9%B1%E5%8A%A8%E7%A8%8B%E5%BA%8F%E3%80%82%E7%84%B6%E5%90%8E%E4%BD%BF%E7%94%A8wget%E5%91%BD%E4%BB%A4%E4%B8%8B%E8%BD%BD%E9%A9%B1%E5%8A%A8%E7%A8%8B%E5%BA%8F%E7%9A%84%E5%AE%89%E8%A3%85%E5%8C%85%E3%80%82%E8%AF%B7%E5%B0%86%E4%BB%A5%E4%B8%8B%E5%91%BD%E4%BB%A4%E4%B8%AD%E7%9A%84URL%E6%9B%BF%E6%8D%A2%E4%B8%BA%E6%82%A8%E9%80%89%E6%8B%A9%E7%9A%84%E9%A9%B1%E5%8A%A8%E7%A8%8B%E5%BA%8F%E7%9A%84%E4%B8%8B%E8%BD%BD%E9%93%BE%E6%8E%A5%EF%BC%9A)
    
    <div><div></div><div>`<span class="xml">wget <span class="hljs-tag"><<span class="hljs-name">驱动程序的下载链接</span>></span></span>`</div></div>
5. 解壓驅動程序的安裝包。使用以下命令，將`<package-name>`替換為您下載的安裝包的名稱：
    
    <div><div></div><div>`tar xvf <<span class="hljs-keyword">package</span>-name>.tar.gz`</div></div>
6. 進入解壓後的目錄：
    
    <div><div></div><div>`<span class="hljs-built_in">cd</span> <package-name>`</div></div>
7. 使用以下命令執行驅動程序的安裝。請注意，這裡的命令可能會因驅動程序的版本和您系統的配置而有所不同。確保按照驅動程序的安裝說明進行操作。
    
    <div><div></div><div>`sudo ./configuresudo <span class="hljs-built_in">make</span>sudo <span class="hljs-built_in">make</span> install`</div></div>
8. 配置ODBC連接。使用文本編輯器（如nano或vi）打開ODBC配置文件`/etc/odbcinst.ini`：
    
    <div><div></div><div>`sudo nano /etc/odbcinst.ini`</div></div>
9. 在配置文件中添加以下內容，以指定MariaDB的ODBC驅動程序：
    
    <div><div></div><div>`[MariaDB]Description = MariaDB ODBC driverDriver = <驱动程序的完整路径>`</div></div>命令`<驱动程序的完整路径>`替換為您安裝的驅動程序的實際路徑。保存並關閉文件。
10. 配置ODBC數據源。使用文本編輯器打開ODBC數據源配置文件`/etc/odbc.ini`：
    
    <div><div></div><div>`sudo nano /etc/odbc.ini`</div></div>
11. 在文件中添加以下內容，以指定您的MariaDB數據庫連接信息：
    
    <div><div></div><div>`[ODBC Data Source]Driver = MariaDBServer = <数据库服务器地址>Port = <数据库端口号>Database = <数据库名称>User = <用户名>Password = <密码>`</div></div>將`<数据库服务器地址>`、`<数据库端口号>`、`<数据库名称>`、`<用户名>`和`<密码>`替換為您實際的數據庫連接信息。保存並關閉文件。
12. 現在，您應該能夠使用ODBC連接到MariaDB數據庫了。您可以使用ODBC工具或編程語言中的ODBC庫來建立連接並執行查詢。

請注意，這些步驟提供了基本的安裝和配置過程，具體取決於您使用的驅動程序版本和系統配置。您可以根據驅動程序的安裝說明進行操作，並在遇到問題時參考相關文檔和資源。

# 【Redis】設定相關

/etc/redis.conf

or

/usr/local/etc/redis/redis.conf

- #### 開放外部連線

```INI
# 只支持本地
bind 127.0.0.1 -::1
protected-mode yes

# 要支援遠端存取註解 bind 127.0.0.1 -::1 
# protected-mode no
# bind 127.0.0.1 -::1
protected-mode no
```

#### 其他設定

```INI
# 未完成三向交握＋已完成三向交握上限
tcp-backlog 511

# Close the connection after a client is idle for N seconds (0 to disable)
# 0: 永不過期
timeout 0

# 檢測連線存在的間隔時間
tcp-keepalive 300

# 使用daemon啟動
daemonize yes

# pid檔案位置
# 如果指定了pid文件，Redis在啟動時會將其寫入指定位置，並在退出時刪除它。
# 當服務器以非守護模式運行時，如果在配置中未指定pid文件，則不會創建pid文件。當服務器以守護模式運行時，即使未指定，也將使用pid文件，默認為 "/var/run/redis.pid"。
# 創建pid文件是盡力而為的：如果Redis無法創建它，不會發生任何壞事，服務器將正常啟動和運行。
# 請注意，在現代Linux系統上，應改用 "/run/redis.pid"，因為這更符合規範。
pidfile /var/run/redis_6379.pid

# log 級別 
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice

# 設定db數量
databases 16

# 設置密碼
# requirepass {密碼}
requirepass foobared

# 使用命令臨時設定密碼(服務重啟失效)
127.0.0.1:6379>config set requirepass "123456"
# 登入
127.0.0.1:6379>auth 123456

# 最大連接數(預設無)
maxclients 10000
# 最大記憶體使用量(預設無)
maxmemory <bytes>


```

#### 持久化(快照)(RDB)

```INI
# 持久化(快照)(REB)
# 有可能最後一次資料丟失
# 寫入檔名
dbfilename dump.rdb
# 寫入目錄(啟動redis命令當前目錄)
dir ./
# 快照設定

# 3600秒內有1個key異動
# save 3600 1
# 300秒內有100個key異動
# save 300 100
# 60秒內有10000個key異動
# save 60 10000

# Redis 無法寫入檔案，關閉寫入操作
stop-writes-on-bgsave-error yes
# 文件進行壓縮
rdbcompression yes
# 文件檢查(影響10%效能)
rdbchecksum yes

# 如何恢復 ：
# 關閉 redis 將原先dump.rdb 放回執行目錄下，啟動 redis，會自動載入資料
```

#### 持久化(AOF)

使用log方式存放，只記錄寫的指令，不紀錄讀

<span style="background-color: #ffff00;">RDB &amp; AOF 同時開啟，只會載入AOF資料</span>

```INI
# 開啟AOF模式
# 預設 no
appendonly yes
# 檔案名稱
appendfilename "appendonly.aof"
# 目錄名稱(上層預設為啟動目錄)
appenddirname "appendonlydir"
# aof 同步頻率
# appendfsync always|everysec|no
# always:  即時同步(每次寫入同步)
# everysec：每秒同步
# no: 同步時機交給作業系統
# http://antirez.com/post/redis-persistence-demystified.html
appendfsync everysec

# no-appendfsync-on-rewrite 預設no
# 重寫壓縮指令 將多筆設定令重寫成一筆指令
# set A1 1
# set B1 3
# set A1 1 B1 3 => 壓縮重寫
no-appendfsync-on-rewrite yes
# 觸發比例
auto-aof-rewrite-percentage 100
# 觸發檔案大小
auto-aof-rewrite-min-size 64mb

# 如何恢復 ：
# 關閉 redis 將原先appendonly.aof 放回執行目錄下，啟動 redis，會自動載入資料

# aof 檔案損壞
# /usr/local/bin/redis-check-aof --fix appendonly.aof
# 備份該檔
# 重啟服務

```

##### RDB &amp; AOF 使用時機

- 官方推薦 都開
- 資料不嚴謹，可單獨使用RDB
- 不建議單獨使用AOF，可能有BUG
- Redis <span class="s1">只做</span>cache <span class="s1">用，可都不開</span>

---

### <span class="s1">sentinel config</span>

```INI
sentinel monitor mymaster 192.168.1.1 6379 2
sentinel auth-pass mymaster 12345678
```

https://hsiehjenhsuan.medium.com/spring-boot-%E4%BD%BF%E7%94%A8-lettuce-%E8%A8%AD%E5%AE%9A%E5%A4%9A%E5%80%8B-redis-%E9%80%A3%E7%B7%9A-55307dc6a480

# 【Redis】安裝

[https://redis.io/docs/install/install-redis/install-redis-from-source/](https://redis.io/docs/install/install-redis/install-redis-from-source/)

```shell
# download
wget https://download.redis.io/redis-stable.tar.gz

tar -xzvf redis-stable.tar.gz
cd redis-stable
make

sudo make install

# run server 
redis-server
```

# 【Redis相關】RedisSDK

```Java
import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/***
 *  @date 2018/9/25 15:07
 * @description:
 */
public class RedisSDK {

    /**
     * 日誌
     */
    public static final Logger LOG = LoggerFactory.getLogger(RedisSDK.class);

    private RedisTemplate<String, Object> redisTemplate;

    private static final String TEMP_STR1 = ",value:";

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    //=============================common============================

    /**
     * 指定緩存失效時間
     *
     * @param key  鍵
     * @param time 時間(秒)
     * @return
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根據key 獲取過期時間
     *
     * @param key 鍵 不能為null
     * @return 時間(秒) 返回0代表為永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判斷key是否存在
     *
     * @param key 鍵
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 刪除緩存
     *
     * @param key 可以傳一個值 或多個
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null &amp;&amp; key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

    //============================操作String=============================

    /**
     * 普通緩存獲取
     *
     * @param key 鍵
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    public String getString(String key) {
        Object result = get(key);
        return result == null ? null : result.toString();
    }


    /**
     * 普通緩存放入
     *
     * @param key   鍵
     * @param value 值
     * @return true成功 false失敗
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

    /**
     * 判斷是否存在
     * @param key
     * @return
     */
    public boolean exists(String key) {
        try {
            return redisTemplate.execute(new RedisCallback<Boolean>() {
                @Override
                public Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {
                    return redisConnection.exists(key.getBytes());
                }
            });
        } catch (Exception e) {
            LOG.error("", e);
        }
        return false;
    }

    /**
     * 普通緩存放入並設置時間
     *
     * @param key   鍵
     * @param value 值
     * @param time  時間(秒) time要大於0 如果time小於等於0 將設置無限期
     * @return true成功 false 失敗
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 遞增
     *
     * @param key 鍵
     * @param by  要增加幾(大於0)
     * @return
     */
    public long incr(String key, long by) {
        if (by < 0) {
            throw new RuntimeException("遞增因子必須大於0");
        }
        return redisTemplate.opsForValue().increment(key, by);
    }

    /**
     * 遞減
     *
     * @param key 鍵
     * @param by  要減少幾(小於0)
     * @return
     */
    public long decr(String key, long by) {
        if (by < 0) {
            throw new RuntimeException("遞減因子必須大於0");
        }
        return redisTemplate.opsForValue().increment(key, -by);
    }

    //===============================操作Map=================================

    /**
     * HashGet
     *
     * @param key  鍵 不能為null
     * @param item 項 不能為null
     * @return 值
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 獲取hashKey對應的所有鍵值
     *
     * @param key 鍵
     * @return 對應的多個鍵值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     *
     * @param key 鍵
     * @param map 對應多個鍵值
     * @return true 成功 false 失敗
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * HashSet 並設置時間
     *
     * @param key  鍵
     * @param map  對應多個鍵值
     * @param time 時間(秒)
     * @return true成功 false失敗
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一張hash表中放入數據,如果不存在將創建
     *
     * @param key   鍵
     * @param item  項
     * @param value 值
     * @return true 成功 false失敗
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一張hash表中放入數據,如果不存在將創建
     *
     * @param key   鍵
     * @param item  項
     * @param value 值
     * @param time  時間(秒)  注意:如果已存在的hash表有時間,這裡將會替換原有的時間
     * @return true 成功 false失敗
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 刪除hash表中的值
     *
     * @param key  鍵 不能為null
     * @param item 項 可以使多個 不能為null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判斷hash表中是否有該項的值
     *
     * @param key  鍵 不能為null
     * @param item 項 不能為null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash遞增 如果不存在,就會創建一個 並把新增後的值返回
     *
     * @param key  鍵
     * @param item 項
     * @param by   要增加幾(大於0)
     * @return
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash遞減
     *
     * @param key  鍵
     * @param item 項
     * @param by   要減少記(小於0)
     * @return
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }


    /**
     * @param key
     * @return
     */
    public <T> T hgetAll(String key, Class<T> clazz) {
        try {
            Map<Object, Object> values = hmget(key);
            LOG.info("redis獲取值,key:" + key + TEMP_STR1 + JSONObject.toJSONString(values));
            String json = JSONObject.toJSONString(values);
            Map<String, String> all = (Map) JSONObject.parse(json);
            T t = clazz.newInstance();
            BeanUtils.populate(t, all);
            return t;
        } catch (Exception e) {
            LOG.info("redis獲取值,key:" + key + "失敗");
        }
        return null;
    }

    //============================操作set=============================

    /**
     * 根據key獲取Set中的所有值
     *
     * @param key 鍵
     * @return
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根據value從一個set中查詢,是否存在
     *
     * @param key   鍵
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 將數據放入set緩存
     *
     * @param key    鍵
     * @param values 值 可以是多個
     * @return 成功個數
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 將set數據放入緩存
     *
     * @param key    鍵
     * @param time   時間(秒)
     * @param values 值 可以是多個
     * @return 成功個數
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0) {
                expire(key, time);
            }
            return count == null ? 0L : count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 獲取set緩存的長度
     *
     * @param key 鍵
     * @return
     */
    public long sGetSetSize(String key) {
        try {
            Long size = redisTemplate.opsForSet().size(key);
            return size == null ? 0L : size;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 移除值為value的
     *
     * @param key    鍵
     * @param values 值 可以是多個
     * @return 移除的個數
     */
    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count == null ? 0L : count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    //===============================操作list=================================

    /**
     * 獲取list緩存的內容
     *
     * @param key   鍵
     * @param start 開始
     * @param end   結束  0 到 -1代表所有值
     * @return
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 獲取list緩存的長度
     *
     * @param key 鍵
     * @return
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通過索引 獲取list中的值
     *
     * @param key   鍵
     * @param index 索引  index>=0時, 0 表頭,1 第二個元素,依次類推;index<0時,-1,表尾,-2倒數第二個元素,依次類推
     * @return
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 將list放入緩存
     *
     * @param key   鍵
     * @param value 值
     * @return
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 將list放入緩存
     *
     * @param key   鍵
     * @param value 值
     * @param time  時間(秒)
     * @return
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 將list放入緩存
     *
     * @param key   鍵
     * @param value 值
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 將list放入緩存
     *
     * @param key   鍵
     * @param value 值
     * @param time  時間(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根據索引修改list中的某條數據
     *
     * @param key   鍵
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 移除N個值為value
     *
     * @param key   鍵
     * @param count 移除多少個
     * @param value 值
     * @return 移除的個數
     */
    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 發佈消息
     *
     * @param channel
     * @param message
     */
    public void sendMessage(String channel, Serializable message) {
        redisTemplate.convertAndSend(channel, message);
    }


}
```

# DB 欄位命名與比較

資料庫欄位命名的選擇對於系統的可讀性、一致性和維護性有著重要的影響。不同的資料庫可能有不同的命名慣例，以下是常見的命名方式及其在各種資料庫中的應用。

以下是常見命名方式與對應資料庫的整理表格：

<table id="bkmrk-%E5%91%BD%E5%90%8D%E6%96%B9%E5%BC%8F-%E5%91%BD%E5%90%8D%E7%89%B9%E5%BE%B5-%E6%87%89%E7%94%A8%E8%B3%87%E6%96%99%E5%BA%AB-%E9%A7%9D%E5%B3%B0%E5%BC%8F%E5%91%BD"><thead><tr><th>**命名方式**</th><th>**命名特徵**</th><th>**應用資料庫**</th></tr></thead><tbody><tr><td>**駝峰式命名法**</td><td>單字首字母小寫，後續單字首字母大寫，如：`firstName`</td><td>**MongoDB**、**Cassandra**：與程式碼結合較緊密的 NoSQL 資料庫常使用</td></tr><tr><td>**蛇形命名法**</td><td>單字之間用底線 `_` 分隔，全小寫，如：`first_name`</td><td>**PostgreSQL**、**MySQL**、**Oracle**、**SQLite**：傳統 SQL 資料庫中最常見</td></tr><tr><td>**帕斯卡命名法**</td><td>每個單字首字母大寫，沒有分隔符號，如：`FirstName`</td><td>**Microsoft SQL Server**、**Entity Framework**：與程式碼結合時偶爾使用</td></tr><tr><td>**中線命名法**</td><td>單字之間用連字符 `-` 分隔，全小寫，如：`first-name`</td><td>**Elasticsearch**：主要用於索引和類型，但不常用於欄位</td></tr><tr><td>**全小寫命名法**</td><td>全部字母小寫，無分隔符號，如：`firstname`</td><td>**MySQL**、**SQLite**：為了避免大小寫區分問題，常用於輕量或無區分大小寫的系統</td></tr><tr><td>**前綴或後綴命名法**</td><td>欄位名稱加上描述性前綴或後綴，如：`user_id`、`created_at`</td><td>**PostgreSQL**、**MySQL**、**SQL Server**、**MongoDB**：常用於表示主鍵、外鍵或時間戳等特定欄位</td></tr></tbody></table>

此表格總結了各種命名方式的特徵和常用的資料庫，選擇命名方式時，應根據資料庫特性和開發需求來做決定，並保持專案中的一致性。

### 常見欄位命名方式及應用

#### 1. **駝峰式命名法（Camel Case）**

- **命名法**：單字首字母小寫，接下來每個單字的首字母大寫。
- **範例**：`firstName`、`lastName`、`orderDate`
- **應用資料庫**： 
    - **MongoDB**：作為 NoSQL 資料庫，MongoDB 使用 JSON/BSON 結構，開發者常使用駝峰式命名與程式碼一致。
    - **Cassandra**：由於 Cassandra 支援靈活的資料模式，駝峰式命名法也被頻繁採用，尤其在與程式碼整合時。

#### 2. **蛇形命名法（Snake Case）**

- **命名法**：單字之間用底線 `_` 分隔，所有字母小寫。
- **範例**：`first_name`、`last_name`、`order_date`
- **應用資料庫**： 
    - **PostgreSQL**：最常見的命名方式，與 SQL 語法兼容性好，避免大小寫問題。
    - **MySQL**：由於 MySQL 預設不區分大小寫，蛇形命名法常被使用以保持命名的一致性和可讀性。
    - **Oracle**：儘管 Oracle 支援大小寫區分，蛇形命名法依然流行。
    - **SQLite**：輕量級資料庫中也常見此命名方式，方便管理。

#### 3. **帕斯卡命名法（Pascal Case）**

- **命名法**：每個單字首字母大寫，沒有分隔符號。
- **範例**：`FirstName`、`LastName`、`OrderDate`
- **應用資料庫**： 
    - **Microsoft SQL Server**：部分開發者，特別是使用 Microsoft 技術堆疊的，會選擇帕斯卡命名法以與程式命名風格保持一致。
    - **Entity Framework**：當與 SQL Server 或其他資料庫結合時，帕斯卡命名法常用於映射資料庫欄位和程式屬性名稱。

#### 4. **中線命名法（Kebab Case）**

- **命名法**：單字之間用連字符 `-` 分隔，所有字母小寫。
- **範例**：`first-name`、`last-name`、`order-date`
- **應用資料庫**： 
    - **Elasticsearch**：儘管大部分資料庫不支援欄位中使用連字符，中線命名偶爾會出現在 Elasticsearch 的索引或類型中，但在欄位名稱中較少見。

#### 5. **全小寫命名法**

- **命名法**：所有字母均小寫，無任何分隔符號。
- **範例**：`firstname`、`lastname`、`orderdate`
- **應用資料庫**： 
    - **MySQL**：由於 MySQL 不區分大小寫，許多開發者會選擇全小寫命名來避免混淆。
    - **SQLite**：小型應用中，開發者常使用全小寫命名來簡化欄位管理。

#### 6. **前綴或後綴命名法**

- **命名法**：在欄位名稱前或後添加描述性前綴或後綴。
- **範例**：`user_id`、`created_at`
- **應用資料庫**： 
    - **PostgreSQL**、**MySQL**、**SQL Server**：此命名法常見於資料表中，特別是主鍵、外鍵或時間戳等欄位，如 `id`、`created_at`。
    - **MongoDB**：在 NoSQL 環境中，這種命名法也被用來標記欄位的特定用途，便於區分不同類型的資料。

### 小結

- **SQL 資料庫（PostgreSQL、MySQL、SQL Server、Oracle）**：推薦使用 **蛇形命名法**，這是一種最常見的標準化命名方式，能在 SQL 查詢中避免大小寫問題，並提高可讀性。
- **NoSQL 資料庫（MongoDB、Cassandra、Elasticsearch）**：**駝峰式命名法** 和 **全小寫命名法** 較為常見，這些資料庫的靈活數據模型和與 JSON 的緊密結合，使得駝峰式命名更符合程式開發習慣。

選擇合適的命名方式應根據具體資料庫、開發團隊的需求和維護便利性來決定，並保持整個專案中的一致性。

# 【Redis】Sentinel 相關指令

下面整理一份「常用 Redis Sentinel 指令速查」（以你目前 master name = `mymaster` 為例）。  
先記得：Sentinel 是連 **26379**（或你映射的 26380/26381），而且很多命令需要先 AUTH（若你 Sentinel 有開帳密）。

---

## 連線與基本檢查

```bash
redis-cli -h <sentinel_ip> -p 26379
redis-cli -h <sentinel-host> -p 26379 --user <sentinel-user> -a <sentinel-pass>

```

- 測連線

```redis
PING

10.10.10.202:26379> ping
PONG
```

- 目前登入的 ACL user（若開 ACL）

```redis
ACL WHOAMI

```

- 授權（若需要）

```redis
AUTH <user> <password>
# 或舊式
AUTH <password>

```

---

## 查 master / topology（最常用）

- 查所有被監控的 masters（摘要列表）

```redis
SENTINEL masters

1)  1) "name"
    2) "mymaster"
    3) "ip"
    4) "10.10.10.202"
    5) "port"
    6) "6379"
    7) "runid"
    8) "3bf7dbeca2952f3b9bbe3961b2b75bc3c03bc040"
    9) "flags"
   10) "master"
   11) "link-pending-commands"
   12) "0"
   13) "link-refcount"
   14) "1"
   15) "last-ping-sent"
   16) "0"
   17) "last-ok-ping-reply"
   18) "533"
   19) "last-ping-reply"
   20) "533"
   21) "down-after-milliseconds"
   22) "30000"
   23) "info-refresh"
   24) "7634"
   25) "role-reported"
   26) "master"
   27) "role-reported-time"
   28) "44657033"
   29) "config-epoch"
   30) "9"
   31) "num-slaves"
   32) "2"
   33) "num-other-sentinels"
   34) "2"
   35) "quorum"
   36) "2"
   37) "failover-timeout"
   38) "180000"
   39) "parallel-syncs"
   40) "1"

```

- 查特定 master 的詳細資訊

```redis
SENTINEL master mymaster

```

- 取得 master 的位址（最常用，拿 IP/Port）

```redis
SENTINEL get-master-addr-by-name mymaster

```

- 查 master 的 replicas（從節點清單）

```redis
SENTINEL replicas mymaster
# 舊名（相容）
SENTINEL slaves mymaster

```

- 查同一個 master 的其他 sentinels

```redis
SENTINEL sentinels mymaster

```

---

## 健康/狀態判斷（排錯必備）

- 看 master 是否被標記 down、目前狀態旗標

```redis
SENTINEL master mymaster
# 觀察 "flags"、"last-ok-ping-reply"、"s-down-time" 等欄位

```

- Sentinel 自己資訊（版本、runid、監控數量）

```redis
INFO

```

- 看 Sentinel 的事件（需要開啟通知才會比較多）

```redis
SENTINEL ckquorum mymaster

```

> 這個會告訴你 quorum 是否足夠（例如你設定 quorum=2，是否能形成共識）。

---

## 故障轉移操作（小心用）

- 立即對指定 master 觸發 failover

```redis
SENTINEL failover mymaster

```

- 中止正在進行的 failover（如果卡住）

```redis
SENTINEL abortfailover mymaster

```

- 主動要求 Sentinel 重新掃描、更新配置（常用在手動改 IP/Port 後）

```redis
SENTINEL reset mymaster
# 或 reset 全部（小心）
SENTINEL reset *

```

---

## 動態改監控設定（線上調參）

- 修改 down-after-milliseconds（多久沒回就判定 down）

```redis
SENTINEL set mymaster down-after-milliseconds 30000

```

- 修改 failover-timeout（failover 超時）

```redis
SENTINEL set mymaster failover-timeout 180000

```

- 修改 parallel-syncs（一次同步幾台 replica）

```redis
SENTINEL set mymaster parallel-syncs 1

```

- 修改 quorum（通常不建議頻繁改）

```redis
SENTINEL set mymaster quorum 2

```

- 查看目前可用的 set 參數（不同版本可能略不同）

```redis
SENTINEL help

```

---

## 通知與腳本（你目前有 deny）

你現在設定：

```conf
sentinel deny-scripts-reconfig yes

```

所以 sentinel 腳本重配置會被限制。常見指令/配置（了解用）：

- 設定通知腳本（在 config 檔內）

```conf
sentinel notification-script mymaster /path/to/script
sentinel client-reconfig-script mymaster /path/to/script

```

---

## 常用排錯流程（超快）

1. Sentinel 能不能連：

```redis
PING

```

2. master 位址：

```redis
SENTINEL get-master-addr-by-name mymaster

```

3. master 狀態：

```redis
SENTINEL master mymaster

```

4. replicas 狀態：

```redis
SENTINEL replicas mymaster

```

5. quorum 是否健康：

```redis
SENTINEL ckquorum mymaster

```

---

## 小提醒（你很常遇到的）

- **Sentinel 不支援 `SELECT`**，因為它不是資料庫服務，是監控/仲裁服務。
- `SENTINEL get-master-addr-by-name` 回的 IP/HostName 對外能不能用，取決於你的網路（Docker 內外網、DNS、announce 等設定）。