Redis 學習筆記
- NoSQL 概述
- 什么是NoSQL
- 阿里巴巴演進分析
- NoSQL 四大分類
- Redis 入門
- Docker 安裝
- Redis-benchmark 性能測驗工具
- 基礎知識
- 五大基本資料型別
- redis-key
- String(字串)
- List(串列)
- Set(集合)
- Hash(哈希)
- Zset(有序集合)
- 三種特殊資料型別
- geospatial 地理位置
- hyperloglogs
- Bitmaps
- 事務
- 編譯型例外
- 運行時例外
- 監控
- Jedis
- 常用Api
- 事務
- redis.conf 詳情
- Redis 持久化
- RDB
- AOF
- 總結
- Redis 訂閱發布
- 命令
- 主從復制
NoSQL 概述
-
單機 MySQL 年代
1. 資料量太大 2. 資料索引太大 3. 訪問量(讀寫混合)過高 -
Memcached(快取)+ MySQL + 垂直拆分(讀寫分離)
發展程序:優化資料結構和索引 -> 檔案快取 -> Memcached
解決 讀 的操作壓力
- 分庫分表 + 水平拆分(集群)
解決 寫 的操作壓力
- 現在時代
資料量太大,關系型資料庫不夠用
用戶自己產生的資訊,地理位置,博客,視頻等
什么是NoSQL
關系型資料庫:表格,行,列
非關系型資料庫:一些資料不需要固定格式,不需要多余的操作就可以橫向擴展
特點:
- 方便擴展,資料之間沒有關系
- 大資料量,高性能(Redis 一秒寫8萬次,讀11萬次,是一種細粒度快取,性能比較高)
- 資料型別多樣(不需要事先設計資料庫)
- 傳統 RDBMS 和 NoSQL
傳統的 RDBMS
- 結構化組織
- SQL
- 資料和關系都存在單獨的表
- 操作,資料定義語言
- 嚴格的一致性
- 基礎的事務
- ......
NoSQL
- 不僅僅是資料
- 沒有固定的查詢語言
- 鍵值對存盤,列存盤,檔案存盤,圖形資料庫
- 最終一致性
- CAP 定理和 BASE(異地多活)初級架構師!
- 高性能,高可用,高可擴
- ......
(大資料時代)了解 3V + 3高:
3V 主要描述問題:
- 海量
- 多樣
- 實時
3高 主要描述對程式問題
- 高并發
- 高可擴
- 高性能
阿里巴巴演進分析
推薦書籍:(王堅:阿里云的這群瘋子)
如果未來當一個架構師:沒有什么是加一層解決不了的
# 1. 商品基本資訊
名稱、價格、商家資訊
關系型資料庫:MySQL / Oracle (淘寶早年就去IOE(在阿里巴巴的IT架構中,去掉IBM的小型機、Oracle資料庫、EMC存盤設備,代之以自己在開源軟體基礎上開發的系統)!)
淘寶內部的 MySQL 不是大家用的 MySQL,他根據自己的需求做了相應修改
# 2. 商品描述、評論(文字比較多)
檔案資料庫 MangoDB
# 3. 圖片
分布式檔案系統 FastDFS
- 淘寶自己的 TFS
- Google GFS
- Hadoop HDFS
- 阿里云 OSS
# 4. 商品關鍵詞(搜索)
- 搜索引擎 solr elasticsearch
- ISerach 淘寶 多隆
# 5. 商品熱門的波段資訊
- 記憶體資料庫
- redis tair memache
# 6. 商品的交易,外部的支付介面
- 三方應用
大型互聯網應用問題
- 資料型別太多
- 資料源多,經常重構
- 資料要改造,大面積改造
解決方案:UDSL(統一資料服務平臺)
NoSQL 四大分類
鍵值對
- 新浪:redis
- 美團:redis + Tair
- 阿里、百度:Redis + memecache
檔案資料庫
-
MongoDB
- 是一個基于分布式檔案存盤的資料庫,c++撰寫,主要用來處理大量檔案!
- 是一個介于關系型資料庫和非關系型資料庫之間的中間產品,MongoDB是關系型資料庫中功能最豐富,最像關系型資料庫的
圖關系資料庫
- 放的是關系,如:朋友圈社交網路,廣告推薦等
列存盤資料庫
- Hbase
- 分布式檔案系統
Redis 入門
什么是 Redis
遠程字典服務(Remote Dictionary Server)
是一個由 Salvatore Sanfilippo 寫的 key-value 存盤系統,是跨平臺的非關系型資料庫,
Redis 是一個開源的使用 ANSI C 語言撰寫、遵守 BSD 協議、支持網路、可基于記憶體、分布式、可選持久性的鍵值對(Key-Value)存盤資料庫,并提供多種語言的 API,
Redis 通常被稱為資料結構服務器,因為值(value)可以是字串(String)、哈希(Hash)、串列(list)、集合(sets)和有序集合(sorted sets)等型別
能干什么
記憶體存盤、持久化、記憶體中斷電即失,所以持久化很重要(RDB、AOF)
發布訂閱系統
地圖資訊分析
計數器,計時器
特性
多樣化資料型別
持久化
集群
官網:http://www.redis.cn/
Docker 安裝
- 創建外部掛載目錄:
mkdir -p /root/docker/redis/data
mkdir -p /root/docker/redis/conf
- 下載 redis.conf 組態檔到 conf 目錄下,后給檔案授權
wget http://download.redis.io/redis-stable/redis.conf
chmod 777 redis.conf
- 修改配置資訊
bind 127.0.0.1 通過#注釋掉,解除本地連接限制
protected-mode yes 默認no,保護模式,限制為本地訪問,修改后解除保護模式
daemonize yes 注釋掉
appendonly yes 持久化
requirepass 123456 密碼
- 啟動命令
docker run -p 6379:6379 -v /root/docker/redis/data:/data -v /root/docker/redis/conf/redis.conf:/etc/redis/redis.conf --name myredis -d redis redis-server /etc/redis/redis.conf --appendonly yes --requirepass 123456
# 命令決議
-p 6379:6379 埠映射
--name myredis 指定容器名稱
-d redis 后臺啟動
--appendonly yes 開啟持久化
--requirepass 123456 設定密碼
-v /root/docker/redis/data:/data
-v /root/docker/redis/conf/redis.conf:/etc/redis/redis.conf --name myredis 資料卷映射
進入容器
# docker exec -it myredis bash
# redis-cli
127.0.0.1>auth 123456 // 認證用戶,不然無法使用
Redis-benchmark 性能測驗工具
# 命令
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
# 運行結果
====== SET ======
100000 requests completed in 3.12 seconds // 10萬個請求在3.12秒內完成
100 parallel clients // 100 個并發客戶端
3 bytes payload // 每次寫入3個位元組
keep alive: 1 // 只有一臺服務器來處理這些請求,單機性能
multi-thread: no
0.09% <= 1 milliseconds
99.54% <= 2 milliseconds
99.86% <= 3 milliseconds
99.96% <= 4 milliseconds
100.00% <= 4 milliseconds
32061.56 requests per second // 每秒處理3萬2千請求
基礎知識
默認有16個資料庫 在組態檔中 database 關鍵字中配置
- 切換資料庫
127.0.0.1:6379> select 3 // 切換資料庫
OK
127.0.0.1:6379[3]>DBSIZE // 回傳當前資料庫內 keys 數量
(integer) 0
127.0.0.1:6379[3]> set name 'xiong' // 添加鍵值
OK
127.0.0.1:6379[3]> get name // 根據 key 查詢 value
"xiong"
127.0.0.1:6379[3]> DBSIZE
(integer) 1
127.0.0.1:6379[3]> keys * // 查看所有鍵
1) "name"
127.0.0.1:6379[3]>flushdb // 清空本資料庫
127.0.0.1:6379[3]>flushall // 清空所有資料庫
- redis 是單執行緒的
redis 基于記憶體操作,CPU 不是 redis 性能瓶頸,reids 的瓶頸為機器記憶體和網路帶寬
redis 為單執行緒還這么快?
誤區1:高性能服務器一定是多執行緒
誤區2:多執行緒(CPU背景關系切換)一定比單執行緒效率高
CPU > 記憶體 > 硬碟
核心:redis 是將所有的資料放在記憶體中,所以說使用單執行緒操作是效率最高的,多執行緒(CPU背景關系切換)耗時間,對于記憶體來說沒有背景關系切換效率就是最高的!多次讀寫都是在一個CPU上
五大基本資料型別
Redis 是一個開源(BSD許可)的,記憶體中的資料結構存盤系統,它可以用作資料庫、快取和訊息中間件, 它支持多種型別的資料結構,如 字串(strings), 散列(hashes), 串列(lists), 集合(sets), 有序集合(sorted sets) 與范圍查詢, bitmaps, hyperloglogs 和 地理空間(geospatial) 索引半徑查詢, Redis 內置了 復制(replication),LUA腳本(Lua scripting), LRU驅動事件(LRU eviction),事務(transactions) 和不同級別的 磁盤持久化(persistence), 并通過 Redis哨兵(Sentinel)和自動 磁區(Cluster)提供高可用性(high availability),
redis-key
基礎命令:http://www.redis.cn/commands.html
| 判斷是否存在 | exists name |
|---|---|
| 移除一個鍵 | move name 1 (1 代表當前資料庫) |
| 設定過期時間 | expire name 10 (設定為10s 過期) |
| 查看當前資料型別 | type name |
String(字串)
| 追加字符 | append key ‘hello’ (如果key不存在,則新建) |
|---|---|
| 獲取資料長度 | strlen key 查看資料長度 |
| 自增1 | incr view |
| 自減1 | decr view |
| setex // 設定過期時間 | setex name 30 ‘xiong’ (30 秒后過期) |
| setnx // 不存在再設定 | setnx name ‘xiong’ (如果存在,不設定,不存在就設定) |
| mset // 設定多個值 | mset k1 v1 k2 v2 |
| mget // 獲取多個值 | mget k1 k2 |
| getset | getset name redis # 如果不存在值,則回傳 nil;如果存在,則回傳原來的值再修改 |
| 設定物件 | set user:1:{name:zhang,age:3} // 設定一個user:1 物件值為 json 字串mset user:1:name zhang user:1:age:3 // 批量設定的方法 |
使用場景:
- 計數器 (博客瀏覽量等)
- 統計多單位的數量
- 粉絲數
- 物件快取存盤!
List(串列)
基本資料型別:串列
在 Redis 內可以實作:堆疊、佇列
127.0.0.1:6379> lpush list one // 向串列內插入值(頭部插入,類似堆疊)
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lrange list 0 -1 // 獲取指定范圍內的值
1) "two"
2) "one"
127.0.0.1:6379> rpush list three // 向右插入(尾部插入)
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "three"
==================================================
pop # 移除操作
127.0.0.1:6379> lpop list // 移除頭部的值
"two"
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "three"
127.0.0.1:6379> rpop list // 移除尾部的值
"three"
127.0.0.1:6379> lrange list 0 -1
1) "one"
===================================================
lindex # 獲取下標某個值
127.0.0.1:6379> lindex list 1
"two"
==================================================
# 獲取長度 llen
127.0.0.1:6379> llen list
(integer) 3
==================================================
lrem # 移除指定個數的 value
127.0.0.1:6379> lrem list 1 one # lrem key count value
(integer) 1
==================================================
ltrim # 截取范圍內的值
127.0.0.1:6379> rpush mylist 'hello'
(integer) 1
127.0.0.1:6379> rpush mylist 'hello1'
(integer) 2
127.0.0.1:6379> rpush mylist 'hello2'
(integer) 3
127.0.0.1:6379> rpush mylist 'hello3'
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "hello1"
3) "hello2"
4) "hello3"
127.0.0.1:6379> ltrim mylist 1 2 # ltrim mylist start end
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello2"
======================================================
rpoplpush # 移除最后一個元素并保存到其他串列
127.0.0.1:6379> rpush list 'hello' 'hello1' 'hello2' 'hello3'
(integer) 4
127.0.0.1:6379> rpoplpush list otherlist
"hello3"
127.0.0.1:6379> lrange list 0 -1
1) "hello"
2) "hello1"
3) "hello2"
127.0.0.1:6379> lrange otherlist 0 -1
1) "hello3"
========================================================
lset # 修改串列內的值 (不存在的值會報錯)
127.0.0.1:6379> rpush list 'hello'
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "hello"
127.0.0.1:6379> lset list 0 'item' # lset key index element
OK
127.0.0.1:6379> lrange list 0 -1
1) "item"
=========================================================
linsert # 插入一個值,定位為串列中的 value
127.0.0.1:6379> linsert list before item 'new' # 向前插入
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "new"
2) "item"
127.0.0.1:6379> linsert list after item 'new' # 向后插入
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "new"
2) "item"
3) "new"
小結
- 實際上是一個鏈表,before Node after,left right 都可以插入值
- 如果key 不存在,創建新的鏈表
- 如果key 存在,新增內容
- 如果移除所有值,空鏈表,也代表不存在
- 兩邊插入或者改動值,效率最高!中間元素效率相對較低
Set(集合)
set 中的值不能重復
127.0.0.1:6379> sadd myset 'hello' 'xiong' 'love' # 向集合中添加值
(integer) 3
127.0.0.1:6379> SMEMBERS set
(empty array)
127.0.0.1:6379> SMEMBERS myset # 查看集合中所有值
1) "xiong"
2) "hello"
3) "love"
127.0.0.1:6379> SISMEMBER myset xiong # 判斷一個值是否在集合中
(integer) 1
127.0.0.1:6379> SISMEMBER myset love
(integer) 1
127.0.0.1:6379> SISMEMBER myset world
(integer) 0
========================================================
scard # 獲取集合中元素個數
127.0.0.1:6379> scard myset
(integer) 3
========================================================
srem # 移除某個值
127.0.0.1:6379> srem myset xiong
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "hello"
2) "love"
========================================================
SRANDMEMBER # 隨機抽選一個值
127.0.0.1:6379> SRANDMEMBER myset # SRANDMEMBER set count
"love"
127.0.0.1:6379> SRANDMEMBER myset
"hello"
========================================================
spop # 隨機洗掉一個元素
127.0.0.1:6379> spop myset # spop set count
"love"
========================================================
# sdiff # 求取差集
========================================================
sinter # 求取交集
========================================================
sunion # 求取并集
用途:
- 微博,B站的 共同關注 (交集)
Hash(哈希)
Map 集合,key-Map ,本質和 string 沒有區別
127.0.0.1:6379> hset myhash filed1 xionog # 存值
(integer) 1
127.0.0.1:6379> hget myhash filed1 # 取值
"xionog"
========================================================
取值
hmget # 獲取多個
hgetall # 獲取全部
========================================================
洗掉
hdel
========================================================
hlen # 獲取 hash 表的欄位數量
========================================================
hexists # 判斷 hash 表的欄位是否存在
========================================================
hkeys # 獲取 hash 表的所有值
應用
- 更適合物件的存盤
Zset(有序集合)
在 set 的基礎上,增加了一個值
127.0.0.1:6379> zadd myset 1 one # 添加一個值 zadd key score value
(integer) 1
127.0.0.1:6379> zadd myset 2 two
(integer) 1
127.0.0.1:6379> zadd myset 3 three
(integer) 1
127.0.0.1:6379> ZRANGE myset 0 -1 # 查看值
1) "one"
2) "two"
3) "three"
========================================================
ZRANGEBYSCORE # 排序
127.0.0.1:6379> zadd saraly 300 xiong 200 xin 400 qiang
(integer) 3
127.0.0.1:6379> zrange saraly 0 -1
1) "xin"
2) "xiong"
3) "qiang"
127.0.0.1:6379> ZRANGEBYSCORE saraly -inf +inf withscores # 排序,只能從小到大,在設定值的時候就已經排好序了
1) "xin"
2) "200"
3) "xiong"
4) "300"
5) "qiang"
6) "400"
127.0.0.1:6379> zrevrange saraly 0 -1 # 排序從大到小
1) "qiang"
2) "xiong"
3) "xin"
========================================================
zrem # 移除元素
========================================================
zcard # 獲取集合中元素個數
========================================================
zcount # 獲取指定區間內的元素個數
應用
- 存盤班級程式表
- 普通訊息和重要訊息區分,按權重進行判斷
- 排行榜實作
三種特殊資料型別
geospatial 地理位置
朋友的定位,附近的人,打車距離計算
redis 的 Geo 在 Redis 3.2 版本就推出了,可以推算地理位置資訊
測驗資料連接:http://www.jsons.cn/lngcode/
相關命令:http://www.redis.cn/commands/geoadd.html
- GEOADD
- GEODIST
- GEOHASH
- GEOPOS
- GEORADIUS
- GEORADIUSBYMEMBER
geoadd # 添加地理位置 (兩級-南極北極,無法直接添加)
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijin
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shengzheng
(integer) 2
========================================================
由EPSG:900913 / EPSG:3785 / OSGEO:41001 規定如下:
有效的經度從-180度到180度
有效的緯度從-85.05112878度到85.05112878度
========================================================
GEODIST key member1 member2
回傳兩個給定位置之間的距離,
如果兩個位置之間的其中一個不存在, 那么命令回傳空值,
指定單位的引數 unit 必須是以下單位的其中一個:
- m 表示單位為米,
- km 表示單位為千米,
- mi 表示單位為英里,
- ft 表示單位為英尺,
如果用戶沒有顯式地指定單位引數, 那么 GEODIST 默認使用米作為單位,
GEODIST 命令在計算距離時會假設地球為完美的球形, 在極限情況下, 這一假設最大會造成 0.5% 的誤差,
127.0.0.1:6379> geodist china:city shanghai beijin
"1067378.7564"
========================================================
GEOPOS key member [member ...]
從key里回傳所有給定位置元素的位置(經度和緯度),
給定一個sorted set表示的空間索引,密集使用 geoadd 命令,它以獲得指定成員的坐標往往是有益的,當空間索引填充通過 geoadd 的坐標轉換成一個52位Geohash,
所以回傳的坐標可能不完全以添加元素的,但小的錯誤可能會出臺,
因為 GEOPOS 命令接受可變數量的位置元素作為輸入, 所以即使用戶只給定了一個位置元素, 命令也會回傳陣列回復,
========================================================
GEORADIUS key longitude latitude radius m|km|ft|mi
以給定的經緯度為中心, 回傳鍵包含的位置元素當中, 與中心的距離不超過給定最大距離的所有位置元素,
案例,獲取附近的人:
127.0.0.1:6379> georadius china:city 110 30 500 km
1) "chongqing"
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord # 查詢他人的經緯度
1) 1) "chongqing"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist # 查詢顯示到中心距離的位置
1) 1) "chongqing"
2) "341.9374"
========================================================
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]
GEORADIUSBYMEMBER 的中心點是由給定的位置元素決定的, 而不是像 GEORADIUS 那樣, 使用輸入的經度和緯度來決定中心點指定成員的位置被用作查詢的中心
127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 1000 km
1) "shanghai"
========================================================
GEOHASH key member [member ...]
該命令回傳11個字符的字串
127.0.0.1:6379> geohash china:city shanghai chongqing
1) "wtw3sj5zbj0"
2) "wm5xzrybty0"
# 將二維經緯度轉換為一維字串,如果兩個字串越接近,則距離越近
GEO 底層為 zset 集合所以可以用 zset 命令
hyperloglogs
什么是基數:
一個集合中不重復元素的個數
優點:占用的記憶體是固定的,2^64 不同的元素基數,只需要廢12kb記憶體!如果要從記憶體角度來比驕傲的話 hyperloglog 首選
應用:網頁的 UV (一個人訪問一個網站多次,訪問量為1)
傳統方式:使用 set 集合儲存用戶id,然后統計 set 集合中的元素個數作為標準判斷
127.0.0.1:6379> pfadd mykey a b c d e f g h i j # 添加元素
(integer) 1
127.0.0.1:6379> pfcount mykey # 統計基數個數
(integer) 10
127.0.0.1:6379> pfadd mykey2 i j a l n m e c x
(integer) 1
127.0.0.1:6379> pfmerge mykey3 mykey mykey2 # 合并兩個集合,并集
OK
127.0.0.1:6379> pfcount mykey3
(integer) 14
如果允許容錯,則使用 hyperloglog,出錯率為 0.81%
Bitmaps
位存盤(只有 0 和 1)都是操作二進制為來進行記錄,就只有0和1兩個狀態
應用:
統計用戶資訊:活躍,不活躍;登陸,未登錄;打卡
127.0.0.1:6379> setbit sign 0 1 # 存值
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 0
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> getbit sign 3 # 取值
(integer) 0
127.0.0.1:6379> bitcount sign # 統計
(integer) 2
事務
Redis 單條命令保證原子性,但是事務不保證原子性!
本質: 一組命令的集合!一個事務中的所有命令都會被序列化,在事務執行程序中,按順序執行
所有命令在事務中,并沒有直接被執行!只有發起執行命令的時候才會執行!
Redis 事務
- 開啟事務
- 命令入隊
- 執行事務
127.0.0.1:6379> multi # 開啟事務
OK
127.0.0.1:6379> set k1 v1 # 命令入隊
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec # 執行事務
1) OK
2) OK
3) OK
4) "v2"
=============================================================
127.0.0.1:6379> multi # 開啟事務
OK
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> discard # 取消事務
OK
127.0.0.1:6379> get k4 # 事務佇列中的命令不會執行
(nil)
127.0.0.1:6379>
編譯型例外
代碼有問題,命令有錯誤
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> getset k3
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k4
(nil)
127.0.0.1:6379> get k2
(nil)
命令都不會運行,整個事務都取消
運行時例外
語法型問題,其他命令正常執行,錯誤出拋出例外
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range
4) OK
監控
悲觀鎖
- 很悲觀,什么時候都會出問題,無論做什么都加鎖
樂觀鎖
- 很樂觀,什么時候都不會出問題,所以不會上鎖!更新資料的時候去判斷一下,在此期間是否有人修改過這個資料
- 獲取 version
- 更新的時候比較 version
Redis 監控測驗
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money # 監視 money 物件
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY out 20
QUEUED
127.0.0.1:6379> exec # 正常執行 成功后監控會自動取消
1) (integer) 80
2) (integer) 20
=============================================================
# 測驗多執行緒修改值,使用 watch 可以當作 reids 的樂觀鎖操作
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
# 另一主機連接并修改監控的值后
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> exec # 監視失敗,事務取消
(nil)
失敗后的步驟
- 放棄監視 unwatch
- 重新監視 watch key
- 事務執行是比對監視的值是否變化,如果沒有變化則執行,如果有變化則放棄監視,重新監視執行
Jedis
是官方推薦的 java 連接開發工具!使用 java 操作的Redis 中間件!
構建專案:
創建一個空專案:
構建 maven 專案 -> 修改:


