柚子快報(bào)邀請(qǐng)碼778899分享:nosql Redis
柚子快報(bào)邀請(qǐng)碼778899分享:nosql Redis
Redis
1.NoSQL
NoSQL = Not Only SQL
泛指非關(guān)系型數(shù)據(jù)庫
2.NoSQL的四大分類
KV鍵值對(duì)
新浪:Redis美團(tuán):Redis + Tair 文檔型數(shù)據(jù)庫(bson格式和json格式一樣)
MongoDB(一般必須要掌握),基于分布式文件存儲(chǔ)的數(shù)據(jù)庫,c++編寫,主要用來處理大量的文檔MongoDB世界語關(guān)系型數(shù)據(jù)庫和非關(guān)系型數(shù)據(jù)庫中間的產(chǎn)品,MongoDB是非關(guān)系型數(shù)據(jù)庫中功能最豐富,最像關(guān)系型數(shù)據(jù)庫的ConthDB 列存儲(chǔ)數(shù)據(jù)庫
HBase(需要掌握)分布式文件系統(tǒng) 圖關(guān)系數(shù)據(jù)庫
不是存儲(chǔ)圖形,存放的是關(guān)系,比如朋友圈社交網(wǎng)絡(luò),廣告推薦Neo4j(需要掌握),InfoGrid
3.Redis概述
Redis:(Remote Dictionary Server),即遠(yuǎn)程字典服務(wù) Redis做什么:
內(nèi)存存儲(chǔ),持久化,內(nèi)存是斷電即失,所以說持久化很重要(rdb,aof)效率高,可用于高速緩存發(fā)布訂閱系統(tǒng)(可以進(jìn)行一些簡單的消息隊(duì)列)地圖信息分析計(jì)時(shí)器,計(jì)數(shù)器(瀏覽量) Redis的基本命令使用
set name wang //設(shè)置鍵值對(duì)
get name //獲取key對(duì)應(yīng)的值
keys * //查看所有的key
select 3 //選擇3號(hào)數(shù)據(jù)庫,redis默認(rèn)有16個(gè)數(shù)據(jù)庫
dbsize //查看數(shù)據(jù)庫的大小
flushdb //清空當(dāng)前數(shù)據(jù)庫
flushall //清空所有數(shù)據(jù)庫
4.五大數(shù)據(jù)類型
Redis-key
set name wang
type name //查看當(dāng)前類的一個(gè)類型
keys *
get name
exists name
move name
expire name 10 //設(shè)置過期時(shí)間為10s
ttl name //可以查看剩余多長時(shí)間過期
String
127.0.0.1:6379> set key1 v1 #設(shè)置值
OK
127.0.0.1:6379> get key1 #獲得值
"v1"
127.0.0.1:6379> keys * #查看值
1) "key1"
2) "name"
127.0.0.1:6379> exists key1 #判斷某一個(gè)key是否存在
(integer) 1
127.0.0.1:6379> append key1 "hello" #追加字符串
(integer) 7
127.0.0.1:6379> get key1 #獲得值
"v1hello"
127.0.0.1:6379> strlen key1 #獲取字符串的長度
(integer) 7
127.0.0.1:6379> getranger key1 0 3 #截取字符串
127.0.0.1:6379> getranger key1 0 -1 #獲取全部字符串
127.0.0.1:6379> setranger key1 1 xx #從字符串下標(biāo)為1的位置開始,替換下表2和3的字符串
127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> get views #增加1
"0"
127.0.0.1:6379> incr views
(integer) 1
127.0.0.1:6379> get views
"1"
127.0.0.1:6379> decr views #減少1
(integer) 0
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incrby views 10 #增加10
(integer) 10
127.0.0.1:6379> get views
"10"
127.0.0.1:6379> incrby views 10
(integer) 20
127.0.0.1:6379> decrby views 5 #減少5
(integer) 15
setex (set with expire) #設(shè)置過期時(shí)間
setnx (set if not exist) #不存在設(shè)置(在分布式鎖會(huì)常常使用)
#批量設(shè)置值
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 #批量設(shè)置值
OK
127.0.0.1:6379> keys *
1) "views"
2) "k3"
3) "k1"
4) "k2"
127.0.0.1:6379> mget k1 k2 k3 #批量獲取值
1) "v1"
2) "v2"
3) "v3"
#對(duì)象
set user:1 {name:zhangsan,age:3} #設(shè)置一個(gè)user:1對(duì)象,值為json字符來保存一個(gè)對(duì)象
127.0.0.1:6379> set user:1 {name:zhangsan,age:3}
OK
127.0.0.1:6379> get user:1
"{name:zhangsan,age:3}"
#這里的key是一個(gè)巧妙的設(shè)計(jì): user:{id}:{filed}.如此設(shè)計(jì)在Redis中是完全OK了
127.0.0.1:6379> mset user:2:name zhangsan user:2:age 18
OK
127.0.0.1:6379> mget user:2:name user:2:age
1) "zhangsan"
2) "18"
#getset 先get再set
127.0.0.1:6379> getset db redis #如果不存在值,則返回nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongodb #如果存在值,獲取原來的值,并設(shè)置新的值
"redis"
127.0.0.1:6379> get db
"mongodb"
List
我們可以把list玩成棧、隊(duì)列、阻塞隊(duì)列所有的list命令都是L開頭的
# 將一個(gè)或者多個(gè)值插入到列表頭部(左)
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> LPUSH list three
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> LRANGE list 0 1
1) "three"
2) "two"
# 從右面隊(duì)列插入元素
127.0.0.1:6379> RPUSH list right
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379>
#移除元素
Lpop list
Rpop list
#通過下標(biāo)獲取某一個(gè)值
lindex list 1
set集合,無序,不可重復(fù)
sadd myset "hello"
Hash哈希類型
127.0.0.1:6379> hset myhash field1 wang #set一個(gè)具體的key-value
(integer) 1
127.0.0.1:6379> hget myhash field1
"wang"
Zset(有序集合)
在set的基礎(chǔ)上,增加了一個(gè)值,set k1 v1 zset k1 score1 v1
127.0.0.1:6379> zadd myset 1 one
(integer) 1
127.0.0.1:6379> zadd myset 2 two 3 three
(integer) 2
127.0.0.1:6379> zrange myset 0 -1
1) "one"
2) "two"
3) "three"
5.三種特殊類型
(1)geospatial 地理位置(底層實(shí)現(xiàn)原理是Zset),我們可以使用Zset命令來操作geo
可以實(shí)現(xiàn)附近的人業(yè)務(wù)
# 添加地理位置
# 我們一般會(huì)下載城市數(shù)據(jù),直接通過Java程序一次性導(dǎo)入
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
#獲取城市的位置,先經(jīng)度后緯度
127.0.0.1:6379> geopos china:city beijing
1)116.40
2)39.90
#兩個(gè)城市之間的直線距離
geodist china:city beijing chongqing #單位默認(rèn)是M
geodist china:city beijing chongqing KM #指定單位為KM mi為英里 ft表示為英尺
#以110 30這個(gè)經(jīng)緯度為中心,尋找方圓1000KM內(nèi)的城市
127.0.0.1:6379> georadius china:city 110 30 1000 km
#找出以上海為中心周圍400km的城市
127.0.0.1:6379> georadiusbymember china:city shanghai 400 km
#底層實(shí)現(xiàn)原理是Zset
#查看錄入的所有城市
127.0.0.1:6379> zrange china:city 0 -1
#移除某個(gè)指定的城市
127.0.0.1:6379> zrem china:city beijing
(2)Hyperloglog
什么是基數(shù)?
A{1,3,5,7,8,7}
B{1,3,5,7,8}
基數(shù)(不重復(fù)的元素)= 5,可以接受誤差
網(wǎng)站的UV(一個(gè)人訪問一個(gè)網(wǎng)站多次,但是還算做一個(gè)人)
127.0.0.1:6379> PFadd mykey a b c d e f g a b c
#統(tǒng)計(jì)基數(shù)的個(gè)數(shù)
127.0.0.1:6379> PFCOUNT mykey
(integer) 9
(3)Bitmaps
統(tǒng)計(jì)用戶信息,活躍,不活躍!登錄,未登錄,兩個(gè)狀態(tài)的,都可以使用Bitmaps
統(tǒng)計(jì)七天打卡信息
127.0.0.1:6379> setbit sign 0 1
127.0.0.1:6379> setbit sign 1 1
127.0.0.1:6379> setbit sign 2 0
127.0.0.1:6379> setbit sign 3 1
127.0.0.1:6379> setbit sign 4 1
127.0.0.1:6379> setbit sign 5 0
127.0.0.1:6379> setbit sign 6 1
127.0.0.1:6379> getbit sign 6
(integer) 1
6.Redis事務(wù)
127.0.0.1:6379> multi #開啟事務(wù)
OK
127.0.0.1:6379> set k1 v1 #入隊(duì)
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> exec #執(zhí)行事務(wù)
1) OK
2) "v1"
127.0.0.1:6379> multi #開啟事務(wù)
OK
127.0.0.1:6379> set k1 v1 #入隊(duì)
QUEUED
127.0.0.1:6379> DISCARD #取消事務(wù)
OK
# 如果事務(wù)編寫時(shí)中間有編譯時(shí)的錯(cuò)誤入隊(duì)指令,那么這個(gè)事務(wù)不會(huì)成功
# 運(yùn)行時(shí)有錯(cuò)誤,錯(cuò)誤的不會(huì)執(zhí)行,其它正??梢詧?zhí)行
# 使用watch可以當(dāng)作redis的樂觀鎖實(shí)現(xiàn)
7.Jedis
我們要使用Java操作Redis
Jedis:是Redis官方推薦的Java連接開發(fā)工具,使用Java操作Redis中間件
1.導(dǎo)入對(duì)應(yīng)的依賴
2.Jedis連接Redis
Jedis jedis = new Jedis("127.0.0.1",6379);
System.out.println(jedis.ping()); //PONG
Jedis jedis = new Jedis("127.0.0.1",6379);
System.out.println(jedis.ping()); //PONG
System.out.println("清空數(shù)據(jù):" + jedis.flushDB());
System.out.println("判斷某個(gè)建是否存在" + jedis.exists("username"));
System.out.println("新增<'username','admin'>的鍵值對(duì)" + jedis.set("username","admin"));
System.out.println("新增<'username','password'>的鍵值對(duì)" + jedis.set("password","123546"));
Set
System.out.println(keys);
System.out.println("刪除鍵password" + jedis.del("password"));
System.out.println("查看鍵username所存儲(chǔ)值的類型" + jedis.type("username"));
System.out.println("重命名key" + jedis.rename("username","name"));
System.out.println("刪除當(dāng)前選擇數(shù)據(jù)庫的所有key" + jedis.flushDB());
System.out.println("返回當(dāng)前數(shù)據(jù)庫中的所有key" + jedis.dbSize());
System.out.println("刪除所有數(shù)據(jù)庫中的所有key" + jedis.flushAll());
jdesi.close();//關(guān)閉連接
3.Jedis測試Redis事務(wù)
Jedis jedis = new Jedis("127.0.0.1",6379);
JSONObject jsonObject = new JSONObject();
jsonObject.put("user1","wyf");
jsonObject.put("user2","csq");
System.out.println("jsonObject:"+jsonObject);
System.out.println(jsonObject.toJSONString());
//開啟事務(wù)
Transaction multi = jedis.multi();
String result = jsonObject.toJSONString();
jedis.watch("user1");//通過watch實(shí)現(xiàn)樂觀鎖
try {
multi.set("user1",result);
multi.set("user2",result);
//成功執(zhí)行事務(wù)
multi.exec();
} catch (Exception e) {
//失敗放棄事務(wù)
multi.discard();
e.printStackTrace();
} finally {
System.out.println("user1:" + jedis.get("user1"));
System.out.println("user2:" + jedis.get("user2"));
jedis.close();
}
8.SpringBoot整合Redis
說明:在spring boot2.x之后,原來使用的jedis被替換為了lettuce
jedis:采用的直連,多個(gè)線程操作的話,是不安全的,如果想避免不安全,使用jedis pool連接池,更像BIO模式
BIO: 同步并阻塞(傳統(tǒng)阻塞型),服務(wù)器實(shí)現(xiàn)模式為一個(gè)連接一個(gè)線程,即用戶端有連接請(qǐng)求時(shí)服務(wù)器就需要啟動(dòng)一個(gè)線程進(jìn)行處理,如果這個(gè)連接不做任何事情就會(huì)造成不必要的線程開銷
lettuce:采用netty,實(shí)例可以在多個(gè)線程中進(jìn)行共享,不存在線程不安全的情況,可以減少線程數(shù)據(jù),更像NIO模式
NIO: 同步非阻塞,服務(wù)器實(shí)現(xiàn)模式為一個(gè)線程處理多個(gè)請(qǐng)求連接,即客戶端發(fā)送的連接請(qǐng)求都會(huì)注冊(cè)到多路復(fù)用器上,多路復(fù)用器輪詢連接有I/O請(qǐng)求就進(jìn)行處理
1.導(dǎo)入依賴
2.配置依賴
redis:
host: 127.0.0.1
port: 6379
3.測試
@Autowired
private StringRedisTemplate stringRedisTemplate;
調(diào)用StringRedisTemplate中的API
9.Redis持久化
Redis是內(nèi)存數(shù)據(jù)庫,一旦服務(wù)器進(jìn)程退出,服務(wù)器中的數(shù)據(jù)庫狀態(tài)也會(huì)消失,所以Redis提供了持久化功能!
1.RDB(Redis DataBase)
即內(nèi)存快照,也是全量快照,當(dāng)服務(wù)器宕機(jī)時(shí),Redis中存儲(chǔ)的數(shù)據(jù)就會(huì)丟失。這個(gè)時(shí)候就需要內(nèi)存快照來恢復(fù)Redis中的數(shù)據(jù)了。
Redis提供了兩個(gè)命令來生產(chǎn)全量的RDB文件,一個(gè)是 save ,另一個(gè)是 bgsave。
save:在主線程中執(zhí)行,會(huì)導(dǎo)致阻塞;
bgsave:創(chuàng)建一個(gè)子進(jìn)程,專門用于寫入 RDB 文件,避免了主線程的阻塞,這也是 Redis RDB 文件生成的默認(rèn)配置。
不用想也知道,我們應(yīng)該使用哪種方式來生成RDB文件了。
增量快照,就是指,做了一次全量快照后,后續(xù)的快照只對(duì)修改的數(shù)據(jù)進(jìn)行快照記錄,這樣可以避免每次全量快照的開銷。在第一次做完全量快照后,T1 和 T2 時(shí)刻如果再做快照,我們只需要將被修改的數(shù)據(jù)寫入快照文件就行。但是,這么做的前提是,我們需要記住哪些數(shù)據(jù)被修改了。
2.AOF(Append Only File)
將我們所有的命令都記錄下來,保存的是appendonly.aof文件,aof文件隨著指令變多,文件大小越來越大,修復(fù)的速度也比rdb慢,所以默認(rèn)的配置是rdb持久化,恢復(fù)的時(shí)候重新寫入aof文件,不適合大數(shù)據(jù)量。
10.Redis訂閱發(fā)布
//訂閱一個(gè)頻道,等待推送的信息
127.0.0.1:6379> subscribe wyf
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "wyf"
3) (integer) 1
1) "message" #消息
2) "wyf" #頻道名字
3) "hello" #消息的具體內(nèi)容
1) "message"
2) "wyf"
3) "hello"
1) "message"
2) "wyf"
3) "hello11"
//發(fā)布者像頻道發(fā)送消息
127.0.0.1:6379> publish wyf "hello"
(integer) 1
127.0.0.1:6379> publish wyf "hello"
(integer) 1
127.0.0.1:6379> publish wyf "hello11"
(integer) 1
應(yīng)用場景:
1.實(shí)時(shí)聊天系統(tǒng)
2.訂閱、關(guān)注
稍微復(fù)雜的情景我們使用消息中間件MQ
11.Redis主從復(fù)制
主機(jī)只能寫,從機(jī)只能讀
主機(jī)斷開連接,從機(jī)依舊能夠連接到主機(jī),但是讀取不到主機(jī)內(nèi)容,如果主機(jī)返回,從機(jī)依舊可以直接獲取到主機(jī)寫的信息
全量復(fù)制:slave服務(wù)在接收到數(shù)據(jù)庫文件數(shù)據(jù)后,將其存盤并加載到內(nèi)存中(只要重新連到master,全量復(fù)制將自動(dòng)被執(zhí)行)
增量復(fù)制:master繼續(xù)將新的收集到的命令一次傳給slave,完成同步
M—> S/M—>S,中間的既可以當(dāng)前一個(gè)的slave,也可以當(dāng)下一個(gè)的master(層層鏈路)
如果沒有老大(master)了,能不能選擇出一個(gè)老大(master)呢,手動(dòng)(哨兵模式未出之前)
如果主機(jī)斷開了連接,使用 slave of no one讓自己變?yōu)閙aster
12.Redis哨兵模式(自動(dòng)選取master)
測試:
目前場景一主二從
1.配置哨兵模式配置文件sentinel.conf
#sentinel monitor 被監(jiān)控的名稱 host port 1
sentinel monitor myredis 127.0.0.1 6379 1 #這個(gè)1代表如果主機(jī)掛了,slave投票選舉新的主機(jī)
2.啟動(dòng)哨兵
redis-sentinel sentinel.conf
13.Redis的緩存穿透和雪崩(面試高頻)
緩存穿透(查不到)
定義:用戶想要查詢一個(gè)數(shù)據(jù),發(fā)現(xiàn)redis內(nèi)存數(shù)據(jù)庫沒有,也就是緩存沒有命中,于是向持久層數(shù)據(jù)庫查詢,發(fā)現(xiàn)也沒有,于是此次查詢失敗。當(dāng)用戶很多的時(shí)候,緩存都沒有命中(秒殺?。?,于是都去請(qǐng)求了持久層數(shù)據(jù)庫,這給持久層數(shù)據(jù)庫造成很大壓力,這時(shí)候就相當(dāng)于出現(xiàn)了緩存穿透。
緩存穿透的解決方案
布隆過濾器緩存空對(duì)象(缺點(diǎn)是存儲(chǔ)了很多空值的鍵,即使對(duì)空值設(shè)置了國企時(shí)間,還是會(huì)存在緩存層和存儲(chǔ)層的數(shù)據(jù)有一點(diǎn)時(shí)間窗口的不一致) 緩存擊穿(量太大,緩存過期,空檔期請(qǐng)求全部砸到數(shù)據(jù)庫)
緩存擊穿,是指一個(gè)key非常熱點(diǎn),在不停的扛著大并發(fā),大并發(fā)集中對(duì)這一個(gè)點(diǎn)進(jìn)行訪問,當(dāng)這個(gè)key瞬間失效的瞬間,持續(xù)的大并發(fā)就穿破緩存,直接請(qǐng)求數(shù)據(jù)庫,就像在一個(gè)屏障上鑿開一個(gè)洞。
緩存擊穿解決方案
設(shè)置熱點(diǎn)數(shù)據(jù)永不過期 從緩存面來看,沒有設(shè)置過期時(shí)間,所以不會(huì)出現(xiàn)熱點(diǎn)key過期后產(chǎn)生的問題。 加互斥鎖 分布式鎖:使用分布式鎖,保證每一個(gè)key同時(shí)只有一個(gè)線程去查詢后端服務(wù),其他線程沒有獲得分布式鎖的權(quán)限,因此只需要等待即可,這種方式將高并發(fā)的壓力轉(zhuǎn)到了分布式鎖,因此對(duì)分布式鎖的考驗(yàn)很大。 緩存雪崩
定義:在某一個(gè)時(shí)間段,緩存集中過期失效,Redis宕機(jī)
緩存雪崩解決方案
redis高可用 redis有可能掛掉,多設(shè)幾臺(tái)redis,一臺(tái)掛掉之后其它的還可以繼續(xù)工作,其實(shí)就是搭建集群(異地多活) 限流降級(jí)(SpringCould中提到) 緩存失效后,通過加鎖或者隊(duì)列來控制讀數(shù)據(jù)庫寫緩存的線程數(shù)量,比如對(duì)某個(gè)key只允許一個(gè)線程查詢和寫緩存,其它線程等待。 數(shù)據(jù)預(yù)熱 在正式部署之前,把數(shù)據(jù)先預(yù)先訪問一遍,這樣大部分訪問的數(shù)據(jù)就會(huì)加載到緩存中,在即將發(fā)生大并發(fā)訪問前手動(dòng)觸發(fā)加載緩存不同的key,設(shè)置不同的過期時(shí)間,讓緩存失效的時(shí)間盡量均勻。
柚子快報(bào)邀請(qǐng)碼778899分享:nosql Redis
文章來源
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。