匯入對應依賴
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
測驗連接
public class TestPing {
public static void main(String[] args) {
// 1. new jedis 物件
Jedis jedis = new Jedis("ip",6379);
// 認證
jedis.auth("123456");
// 測驗連接
System.out.println(jedis.ping());
jedis.close(); // 關閉連接
}
}
常用Api
與常用基礎型別的命令基本同,特殊型別也相同,只不過換成了方法
事務
public class TestPing {
public static void main(String[] args) {
// 1. new jedis 物件
Jedis jedis = new Jedis("159.75.113.81",6379);
// 認證
jedis.auth("123456");
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("name","xiong");
// 開啟事務
Transaction multi = jedis.multi();
String result = jsonObject.toJSONString();
try {
multi.set("user1", result);
multi.set("user2", result);
multi.exec(); // 執行事務
} catch (Exception e) {
multi.discard(); // 出例外放棄事務
} finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close(); // 關閉連接
}
}
}
輸出:
{"name":"xiong","hello":"world"}
{"name":"xiong","hello":"world"}
redis.conf 詳情
組態檔對大小寫不敏感
網路配置:
bind 127.0.0.1 # 系結 ip
protected-mode no # 保護模式
port 6379 # 埠號
通用配置:
# By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
daemonize yes # 以守護行程方式運行,默認是 no
pidfile /var/run/redis_6379.pid # 如果以后臺方式運行,我們就需要指定一個 pid 檔案
日志級別:
# Specify the server verbosity level.
# This can be one of:
# 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
logfile "" # 日志保存檔案名,如果為空,則為輸出
databases 16 # 資料庫數量
======================================================================================
快照 (持久化使用)
在規定時間內,執行了多少次操作,則會持久化到檔案 rdb aof
如果沒有持久化,則 redis 斷電會丟失資料
# save <seconds> <changes>
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behavior will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
#
# Note: you can disable saving completely by commenting out all "save" lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
#
# save ""
save 900 1 # 如果900 秒內有一個 key 值改變就進行持久化操作
save 300 10 # 如果300 秒內有 10 個 key 值改變就進行持久化操作
save 60 10000 # 如果 60 秒內,有 10000 個 key 值改變就進行持久化操作
stop-writes-on-bgsave-error yes # 持久化出錯,是否繼續作業
rdbchecksum yes # 保存壓縮rdb 檔案的時候,進行錯誤校驗
rdbcompression yes # 是否壓縮 rdb 檔案,需要消耗 cpu 資源
dir ./ # rdb 檔案保存路徑
主從復制:
安全:
requirepass 123456 # 設定密碼,默認沒有
=========================================
通過命令設定密碼:
127.0.0.1:6379> config set requirepass "123456"
獲取密碼
127.0.0.1:6379> config get requirepass
客戶端限制:
maxclients 10000 # 設定能連接的redis 最大客戶端數量
maxmemory <bytes> # redis 配置最大的記憶體
# volatile-lru -> Evict using approximated LRU, only keys with an expire set.
# allkeys-lru -> Evict any key using approximated LRU.
# volatile-lfu -> Evict using approximated LFU, only keys with an expire set. 只對設定了過時間的key 進行lru(默認值)
# allkeys-lfu -> Evict any key using approximated LFU. 洗掉lru 演算法的key
# volatile-random -> Remove a random key having an expire set. 隨機洗掉即將過期的key
# allkeys-random -> Remove a random key, any key. 隨機洗掉key
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL) 洗掉即將過期的key
# noeviction -> Don't evict anything, just return an error on write operations. 永不過期,報錯
#
# LRU means Least Recently Used
# LFU means Least Frequently Used
#
# Both LRU, LFU and volatile-ttl are implemented using approximated
# randomized algorithms.
#
# Note: with any of the above policies, Redis will return an error on write
# operations, when there are no suitable keys for eviction.
#
# At the date of writing these commands are: set setnx setex append
# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
# getset mset msetnx exec sort
#
# The default is:
#
# maxmemory-policy noeviction # 記憶體滿后的策略
AOF 配置:
appendonly yes # 默認為 no,默認使用 rdb 方式持久化,在大部分情況下, rdb 完全夠用
# The name of the append only file (default: "appendonly.aof")
appendfilename "appendonly.aof" # 檔案名
自動持久化方式:
# appendfsync always # 每次修改都追加同步,消耗性能
appendfsync everysec # 每秒一次
# appendfsync no # 系統自動同步資料,不追加同步,速度最快
Redis 持久化
RDB
持久化操作流程圖:

rdb 保存檔案為 dump.rdb(在bin 檔案夾中)該檔案是一個壓縮過的二進制檔案,可以通過該檔案還原快照時的資料庫狀態,即生成該RDB檔案時的服務器資料
有時候在生產環境中會進行備份
觸發規則:
- save 規則(組態檔中寫的)情況下自動觸發保存
- 執行
save和bgsave命令
執行save和bgsave命令,可以手動觸發快照,生成RDB檔案,兩者的區別如下
使用save命令會阻塞Redis服務器行程,服務器行程在RDB檔案創建完成之前是不能處理任何的命令請求
127.0.0.1:6379> save
OK
復制代碼
而使用bgsave命令不同的是,bgsave命令會fork一個子行程,然后該子行程會負責創建RDB檔案,而服務器行程會繼續處理命令請求
127.0.0.1:6379> bgsave
Background saving started
- 執行 flushall 命令,也會觸發保存
- 退出 redis,也會觸發保存
恢復:
- 只需要將 rdb 檔案放在 redis 啟動目錄就可以,redis 啟動時會自動檢查 dump.rdb 并恢復其中資料!
- 查看需要存在的位置 config get dir
優點
- RDB快照是一個壓縮過的非常緊湊的檔案,保存著某個時間點的資料集,適合做資料的備份,災難恢復
- 可以最大化Redis的性能,在保存RDB檔案,服務器行程只需fork一個子行程來完成RDB檔案的創建,父行程不需要做IO操作
- 與AOF相比,恢復大資料集的時候會更快
缺點
- RDB的資料安全性是不如AOF的,保存整個資料集的程序是比繁重的,根據配置可能要幾分鐘才快照一次,如果服務器宕機,那么就可能丟失幾分鐘的資料
- Redis資料集較大時,fork的子行程要完成快斬訓比較耗CPU、耗時
AOF
將我們的所有命令都記錄下來(在大量資料時效率很慢)
開啟:將 appendonly 改為 yes
如果 AOF 檔案超過 64mb ,會 fork 一個新的行程來將我們的檔案進行重寫

觸發保存規則時會將所寫入的命令記錄進 appendonly.aof 檔案中
如果 aof 檔案有錯誤,redis 就無法啟動
可以通過 redis-check-aof --fix appendonly.aof 進行修復(通過洗掉錯誤命令進行修復)
優點:
- 資料更完整,安全性更高,秒級資料丟失(取決fsync策略,如果是everysec,最多丟失1秒的資料)
- AOF檔案是一個只進行追加的日志檔案,且寫入操作是以Redis協議的格式保存的,內容是可讀的,適合誤刪緊急恢復
缺點:
- 對于相同的資料集,AOF檔案的體積要大于RDB檔案,資料恢復也會比較慢
- 根據所使用的 fsync 策略,AOF 的速度可能會慢于 RDB, 不過在一般情況下, 每秒 fsync 的性能依然非常高
總結
- 如果是資料不那么敏感,且可以從其他地方重新生成補回的,那么可以關閉持久化
- 如果是資料比較重要,不想再從其他地方獲取,且可以承受數分鐘的資料丟失,比如快取等,那么可以只使用RDB
- 如果是用做記憶體資料庫,要使用Redis的持久化,建議是RDB和AOF都開啟,或者定期執行
bgsave做快照備份,RDB方式更適合做資料的備份,AOF可以保證資料的不丟失
Redis 訂閱發布
Redis 發布訂閱(pub/sub)是一種訊息通信模式:發送者(pub)發送訊息,訂閱者(sub)接收訊息
Redis 客戶端可以訂閱任意適量的頻道
訊息發布圖

測驗:
127.0.0.1:6379> subscribe xiongxinq // 訂閱頻道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "xiongxinq"
3) (integer) 1
# 第二個客戶端
127.0.0.1:6379> publish xiongxinq "hello wirld" // 向指定頻道發送訊息
(integer) 1
127.0.0.1:6379>
1) "message"
2) "xiongxinq"
3) "hello wirld"
命令
| 1 | [PSUBSCRIBE pattern pattern …] 訂閱一個或多個符合給定模式的頻道, |
|---|---|
| 2 | [PUBSUB subcommand argument [argument …]] 查看訂閱與發布系統狀態, |
| 3 | PUBLISH channel message 將資訊發送到指定的頻道, |
| 4 | [PUNSUBSCRIBE pattern [pattern …]] 退訂所有給定模式的頻道, |
| 5 | [SUBSCRIBE channel channel …] 訂閱給定的一個或多個頻道的資訊, |
| 6 | [UNSUBSCRIBE channel [channel …]] 指退訂給定的頻道, |
原理 Redis 使用 c 實作的們可以通過 pubsub.c 檔案了解
使用場景:
- 實時訊息系統
- 實時聊天(聊天室)
- 訂閱、關注系統
稍微復雜的場景會使用訊息中間件
主從復制
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/260542.html
標籤:其他
下一篇:Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:2.7:resources
