主頁 > 資料庫 > Redis最新超詳細版教程通俗易懂

Redis最新超詳細版教程通俗易懂

2022-03-25 08:02:41 資料庫

Redis最新超詳細版教程通俗易懂

一、Nosql概述

為什么使用Nosql

1、單機Mysql時代

image-20220320111424582

90年代,一個網站的訪問量一般不會太大,單個資料庫完全夠用,隨著用戶增多,網站出現以下問題

  1. 資料量增加到一定程度,單機資料庫就放不下了

  2. 資料的索引(B+ Tree),一個機器記憶體也存放不下

  3. 訪問量變大后(讀寫混合),一臺服務器承受不住,

2、Memcached(快取) + Mysql + 垂直拆分(讀寫分離)

網站80%的情況都是在讀,每次都要去查詢資料庫的話就十分的麻煩!所以說我們希望減輕資料庫的壓力,我們可以使用快取來保證效率!

image-20220320111847463

優化程序經歷了以下幾個程序:

  1. 優化資料庫的資料結構和索引(難度大)
  2. 檔案快取,通過IO流獲取比每次都訪問資料庫效率略高,但是流量爆炸式增長時候,IO流也承受不了
  3. MemCache,當時最熱門的技術,通過在資料庫和資料庫訪問層之間加上一層快取,第一次訪問時查詢資料庫,將結果保存到快取,后續的查詢先檢查快取,若有直接拿去使用,效率顯著提升,

3、分庫分表 + 水平拆分 + Mysql集群

image-20220320112254642

4、如今最近的年代

如今資訊量井噴式增長,各種各樣的資料出現(用戶定位資料,圖片資料等),大資料的背景下關系型資料庫(RDBMS)無法滿足大量資料要求,Nosql資料庫就能輕松解決這些問題,

目前一個基本的互聯網專案

image-20220320112908695

為什么要用NoSQL ?

用戶的個人資訊,社交網路,地理位置,用戶自己產生的資料,用戶日志等等爆發式增長!
這時候我們就需要使用NoSQL資料庫的,Nosql可以很好的處理以上的情況!

什么是NoSQL

NoSQL

NoSQL = Not Only SQL (不僅僅是SQL)

關系型資料庫:列+行,同一個表下資料的結構是一樣的,

非關系型資料庫:資料存盤沒有固定的格式,并且可以進行橫向擴展,

NoSQL泛指非關系型資料庫,隨著web2.0互聯網的誕生,傳統的關系型資料庫很難對付web2.0時代!尤其是超大規模的高并發的社區,暴露出來很多難以克服的問題,NoSQL在當今大資料環境下發展的十分迅速,Redis是發展最快的,

很多的資料型別用戶的個人資訊,社交網路,地理位置,這些資料型別的存盤不需要一個固定的格式! 不需要多月的操作就可以橫向擴展的! Map<String,Object> 使用鍵值對來控制,

NoSQL特點

  1. 方便擴展(資料之間沒有關系,很好擴展!)
  2. 大資料量高性能(Redis一秒可以寫8萬次,讀11萬次,NoSQL的快取記錄級,是一種細粒度的快取,性能會比較高!)
  3. 資料型別是多樣型的!(不需要事先設計資料庫,隨取隨用)
  4. 傳統的 RDBMS 和 NoSQL
傳統的 RDBMS(關系型資料庫)
- 結構化組織
- SQL
- 資料和關系都存在單獨的表中 row col
- 操作,資料定義語言
- 嚴格的一致性
- 基礎的事務
- ...


Nosql
- 不僅僅是資料
- 沒有固定的查詢語言
- 鍵值對存盤,列存盤,檔案存盤,圖形資料庫(社交關系)
- 最終一致性
- CAP定理和BASE
- 高性能,高可用,高擴展
- ...

了解:3V + 3高

大資料時代的3V :主要是描述問題

  1. 海量Velume
  2. 多樣Variety
  3. 實時Velocity

大資料時代的3高 : 主要是對程式的要求

  1. 高并發
  2. 高可擴
  3. 高性能

真正在公司中的實踐:NoSQL + RDBMS 一起使用才是最強的,

啊里巴巴演進分析

推薦閱讀:阿里云的這群瘋子:https://yq.aliyun.com/articles/653511

image-20220320145710775

image-20220320145720258

如果你未來相當一個架構師:沒有什么是加一層解決不了的!

# 商品資訊
- 一般存放在關系型資料庫:Mysql,淘寶內部使用的Mysql都是經過內部改動的不是大家用的mysql,

# 商品描述、評論(文字居多)
- 檔案型資料庫:MongoDB

# 圖片
- 分布式檔案系統 FastDFS
- 淘寶:TFS
- Google: GFS
- Hadoop: HDFS
- 阿里云: oss

# 商品關鍵字 用于搜索
- 搜索引擎:solr,elasticsearch
- 阿里:Isearch 多隆
  ---所有苦逼的人都有一段苦逼的歲月~但是你只要像SB一樣的去堅持,終將牛逼!

# 商品熱門的波段資訊
- 記憶體資料庫:Redis,Tair、Memcache

# 商品交易,外部支付介面
- 第三方應用

大型互聯網應用問題:

  • 資料型別太多了!
  • 資料源繁多,經常重構!
  • 資料要改造,大面積改造

解決問題:

image-20220320150701724

NoSQL的四大分類

KV鍵值對:

  • 新浪:Redis
  • 美團: Redis+Tair
  • 阿里、百度:Redis + memecache

檔案行資料庫(bson格式和json一樣):

  • MongoDB(一般必須要掌握)
    • 基于分布式檔案存盤的資料庫,C++撰寫,用于處理大量檔案,
    • MongoDB是RDBMS和NoSQL的中間產品,MongoDB是非關系型資料庫中功能最豐富的,NoSQL中最像關系型資料庫的資料庫,
  • ConthDB

列存盤資料庫

  • HBase
  • 分布式檔案系統

圖關系資料庫

  • Neo4j、InfoGrid

四者對比

image-20220320153001249

二、Redis入門

概述

Redis是什么

Redis(Remote Dictionary Server ),即遠程字典服務,

是一個開源的使用ANSI C語言撰寫、支持網路、可基于記憶體亦可持久化的日志型、Key-Value資料庫,并提供多種語言的API,

與memcached一樣,為了保證效率,資料都是快取在記憶體中,區別的是redis會周期性的把更新的資料寫入磁盤或者把修改操作寫入追加的記錄檔案,并且在此基礎上實作了master-slave(主從)同步,

Redis能該干什么?

  1. 記憶體存盤、持久化,記憶體是斷電即失的,所以需要持久化(RDB、AOF)
  2. 高效率、用于高速緩沖
  3. 發布訂閱系統
  4. 地圖資訊分析
  5. 計時器、計數器(eg:瀏覽量)
  6. ................

特性

  1. 多樣的資料型別

  2. 持久化

  3. 集群

  4. 事務

    ….....

下載與安裝

官網:https://redis.io/

image-20220320155917671

中文網: http://www.redis.cn/

下載地址:通過官網下載即可

image-20220320154158257

注意:window在Github上下載

Redis推薦都是在Linux服務器上搭建的,我們都是基于Linux學習!

Windows安裝

下載地址:https://github.com/MSOpenTech/redis/releases

然后選擇你喜歡的版本zip或msi下載,這里建議下載 3.0.504 版本,因為 3.2.100 不是穩定版本

1.解壓安裝包

image-20220320155247000

2.雙擊redis-server.exe啟動redis服務器,雙擊redis-cli.exe打開redis客戶端(用來執行命令,訪問服務器的)

image-20220320155417668

3.啟動redis-cli.exe測驗

image-20220320155546529

Linux安裝

1.下載壓縮包 redis-6.2.6.tar.gz

2.解壓redis的安裝包

#第二步  解壓JDk
2.1)如果/usr下沒有redis目錄,則需要先創建一個java目錄: mkdir redis
2.2)再使用cp命令將redis-6.2.6.tar.gz檔案拷貝一份到/usr/redis
cp redis-6.2.6.tar.gz  /usr/redis/
2.3)在usr/redis下使用 tar -zxvf 命令解壓redis-6.2.6.tar.gz檔案
tar -zxvf redis-6.2.6.tar.gz
2.4)使用 rm -f  redis-6.2.6.tar.gz洗掉壓縮包
rm -f redis-6.2.6.tar.gz

image-20220322210552706

3.進入解壓后的檔案,我們可以看到redis的組態檔

image-20220322210834688

4.基本的環境安裝

yum install gcc-c++
gcc -v #查看安裝的版本

make  #執行make

image-20220322211347264

5.redis的默認安裝路徑 :usr/local/bin

image-20220322212009992

6.將redis組態檔,復制到我們當前目錄下 (當前目錄為/usr/local/bin/

image-20220322212440805

7.redis默認不是后臺啟動的,修改組態檔

image-20220322212626639

8.啟動redis服務

image-20220322214308802

9.使用redis-cli 進行連接測驗

image-20220322214520336

10.查看redis的行程是否開啟

image-20220322214838025

11.關閉redis服務 shutdown

image-20220322215112866

12.在此查看行程是否存在

image-20220322215045045

13.后面我們會使用單擊多Reids啟動集群

測驗性能

redis-benchmark:Redis官方提供的性能測驗工具,引數選項如下:

image-20220322215546861

簡單測驗:

#測驗: 100個并發連接,100000請求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000

測驗結果:

image-20220322222210533

基礎的知識

redis默認有16個資料庫

image-20220322222526384

默認使用的是第0個

可以使用select進行切換資料庫

image-20220322223211700

127.0.0.1:6379[3]> keys *  #查看資料庫所有的key
1) "name"

清空當前資料庫 flushdb

清空全部資料庫的內容 flushall

查看當前資料庫中所有的key: keys *

127.0.0.1:6379[3]> flushdb  #清空當前資料庫
OK
127.0.0.1:6379[3]> keys *
(empty array)

127.0.0.1:6379> flushall  #清空全部資料庫的內容 
OK
127.0.0.1:6379> keys *
(empty array)

image-20220322223555571

Redis 是單執行緒的

Redis是基于記憶體操作的,CPU不是Redis性能瓶頸,Redis的瓶頸是根據機器的記憶體網路帶寬

Redis是C語言寫的,官方提供的資料為 100000+的QPS,完全不比同樣是使用key-vale的Memecache差,

Redis為什么單執行緒還這么快?

1、誤區1:高性能的服務器一定是多執行緒的?

2、誤區2:多執行緒(CPU背景關系會切換!)一定比單執行緒效率高!

核心:redis是將所有的資料全部放在記憶體中的,所以說單執行緒去操作效率就是最高的,多執行緒(cpu背景關系會切換,耗時的操作,) ,對呀記憶體系統來說,如果沒有背景關系切換效率就是最高的,多次讀寫都是在一個cpu上的,在記憶體情況下,這就是最佳的方案,

三、五大資料型別

Redis是一個開源(BSD許可),記憶體存盤的資料結構服務器,可用作資料庫高速快取訊息佇列代理,它支持多種型別的資料結構,如:字串(strings)、哈希表(hashes)、串列(lists)、集合(sets)、有序集合(sorted sets)與范圍查詢,bitmaps,位圖,hyperloglogs,地理空間(geospaial),索引半徑查詢等資料型別,內置復制、Lua腳本、LRU驅動時間、事務以及不同級別磁盤持久化功能,同時通過Redis Sentinel提供高可用,通過Redis Cluster提供自動磁區,

Redis-key

在redis中無論什么資料型別,在資料庫中都是以key-value形式保存,通過進行對Redis-key的操作,來完成對資料庫中資料的操作,

一些常用命令

127.0.0.1:6379> exists name #判斷當前的key是否存在
(integer) 1
127.0.0.1:6379> exists name1
(integer) 0
127.0.0.1:6379> move name 1 #移除當前的key
(integer) 1
127.0.0.1:6379> keys *  #查看所有的key
1) "age"
127.0.0.1:6379> set name jihu  #set key
OK
127.0.0.1:6379> keys *
1) "name"
2) "age"
127.0.0.1:6379> get name
"jihu"
127.0.0.1:6379> expire name 10  #設定key的過期時間,單位是秒
(integer) 1
127.0.0.1:6379> ttl name  #查看當前key的剩余時間
(integer) 3
127.0.0.1:6379> ttl name
(integer) 1
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> type name #查看當前key的型別
string
127.0.0.1:6379> type age
string

更多命令學習:https://www.redis.net.cn/order/

String(字串)

127.0.0.1:6379> set key1 vl  #設定值
OK
127.0.0.1:6379> get key1   #獲取值
"vl"
127.0.0.1:6379> keys *   #獲得所有的key
1) "key1"
2) "name"
3) "age"
127.0.0.1:6379> exists key1  #判斷某一個key是否存在
(integer) 1
127.0.0.1:6379> APPEND key1 "hello"  #追加字串,如果當前key不存在就相當于setkey
(integer) 7
127.0.0.1:6379> get key1
"vlhello"
127.0.0.1:6379> strlen key1  #獲取字串的長度
(integer) 7
127.0.0.1:6379> append key1 ",zhangsan"
(integer) 16
127.0.0.1:6379> get key1
"vlhello,zhangsan"
127.0.0.1:6379> strlen key1
(integer) 16
127.0.0.1:6379> 
##########################################################
#步長 i+=
127.0.0.1:6379> set views 0  #初始瀏覽量為0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views  #使值加1(自增1) 
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> decr views  #使值減一(自減1)
(integer) 1
127.0.0.1:6379> decr views
(integer) 0
127.0.0.1:6379> decr views
(integer) -1
127.0.0.1:6379> get views
"-1"
127.0.0.1:6379> incrby views 10  #設定步長,指定增量
(integer) 9
127.0.0.1:6379> incrby views 10
(integer) 19
127.0.0.1:6379> decrby views 5  #設定步長,減少增量
(integer) 14
#############################################################
#字串范圍 : range
127.0.0.1:6379> get key1
"vlhello,zhangsan"
127.0.0.1:6379> getrange key1 2 5  #截取字串 [2,5]
"hell"
127.0.0.1:6379> getrange key1 2 -1  #從第二個字符開始,獲取全部字串
"hello,zhangsan"
127.0.0.1:6379> getrange key1 0 -1  #獲取全部字串 和 get key是一樣的
"vlhello,zhangsan"

#替換
127.0.0.1:6379> set key2 asfzxc
OK
127.0.0.1:6379> get key2
"asfzxc"
127.0.0.1:6379> setrange key2 2 www  #替換指定位置開始的字串
(integer) 6
127.0.0.1:6379> get key2
"aswwwc"
#############################################################
# setex (set with expire)  #設定過期時間
# setnx (set if not exist)  #不存在在設定 (在分布式鎖中會常常使用)
127.0.0.1:6379> get key1
"vlhello,zhangsan"
127.0.0.1:6379> setex key1 10 "zhangsan" #設定key1的值為 zhangsan,10秒后過期	
OK
127.0.0.1:6379> get key1
"zhangsan"
127.0.0.1:6379> ttl key1
(integer) -2
127.0.0.1:6379> get key1
(nil)
127.0.0.1:6379> setnx key3 "redis"  #如果key3不存在,創建key3
(integer) 1
127.0.0.1:6379> setnx key3 "monkey" #如果key3存在,創建失敗
(integer) 0
127.0.0.1:6379> get key3
"redis"
#############################################################
mset
mget
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3  #同時設定多個值
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379> mget k1 k2 k3  #同時獲取多個值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379>  msetnx k1 v1 k5 v5 #msetnx 是一個原子性操作,要么都成功要么都失敗
(integer) 0
127.0.0.1:6379> get k5
(nil)

#物件
set user:1 {name:zhangsan,age:30}  #設定一個user:1物件值為json字符來保存一個物件,
127.0.0.1:6379> set user:1 {name:zhangsan,age:30}
OK
127.0.0.1:6379> get user:1
"{name:zhangsan,age:30}"

#這里的key是一個巧妙的設計: user:{id}:{filed},如此設計在redis中是可以的,
127.0.0.1:6379> mset user:1:name lisi user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "lisi"
2) "2"
127.0.0.1:6379> get user:1
(nil)
#############################################################
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 monkey #如果存在值,獲取原來的值,并設定新的值
"redis"
127.0.0.1:6379> get db
"monkey"

String類似的使用場景:value除了是我們的字串還可以是我們的數字!

  • 計數器
  • 統計多單位的數量 uid:5416456:follow 0
  • 粉絲數
  • 物件快取存盤

List(串列)

Redis串列是簡單的字串串列,按照插入順序排序,你可以添加一個元素到串列的頭部(左邊)或者尾部(右邊)

一個串列最多可以包含 232 - 1 個元素 (4294967295, 每個串列超過40億個元素),

首先我們串列,可以經過規則定義將其變為佇列、堆疊、雙端佇列等

image-20220323113025780

正如圖Redis中List是可以進行雙端操作的,所以命令也就分為了LXXXRLLL兩類,有時候L也表示List例如LLEN

####################################################
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  #獲取list中的值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1  #通過區間獲取具體的值
1) "three"
2) "two"
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"
####################################################
LPOP
RPOP
127.0.0.1:6379> lpop list  #移除list的第一個元素
"three"
127.0.0.1:6379> lpop list 2  #移除list的前兩個元素
1) "two"
2) "one"
127.0.0.1:6379> lrange list  0 -1  #獲取list中的值
1) "right"
127.0.0.1:6379> lrange list 0 -1
1) "right"
2) "li1"
3) "li"
127.0.0.1:6379> rpop list 1 #移除list的最后一個元素
1) "li"
127.0.0.1:6379> lrange list 0 -1
1) "right"
2) "li1"
####################################################
Lindex
127.0.0.1:6379> lrange list 0 -1
1) "right"
2) "li1"
127.0.0.1:6379> lindex list 2 #通過下標獲得list中的某一個值
(nil)
127.0.0.1:6379> lindex list 0
"right"
####################################################
Llen
127.0.0.1:6379> llen list  #回傳串列的長度
(integer) 2
####################################################
移除指定的值  Lrem
取關用到: uid
127.0.0.1:6379> lrange list  0 -1
1) "one"
2) "one"
3) "right"
4) "li1"
127.0.0.1:6379> lrem list 1 one  #移除list集合中指定個數的value,精確匹配
(integer) 1
127.0.0.1:6379> lrange list  0 -1
1) "one"
2) "right"
3) "li1"
127.0.0.1:6379> lpush list one
(integer) 4
127.0.0.1:6379> lpush list one
(integer) 5
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "one"
3) "one"
4) "right"
5) "li1"
127.0.0.1:6379> lrem list 3 one  #移除三個指定的值
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "right"
2) "li1"
####################################################
trim  修剪 , list截斷
127.0.0.1:6379> rpush mulist "hello1"
(integer) 1
127.0.0.1:6379> rpush mulist "hello2"
(integer) 2
127.0.0.1:6379> rpush mulist "hello3"
(integer) 3
127.0.0.1:6379> rpush mulist "hello4"
(integer) 4
127.0.0.1:6379> lrange mulist  0 -1
1) "hello1"
2) "hello2"
3) "hello3"
4) "hello4"
127.0.0.1:6379> ltrim mulist 1 2  #通過下標截取指定的長度,這個list已經被改變了,截斷了只剩下截取的元素,
OK
127.0.0.1:6379> lrange mulist  0 -1
1) "hello2"
2) "hello3"
####################################################
rpoplpush #移除串列的最后一個元素,將它移動到新的串列當中
127.0.0.1:6379> lrange mulist 0 -1
1) "hello2"
2) "hello3"
3) "hello4"
4) "hello5"
127.0.0.1:6379> rpoplpush mulist myotherlist #移除串列的最后一個元素,將它移動到新的串列當中
"hello5"
127.0.0.1:6379> lrange mulist 0 -1   #查看原來的串列
1) "hello2"
2) "hello3"
3) "hello4"
127.0.0.1:6379> lrange myotherlist 0 -1  #查看目標串列中,確實存在該值
1) "hello5"
####################################################
lset  將串列中指定下標的值替換為另一個值,相當于更新操作
127.0.0.1:6379> exists lit  #判斷這個串列是否存在
(integer) 0
127.0.0.1:6379> lset list 0 item  #如果不存在串列 我們去更新就會報錯
(error) ERR no such key
127.0.0.1:6379> lpush list value1
(integer) 1
127.0.0.1:6379> lrange list 0 0
1) "value1"
127.0.0.1:6379> lset list 0 item  #如果存在,更新當前下標的值
OK
127.0.0.1:6379> lrange list 0 0
1) "item"
127.0.0.1:6379> lset list 1 other  #如果不存在 則會報錯
(error) ERR index out of range
####################################################
linsert  #將某個具體的value值插入到串列中某個元素的前面或者后面

127.0.0.1:6379> lrange mulist 0 -1
1) "hello2"
2) "hello3"
3) "hello4"
127.0.0.1:6379> linsert mulist before hello3 zhangsan #將zhangsan值插入到mulist串列中hello3的前面
(integer) 4
127.0.0.1:6379> lrange mulist 0 -1
1) "hello2"
2) "zhangsan"
3) "hello3"
4) "hello4"
127.0.0.1:6379> linsert mulist after hello3 zhangsha #將zhangsha值插入到mulist串列中hello3的后面
(integer) 5
127.0.0.1:6379> lrange mulist 0 -1
1) "hello2"
2) "zhangsan"
3) "hello3"
4) "zhangsha"
5) "hello4"

小結

  • 它實際上是一個鏈表,before node after ,left , right 都可以插入值
  • 如果key不存在,創建新的鏈表
  • 如果key存在,新增內容
  • 如果移除了所有值,空鏈表,也代表不存在,
  • 在兩邊插入或者改動值,效率最高,中間元素相對來說效率會低一點

可以做訊息排隊 訊息佇列( Lpush Rpop) ,堆疊(Lpush Lpop)

Set(集合)

set中的值是不能重復的

####################################################
127.0.0.1:6379> sadd myset hello  #set集合中添加元素
(integer) 1
127.0.0.1:6379> sadd myset zhangsan
(integer) 1
127.0.0.1:6379> sadd myset lisi
(integer) 1
127.0.0.1:6379> smembers myset  #查看指定set的所有值
1) "hello"
2) "lisi"
3) "zhangsan"
127.0.0.1:6379> sismember myset lisi #判斷某一個值是不是在set集合中
(integer) 1
127.0.0.1:6379> sismember myset world
(integer) 0

####################################################
scard
127.0.0.1:6379> scard myset #獲取set集合中內容的元素個數!
(integer) 3
127.0.0.1:6379> sadd myset hello
(integer) 0
####################################################
rem
127.0.0.1:6379> srem myset hello #移除set集合中的指定元素
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "lisi"
2) "zhangsan"
127.0.0.1:6379> scard myset
(integer) 2

####################################################
set 無序不重復集合,比如:隨機抽獎
127.0.0.1:6379> srandmember myset   #隨機抽選出一個元素
"lisi"
127.0.0.1:6379> srandmember myset 
"zhangsan"
127.0.0.1:6379> srandmember myset 
"zhangsan"
127.0.0.1:6379> srandmember myset 
"lisi"
127.0.0.1:6379> SMEMBERS myset
1) "lisi"
2) "zhangsan"
127.0.0.1:6379> srandmember myset 2  #隨機抽選出兩個元素
1) "lisi"
2) "zhangsan"

####################################################
洗掉定的key,洗掉隨機的key

127.0.0.1:6379> SMEMBERS myset
1) "111"
2) "blue"
3) "lisi"
4) "zhangsan"
127.0.0.1:6379> spop myset  #隨機洗掉一些set集合中的元素
"lisi"
127.0.0.1:6379> spop myset
"111"
127.0.0.1:6379> SMEMBERS myset
1) "blue"
2) "zhangsan"

####################################################
smove 將一個指定的值,移動到另外一個set集合
127.0.0.1:6379> sadd myset world zhangsan 
(integer) 2
127.0.0.1:6379> SMEMBERS myset
1) "hello"
2) "world"
3) "hello2"
4) "hello3"
5) "zhangsan"
127.0.0.1:6379> smove myset myset2 zhangsan   #將myset中的zhangsan移動到myset2集合中去
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "world"
2) "hello2"
3) "hello3"
4) "hello"
127.0.0.1:6379> SMEMBERS myset2
1) "zhangsan"

####################################################
微博,B站中的共同關注!(并集)
- 差集  sdiff
- 交集  sinter
- 并集  sunion
127.0.0.1:6379> sadd key1 a b c d 
(integer) 4
127.0.0.1:6379> sadd key2 c d e f
(integer) 4
127.0.0.1:6379> SDIFF key1 key2   #差集
1) "b"
2) "a"
127.0.0.1:6379> SINTER key1 key2  #交集   共同好友就可以這樣實作
1) "d"
2) "c"
127.0.0.1:6379> SUNION key1 key2  #并集
1) "b"
2) "c"
3) "e"
4) "a"
5) "d"
6) "f"


Hash(哈希)

Map集合 , key-map,時候這個值是一個map集合,本質和string型別沒有太大區別,還是一個簡單的key-value!

####################################################
hset myhash field zhangsan
127.0.0.1:6379> hset myhash field1 zhangsan  #set一個具體的 key-value
(integer) 1
127.0.0.1:6379> hget myhash field1 #獲取一個欄位值
"zhangsan"
127.0.0.1:6379> hmset myhash field1 hello field2 world #設定多個key-value
OK
127.0.0.1:6379> hmget myhash field1 field2 #獲取多個欄位值
1) "hello"
2) "world"
127.0.0.1:6379> hgetall myhash #獲取全部的資料
1) "field1"
2) "hello"
3) "field2"
4) "world"
127.0.0.1:6379> hdel myhash field1 #洗掉hash指定key欄位! 對應的value值也就消失了
(integer) 1
127.0.0.1:6379> HGETALL myhash
1) "field2"
2) "world"
####################################################
hlen #獲取hash表的欄位數量
127.0.0.1:6379> hmset myhash field hello field3 wufeng field4 lis 
OK
127.0.0.1:6379> HGETALL myhash
1) "field2"
2) "world"
3) "field"
4) "hello"
5) "field3"
6) "wufeng"
7) "field4"
8) "lis"
127.0.0.1:6379> hget myhash field  
"hello"
127.0.0.1:6379> hlen myhash  #獲取hash表的欄位數量
(integer) 4

####################################################
127.0.0.1:6379> HEXISTS myhash field #判斷hash中指定欄位是否存在
(integer) 1
127.0.0.1:6379> HEXISTS myhash field6
(integer) 0

####################################################
#只獲得所有filed   hkeys
#只獲得所有value   hvals
127.0.0.1:6379> HKEYS myhash #獲得所有filed
1) "field2"
2) "field"
3) "field3"
4) "field4" 
127.0.0.1:6379> HVALS myhash  #獲得所有value
1) "world"
2) "hello"
3) "wufeng"
4) "lis"

####################################################
hincrby #加1  hdecrby #減1
127.0.0.1:6379> hset myhash field6 5  #指定增量為5
(integer) 1
127.0.0.1:6379> HINCRBY myhash field6 1 #數值加1
(integer) 6
127.0.0.1:6379> HINCRBY myhash field6 -2 #數值減2
(integer) 3
127.0.0.1:6379> HSETNX myhash field7 hello #如果不存在則可以設定
(integer) 1
127.0.0.1:6379> HSETNX myhash field6 hello #如果存在則可以設定
(integer) 0

hash變更的資料 user name age ,尤其是用戶資訊之類的,經常變動的資訊, hash更適合于物件的存盤,String更加適合字串的存盤,

127.0.0.1:6379> hmset myhash user:1:name zhangsan user:1:age 30 #設定user:{id}:{field}
OK
127.0.0.1:6379> hmget myhash user:1:name user:1:age
1) "zhangsan"
2) "30"
127.0.0.1:6379> HGETALL myhash
1) "field7"
2) "hello"
3) "user:1:name"
4) "zhangsan"
5) "user:1:age"
6) "30"

Zset(有序集合)

在set的基礎上,增加一個值,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"

####################################################
排序如何實作

127.0.0.1:6379> zadd salary 2500 xiaohong  #添加三個用戶   薪水,名字
(integer) 1
127.0.0.1:6379> zadd salary 5000 zhangsan
(integer) 1
127.0.0.1:6379> zadd salary 1000 lisi
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf  #顯示全部用戶,按薪水從小到大排名 -inf:表示負無窮
1) "lisi"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> zrevrange salary 0 -1 withscores # 從大到小進行排序
1) "zhangsan"
2) "5000"
3) "xiaohong"
4) "2500"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores #查找所有的資料 ,withscores:帶有的薪水資訊
1) "lisi"
2) "1000"
3) "xiaohong"
4) "2500"
5) "zhangsan"
6) "5000"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 2500 withscores #顯示工資小于2500員工的升序排列
1) "lisi"
2) "1000"
3) "xiaohong"
4) "2500"

####################################################
移除:zrem
127.0.0.1:6379> zrange salary 0 -1
1) "lisi"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> zrem salary lisi  #移除有序集合中的指定元素 
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "xiaohong"
2) "zhangsan"
127.0.0.1:6379> zcard salary  #獲取集合中的個數
(integer) 2

####################################################
127.0.0.1:6379> zrange myset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> zcount myset 0 2  #獲取指定區間的成員數量
(integer) 2
127.0.0.1:6379> zcount myset 0 5
(integer) 3

案例思路:set 排序,存盤班級成績表,工資表排序

普通訊息, 1.重要訊息 2.帶權重進行判斷

排行榜應用實作,

四、三種特殊資料型別

geospatial地理位置詳解

使用場景: 朋友的定位,附近的人,打車距離計算

這個功能可以推算地理位置資訊,兩地之間的距離,方圓幾里的人,

可以查詢一些測驗資料:http://www.jsons.cn/lngcodeinfo/0706D99C19A781A3

官方檔案:https://www.redis.net.cn/order/3685.html

只有六個命令

GEOADD
GEODIST
GEOHASH
GEOPOS 
GEORADIUS
GEORADIUSBYMEMBER

geoadd

#geoadd 添加地理位置
# 規則: 兩極無法直接添加,我們一般會下載城市資料,直接通過java程式一次性匯入
#引數  key 值(緯度、經度、名稱)
	#有效的經度從-180度到180度,
	#有效的緯度從-85.05112878度到85.05112878度,
	#當坐標位置超出上述指定范圍時,該命令將會回傳一個錯誤,
127.0.0.1:6379> geoadd china:city  116.40 39.90 beijing
(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
(integer) 1
127.0.0.1:6379> geoadd china:city 114.08 22.54   shenzhen 116.85 38.31 cangzhou
(integer) 2
127.0.0.1:6379> geoadd china:city 120.15 30.28  hangzhou 125.14 42.92 xian
(integer) 2

geopos

獲得當前定位:一定是一個坐標值,
127.0.0.1:6379> geopos china:city beijing #獲取指定的城市的經度和緯度
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
127.0.0.1:6379> geopos china:city beijing cangzhou
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
2) 1) "116.84999853372573853"
   2) "38.30999992507150864"

geodist

兩人之間的距離

單位:

  • m 表示單位為米
  • km 表示單位為千米
  • mi 表示單位為英里
  • ft 表示單位為英尺
127.0.0.1:6379> geodist china:city beijing shanghai km #查看北京到上海的直線距離
"1067.3788"
127.0.0.1:6379> GEODIST china:city beijing chongqing km #查看北京到重慶的直線距離
"1464.0708"

georadius 以給定的經緯度為中心,找出某一半徑內的元素

我附近的人? (獲得所有附近的人的地址,定位!) 通過半徑來查詢

獲得指定數量的人為200個 count 200

所有資料應該都錄入 china:city ,才會讓結果更加清晰

127.0.0.1:6379> GEORADIUS china:city 110 30 500 km #以110 30這個經度緯度為中心,尋找方圓1000km內的城市
1) "chongqing"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km
1) "chongqing"
2) "shenzhen"
3) "hangzhou"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist #顯示到中間距離的位置
1) 1) "chongqing"
   2) "341.9374"
2) 1) "shenzhen"
   2) "923.9364"
3) 1) "hangzhou"
   2) "976.4868"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord  #顯示他人的定位資訊
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "shenzhen"
   2) "923.9364"
   3) 1) "114.08000081777572632"
      2) "22.53999903789756587"
3) 1) "hangzhou"
   2) "976.4868"
   3) 1) "120.15000075101852417"
      2) "30.2800007575645509"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord  count 1
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord  count 2 #篩選出指定的結果!
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "shenzhen"
   2) "923.9364"
   3) 1) "114.08000081777572632"
      2) "22.53999903789756587"
127.0.0.1:6379> 

georadiusbymember

#找出位于指定元素周圍的其他元素
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km
1) "cangzhou"
2) "beijing"
3) "xian"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 500km
(error) ERR wrong number of arguments for 'georadiusbymember' command
127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 500 km
1) "hangzhou"
2) "shanghai"

geohash 回傳一個或多個位置元素的geohash表示(很少使用到)

該命令將回傳11個字符的geohash字串

#將二維的經緯度轉換為一維的字串,如果兩個字串越近,那么則距離越近,
127.0.0.1:6379> GEOHASH china:city beijing chongqing
1) "wx4fbxxfke0"
2) "wm5xzrybty0"

geo 底層的實作原理其實就是Zset 我們可以使用Zset命令來操作geo

127.0.0.1:6379> ZRANGE china:city 0 -1  #查看地圖中全部元素
1) "chongqing"
2) "shenzhen"
3) "hangzhou"
4) "shanghai"
5) "cangzhou"
6) "beijing"
7) "xian"
127.0.0.1:6379> zrem china:city beijing xian  #移除指定元素
(integer) 2
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqing"
2) "shenzhen"
3) "hangzhou"
4) "shanghai"
5) "cangzhou"

Hyperloglog基數統計

什么是基數?

image-20220323211451148

簡介

Redis 2.8.9版本就更新了hyperloglog 資料結構

Redis hyperloglog :基數統計的演算法

優點:占用的記憶體是固定的,2^64個不同的元素的技術,只需要花費12kb記憶體,如果要從記憶體角度來比較的話 hyperloglog首選!

網頁的UV(一個人訪問一個網站多次,但還是算作一個個)

傳統的方式,set保存用戶的id,然后就可以統計set中的元素數量作為標準判斷.

這個方式如果保存大量的用戶id,就會比較麻煩,我們的目的是為了計數,而不是保存用戶id.

0.81%錯誤率,統計UV任務,可以忽略不計.

127.0.0.1:6379> PFADD mykey a b c d e f g h k  #創建第一組元素  mykey
(integer) 1
127.0.0.1:6379> PFCOUNT mykey #統計 mykey 元素的基數數量
(integer) 9
127.0.0.1:6379> PFADD mykey2 q w e r t y u i o #創建第二組元素  mykey2
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 9
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 # 合并兩組  mykey mykey2 => mykey3
OK
127.0.0.1:6379> PFCOUNT mykey3 #看并集的數量  相同的不計
(integer) 16

如果允許容錯,那么一個可用使用 hyperloglog

如果不允許容錯,就使用set或者自己的資料型別即可

Bitmaps位圖場景詳解

位存盤

統計用戶資訊,活躍,不活躍!登錄,未登錄!打卡,未打卡! 兩個的狀態,都可以使用bitmaps

Bitmaps位圖,資料結構!都是操作二進制位來進行記錄,就只有0和1兩個狀態.

365天 = 365bit 1位元組 = 8bit 46個位元組左右!

使用bitmap來記錄 周一到周日的打卡

127.0.0.1:6379> setbit sign 0 1  #周一到周日打卡情況,1代表打卡  0代表未打卡
(integer) 0
127.0.0.1:6379> setbit sign 1 1
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 0
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 1
(integer) 0

127.0.0.1:6379> GETBIT sign 5  #查看某一天是否打卡
(integer) 0
127.0.0.1:6379> GETBIT sign 6
(integer) 1

127.0.0.1:6379> bitcount sign #統計這周的打卡記錄
(integer) 4

五、事務

Redis事務本質: 一組命令的集合, 一個事務中的所有命令都會被序列化,在事務執行程序中,會按照順序執行!

一次性、順序性、排他性 執行一系列的命令

------  佇列  set set set 執行  ---------

Redis事務沒有隔離級別的概念!

所有的命令在事務中,并沒有直接被執行,只有發起執行命令的時候才會執行

redis單條命令是保證原子性的,但是事務不保證原子性

Redis的事務:

  • 開啟事務(multi
  • 命令入隊(........)
  • 執行事務(exec

正常執行事務

127.0.0.1:6379> multi  #開啟事務
OK
127.0.0.1:6379(TX)> set k1 v1  #命令入隊
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> exec  #執行事務
1) OK
2) OK
3) "v2"
4) OK

放棄事務 ( discard )

127.0.0.1:6379> multi #開啟事務
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> DISCARD  #取消事務
OK
127.0.0.1:6379> get k2  #事務佇列中命令都不會被執行
(nil)
127.0.0.1:6379> get k1
(nil)

編譯型例外(代碼有問題 ,及命令有錯) ,事務中所有的命令都不會被執行

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1 
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> getset k2  #錯誤的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> exec  #執行事務報錯
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k3   #所有的命令都不執行
(nil)
127.0.0.1:6379> get k1
(nil)

運行時例外(1/0),如果事務佇列中存在語法性,那么執行命令的時候,其他命令是可以正常執行的,錯誤命令拋出例外

127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> incr k1  #執行的時候會失敗
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range #雖然第一條命令報錯了,但是依舊正常執行成功了,
4) "v2"
127.0.0.1:6379> get k3
"v3"

悲觀鎖、樂觀鎖

監控 watch (面試常問)

悲觀鎖:

  • 很悲觀,認為什么時候都會出現問題,無論做什么都會加鎖

樂觀鎖:

  • 很樂觀,認為什么時候都不會出現問題,所以不會上鎖!更新資料的時候去判斷一下,在此期間是否有人修改過這個資料
  • 獲取version
  • 更新的時候比較version

redis監視測驗

正常執行成功

127.0.0.1:6379> set money 100  # 設定余額:100
OK
127.0.0.1:6379> set out 0  # 支出使用:0
OK
127.0.0.1:6379> watch money #監視money 物件
OK
127.0.0.1:6379> multi  
OK
127.0.0.1:6379(TX)> DECRBY money 20
QUEUED
127.0.0.1:6379(TX)> INCRBY out 20
QUEUED
127.0.0.1:6379(TX)> exec  #事務正常結束,資料期間沒有發生變動,這個時候就正常執行成功
1) (integer) 80
2) (integer) 20

測驗多執行緒修改值,使用watch 可以當做redis的樂觀鎖操作

#第一個執行緒
127.0.0.1:6379> watch money  #監控 money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 20
QUEUED
127.0.0.1:6379(TX)> INCRBY out 20
QUEUED
127.0.0.1:6379(TX)> exec  #執行之前,另外一個執行緒,修改了我們的值,這個時候就會導致食物執行失敗,
(nil)

#第二個執行緒
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> INCRby money 100
(integer) 180
127.0.0.1:6379> get money
"180"

? unwatch進行解鎖,

如果修改失敗,獲取最新的值就好

image-20220323223346903

注意:每次提交執行exec后都會自動釋放鎖,不管是否成功

六、Jedis

使用Java來操作Redis,Jedis是Redis官方推薦使用的Java連接redis的客戶端的開發工具,使用java操作redis中間件,

測驗

1.匯入對應的依賴

  <dependencies>
        <!--匯入jedis的包-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.2.0</version>
        </dependency>
        <!--fastjson 存一些資料用的-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.48</version>
        </dependency>

    </dependencies>

2.編碼測驗:

  • 連接資料庫
  • 操作命令
  • 斷開連接
package com.jihu;
import redis.clients.jedis.Jedis;
public class TestPing {
    public static void main(String[] args) {
        //1. new jedis 物件即可
        Jedis jedis = new Jedis("192.168.56.130",6379);  //自己虛擬機的ip地址
        jedis.auth("123456");

        // jedis 所有的命令就是我們之前學習的所有指令
        System.out.println(jedis.ping());

    }
}

輸出結果:

image-20220324095107143

常用的API

String 、list、set 、hash、Zset等 所有的api命令,就是我們對應的上面學習的指令,一個都沒有變化!

判斷keys * 以及一些基本方法

package com.jihu.base;

import redis.clients.jedis.Jedis;

import java.util.Set;

public class TestKey {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("192.168.56.130",6379);
        jedis.auth("123456");

        System.out.println("清空資料:"+jedis.flushDB());
        System.out.println("判斷某個鍵是否存在:"+jedis.exists("username"));
        System.out.println("新增<'username','kuangshen'>的鍵值對:"+jedis.set("username", "kuangshen"));
        System.out.println("新增<'password','password'>的鍵值對:"+jedis.set("password", "password"));
        System.out.print("系統中所有的鍵如下:");
        Set<String> keys = jedis.keys("*");
        System.out.println(keys);
        System.out.println("洗掉鍵password:"+jedis.del("password"));
        System.out.println("判斷鍵password是否存在:"+jedis.exists("password"));
        System.out.println("查看鍵username所存盤的值的型別:"+jedis.type("username"));
        System.out.println("隨機回傳key空間的一個:"+jedis.randomKey());
        System.out.println("重命名key:"+jedis.rename("username","name"));
        System.out.println("取出改后的name:"+jedis.get("name"));
        System.out.println("按索引查詢:"+jedis.select(0));
        System.out.println("洗掉當前選擇資料庫中的所有key:"+jedis.flushDB());
        System.out.println("回傳當前資料庫中key的數目:"+jedis.dbSize());
        System.out.println("洗掉所有資料庫中的所有key:"+jedis.flushAll());
    }
}

對String操作的命令

package com.jihu.base;
import redis.clients.jedis.Jedis;
import java.util.concurrent.TimeUnit;

public class TestString {
    public static void main(String[] args) {
       Jedis jedis = new Jedis("192.168.56.130",6379);
        jedis.auth("123456");

        jedis.flushDB();
        System.out.println("===========增加資料===========");
        System.out.println(jedis.set("key1","value1"));
        System.out.println(jedis.set("key2","value2"));
        System.out.println(jedis.set("key3", "value3"));
        System.out.println("洗掉鍵key2:"+jedis.del("key2"));
        System.out.println("獲取鍵key2:"+jedis.get("key2"));
        System.out.println("修改key1:"+jedis.set("key1", "value1Changed"));
        System.out.println("獲取key1的值:"+jedis.get("key1"));
        System.out.println("在key3后面加入值:"+jedis.append("key3", "End"));
        System.out.println("key3的值:"+jedis.get("key3"));
        System.out.println("增加多個鍵值對:"+jedis.mset("key01","value01","key02","value02","key03","value03"));
        System.out.println("獲取多個鍵值對:"+jedis.mget("key01","key02","key03"));
        System.out.println("獲取多個鍵值對:"+jedis.mget("key01","key02","key03","key04"));
        System.out.println("洗掉多個鍵值對:"+jedis.del("key01","key02"));
        System.out.println("獲取多個鍵值對:"+jedis.mget("key01","key02","key03"));

        jedis.flushDB();
        System.out.println("===========新增鍵值對防止覆寫原先值==============");
        System.out.println(jedis.setnx("key1", "value1"));
        System.out.println(jedis.setnx("key2", "value2"));
        System.out.println(jedis.setnx("key2", "value2-new"));
        System.out.println(jedis.get("key1"));
        System.out.println(jedis.get("key2"));

        System.out.println("===========新增鍵值對并設定有效時間=============");
        System.out.println(jedis.setex("key3", 2, "value3"));
        System.out.println(jedis.get("key3"));
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(jedis.get("key3"));

        System.out.println("===========獲取原值,更新為新值==========");
        System.out.println(jedis.getSet("key2", "key2GetSet"));
        System.out.println(jedis.get("key2"));

        System.out.println("獲得key2的值的字串:"+jedis.getrange("key2", 2, 4));
    }
}

對List操作的命令

package com.jihu.base;
import redis.clients.jedis.Jedis;
public class TestList {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("192.168.56.130",6379);
        jedis.auth("123456");
        jedis.flushDB();
        System.out.println("===========添加一個list===========");
        jedis.lpush("collections", "ArrayList", "Vector", "Stack", "HashMap", "WeakHashMap", "LinkedHashMap");
        jedis.lpush("collections", "HashSet");
        jedis.lpush("collections", "TreeSet");
        jedis.lpush("collections", "TreeMap");
        System.out.println("collections的內容:"+jedis.lrange("collections", 0, -1));//-1代表倒數第一個元素,-2代表倒數第二個元素,end為-1表示查詢全部
        System.out.println("collections區間0-3的元素:"+jedis.lrange("collections",0,3));
        System.out.println("===============================");
        // 洗掉串列指定的值 ,第二個引數為洗掉的個數(有重復時),后add進去的值先被刪,類似于出堆疊
        System.out.println("洗掉指定元素個數:"+jedis.lrem("collections", 2, "HashMap"));
        System.out.println("collections的內容:"+jedis.lrange("collections", 0, -1));
        System.out.println("洗掉下表0-3區間之外的元素:"+jedis.ltrim("collections", 0, 3));
        System.out.println("collections的內容:"+jedis.lrange("collections", 0, -1));
        System.out.println("collections串列出堆疊(左端):"+jedis.lpop("collections"));
        System.out.println("collections的內容:"+jedis.lrange("collections", 0, -1));
        System.out.println("collections添加元素,從串列右端,與lpush相對應:"+jedis.rpush("collections", "EnumMap"));
        System.out.println("collections的內容:"+jedis.lrange("collections", 0, -1));
        System.out.println("collections串列出堆疊(右端):"+jedis.rpop("collections"));
        System.out.println("collections的內容:"+jedis.lrange("collections", 0, -1));
        System.out.println("修改collections指定下標1的內容:"+jedis.lset("collections", 1, "LinkedArrayList"));
        System.out.println("collections的內容:"+jedis.lrange("collections", 0, -1));
        System.out.println("===============================");
        System.out.println("collections的長度:"+jedis.llen("collections"));
        System.out.println("獲取collections下標為2的元素:"+jedis.lindex("collections", 2));
        System.out.println("===============================");
        jedis.lpush("sortedList", "3","6","2","0","7","4");
        System.out.println("sortedList排序前:"+jedis.lrange("sortedList", 0, -1));
        System.out.println(jedis.sort("sortedList"));
        System.out.println("sortedList排序后:"+jedis.lrange("sortedList", 0, -1));
    }
}


對set操作的命令

package com.jihu.base;

import redis.clients.jedis.Jedis;

public class TestSet {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("192.168.56.130",6379);
        jedis.auth("123456");
        jedis.flushDB();
        System.out.println("============向集合中添加元素(不重復)============");
        System.out.println(jedis.sadd("eleSet", "e1","e2","e4","e3","e0","e8","e7","e5"));
        System.out.println(jedis.sadd("eleSet", "e6"));
        System.out.println(jedis.sadd("eleSet", "e6"));
        System.out.println("eleSet的所有元素為:"+jedis.smembers("eleSet"));
        System.out.println("洗掉一個元素e0:"+jedis.srem("eleSet", "e0"));
        System.out.println("eleSet的所有元素為:"+jedis.smembers("eleSet"));
        System.out.println("洗掉兩個元素e7和e6:"+jedis.srem("eleSet", "e7","e6"));
        System.out.println("eleSet的所有元素為:"+jedis.smembers("eleSet"));
        System.out.println("隨機的移除集合中的一個元素:"+jedis.spop("eleSet"));
        System.out.println("隨機的移除集合中的一個元素:"+jedis.spop("eleSet"));
        System.out.println("eleSet的所有元素為:"+jedis.smembers("eleSet"));
        System.out.println("eleSet中包含元素的個數:"+jedis.scard("eleSet"));
        System.out.println("e3是否在eleSet中:"+jedis.sismember("eleSet", "e3"));
        System.out.println("e1是否在eleSet中:"+jedis.sismember("eleSet", "e1"));
        System.out.println("e1是否在eleSet中:"+jedis.sismember("eleSet", "e5"));
        System.out.println("=================================");
        System.out.println(jedis.sadd("eleSet1", "e1","e2","e4","e3","e0","e8","e7","e5"));
        System.out.println(jedis.sadd("eleSet2", "e1","e2","e4","e3","e0","e8"));
        System.out.println("將eleSet1中洗掉e1并存入eleSet3中:"+jedis.smove("eleSet1", "eleSet3", "e1"));//移到集合元素
        System.out.println("將eleSet1中洗掉e2并存入eleSet3中:"+jedis.smove("eleSet1", "eleSet3", "e2"));
        System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1"));
        System.out.println("eleSet3中的元素:"+jedis.smembers("eleSet3"));
        System.out.println("============集合運算=================");
        System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1"));
        System.out.println("eleSet2中的元素:"+jedis.smembers("eleSet2"));
        System.out.println("eleSet1和eleSet2的交集:"+jedis.sinter("eleSet1","eleSet2"));
        System.out.println("eleSet1和eleSet2的并集:"+jedis.sunion("eleSet1","eleSet2"));
        System.out.println("eleSet1和eleSet2的差集:"+jedis.sdiff("eleSet1","eleSet2"));//eleSet1中有,eleSet2中沒有
        jedis.sinterstore("eleSet4","eleSet1","eleSet2");//求交集并將交集保存到dstkey的集合
        System.out.println("eleSet4中的元素:"+jedis.smembers("eleSet4"));
    }
}


對hash操作的命令

package com.jihu.base;

import redis.clients.jedis.Jedis;

import java.util.HashMap;
import java.util.Map;

public class TestHash {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("192.168.56.130",6379);
        jedis.auth("123456");
        jedis.flushDB();
        Map<String,String> map = new HashMap<String,String>();
        map.put("key1","value1");
        map.put("key2","value2");
        map.put("key3","value3");
        map.put("key4","value4");
        //添加名稱為hash(key)的hash元素
        jedis.hmset("hash",map);
        //向名稱為hash的hash中添加key為key5,value為value5元素
        jedis.hset("hash", "key5", "value5");
        System.out.println("散列hash的所有鍵值對為:"+jedis.hgetAll("hash"));//return Map<String,String>
        System.out.println("散列hash的所有鍵為:"+jedis.hkeys("hash"));//return Set<String>
        System.out.println("散列hash的所有值為:"+jedis.hvals("hash"));//return List<String>
        System.out.println("將key6保存的值加上一個整數,如果key6不存在則添加key6:"+jedis.hincrBy("hash", "key6", 6));
        System.out.println("散列hash的所有鍵值對為:"+jedis.hgetAll("hash"));
        System.out.println("將key6保存的值加上一個整數,如果key6不存在則添加key6:"+jedis.hincrBy("hash", "key6", 3));
        System.out.println("散列hash的所有鍵值對為:"+jedis.hgetAll("hash"));
        System.out.println("洗掉一個或者多個鍵值對:"+jedis.hdel("hash", "key2"));
        System.out.println("散列hash的所有鍵值對為:"+jedis.hgetAll("hash"));
        System.out.println("散列hash中鍵值對的個數:"+jedis.hlen("hash"));
        System.out.println("判斷hash中是否存在key2:"+jedis.hexists("hash","key2"));
        System.out.println("判斷hash中是否存在key3:"+jedis.hexists("hash","key3"));
        System.out.println("獲取hash中的值:"+jedis.hmget("hash","key3"));
        System.out.println("獲取hash中的值:"+jedis.hmget("hash","key3","key4"));
    }
}

對Zset操作的命令


事務

package com.jihu;
import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("192.168.56.130",6379);
        jedis.auth("123456");
    jedis.flushDB();

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello","world");
        jsonObject.put("name","jihu");
        //開啟事務
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();
        //jedis.watch(result) //給result加樂觀鎖

        try {
            multi.set("user1",result);
            multi.set("user2",result);
            int i = 1/0; //代碼拋出例外,事務執行失敗

            multi.exec();  //執行事務
        } catch (Exception exception) {
            multi.discard();  //放棄事務
            exception.printStackTrace();
        }finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close(); //關閉連接
        }
    }
}

七、SpringBoot整合

SpringBoot 操作資料:spring-data jpa jdbc mongodb redis!

SpringData 也是和 SpringBoot 齊名的專案!

說明: 在 SpringBoot2.x 之后,原來使用的jedis 被替換為了 lettuce?

jedis : 采用的直連,多個執行緒操作的話,是不安全的,如果想要避免不安全的,使用 jedis pool 連接池! 更像 BIO 模式

lettuce : 采用netty,實體可以再多個執行緒中進行共享,不存在執行緒不安全的情況!可以減少執行緒資料了,更像 NIO 模式

原始碼分析

@Bean // 我們可以自己定義一個redisTemplate來替換這個默認的!
@ConditionalOnMissingBean(name = "redisTemplate") 

public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
// 默認的 RedisTemplate 沒有過多的設定,redis 物件都是需要序列化!
// 兩個泛型都是 Object, Object 的型別,我們后使用需要強制轉換 <String, Object>
    RedisTemplate<Object, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(redisConnectionFactory);
    return template;
}

@Bean
@ConditionalOnMissingBean // 由于 String 是redis中最常使用的型別,所以說單獨提出來了一個bean!
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
    StringRedisTemplate template = new StringRedisTemplate();
    template.setConnectionFactory(redisConnectionFactory);
    return template;
}

整合測驗

1.匯入依賴

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.配置連接

#springboot所有的配置類都有一個自動配置類 :RedisAutoConfiguration
# (RedisAutoConfiguration它在RedisAutoConfiguration/META-INF/spring.factories檔案中)
#自動配置類都會系結一個 properties組態檔 RedisProperties

#配置Redis
spring.redis.host=192.168.56.130
spring.redis.port=6379
spring.redis.password=123456

3.測驗

package com.jihu;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
class Redis02SpringbootApplicationTests {

	@Autowired
	private RedisTemplate redisTemplate;

	@Test
	void contextLoads() {

		//redisTemplate  操作不同的資料型別,api和我們的指令是一樣的
		//redisTemplate.opsForValue()  表示操作字串 類似String
		//redisTemplate.opsForList()  表示操作List ,類似list
		//等等
		redisTemplate.opsForList();

		//除了基本的操作,我們常用的方法都可以直接通過redisTemplate操作,比如事務和基本的CRUD

		//獲取redis的連接物件
		//RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
		//connection.flushDb();
		//connection.flushAll();

		redisTemplate.opsForValue().set("mykey","jihu");
		System.out.println(redisTemplate.opsForValue().get("mykey"));
	}
}

4.測驗結果

此時我們回到Redis查看資料時候,驚奇發現全是亂碼,可是程式中可以正常輸出,這時候就關系到存盤物件的序列化問題,在網路中傳輸的物件也是一樣需要序列化,否者就全是亂碼,

image-20220324111546417

RedisTemplate內部的序列化配置是這樣的

image-20220324112055460

默認的序列化器是采用JDK序列化器

image-20220324112123665

對呀物件的保存

image-20220324113730650

如果不序列化這樣會報錯

image-20220324113916994

這里序列化之后就好了

image-20220324113958570

1.自定義RedisTemplate序列化配置

我們來撰寫一個自己的 RedisTemplete

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    // 這是我給大家寫好的一個固定模板,大家在企業中,拿去就可以直接使用!
    // 自己定義了一個 RedisTemplate
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 我們為了自己開發方便,一般直接使用 <String, Object>
        RedisTemplate<String, Object> template = new RedisTemplate<String,
        Object>();
        template.setConnectionFactory(factory);

    // Json序列化配置
    Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
    Jackson2JsonRedisSerializer(Object.class);
    ObjectMapper om = new ObjectMapper();
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jackson2JsonRedisSerializer.setObjectMapper(om);
    // String 的序列化
    StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

    // key采用String的序列化方式
    template.setKeySerializer(stringRedisSerializer);
    // hash的key也采用String的序列化方式
    template.setHashKeySerializer(stringRedisSerializer);
    // value序列化方式采用jackson
    template.setValueSerializer(jackson2JsonRedisSerializer);
    // hash的value序列化方式采用jackson
    template.setHashValueSerializer(jackson2JsonRedisSerializer);
    template.afterPropertiesSet();

    return template;
}
}


再查看就不是亂碼了

image-20220324115456183

2.RedisUtil配置(CRUD操作string,map,list,set)

package com.jihu.config.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

//在我們真實的開發中,或者你們的公司,一般都可以看到一個公司自己封裝的RedisUtil
@Component
public final class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // =============================common============================
    /**
     * 指定快取失效時間
     * @param key  鍵
     * @param time 時間(秒)
     */
    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 && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
            }
        }
    }


    // ============================String=============================

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

    /**
     * 普通快取放入
     * @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   鍵
     * @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 delta 要增加幾(大于0)
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("遞增因子必須大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }


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


    // ================================Map=================================

    /**
     * HashGet
     * @param key  鍵 不能為null
     * @param item 項 不能為null
     */
    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 對應多個鍵值
     */
    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)
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }


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


    // ============================set=============================

    /**
     * 根據key獲取Set中的所有值
     * @param key 鍵
     */
    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;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * 獲取set快取的長度
     *
     * @param key 鍵
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } 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;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    // ===============================list=================================

    /**
     * 獲取list快取的內容
     *
     * @param key   鍵
     * @param start 開始
     * @param end   結束 0 到 -1代表所有值
     */
    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 鍵
     */
    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倒數第二個元素,依次類推
     */
    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 值
     */
    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  時間(秒)
     */
    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;
        }

    }

}

八、Redis.conf詳解

1.容量單位不區分大小寫,G和GB有區別

image-20220324131409206

2.可以使用 include 組合多個組態檔

image-20220324131928244

3.網路配置

image-20220324132647289

protected-mode no  #保護模式
port 6379  #埠設定

4.通用配置(GENERAL)

daemonize yes  #以守護行程的方式運行,默認是no ,我們需要自己開啟為yes

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  #資料庫的數量,默認是16個資料庫
always-show-logo no  #是否總是顯示log

5.快照(SNAPSHOTTING) rdb配置

持久化,在規定的時間內,執行了多少次操作,則會持久化到檔案 .rdb .aof

redis是記憶體資料庫,如果沒有持久化,那么資料斷電及失,

#在3600s內,如果至少有 1 key進行了修改,我們將進行持久化操作
 save 3600 1
 #在300s內,如果至少有 100 key進行了修改,我們將進行持久化操作
 save 300 100
 #在 60s 內,如果至少有 10000 key進行了修改,我們將進行持久化操作
 save 60 10000
 #我們之后學習持久化,會自己定義這個測驗
 
 stop-writes-on-bgsave-error yes  #持久化如果出錯,是否還需要繼續作業,

 rdbcompression yes  #是否壓縮rdb檔案,需要消耗一些 cpu資源
 
 rdbchecksum yes  #保存rdb檔案的視覺,進行錯誤的檢查效驗
 
 dir ./  #rdb檔案保存的目錄

REPLICATION 主從復制,我們后面講解主從復制的時候再進行詳解

6.SECURITY 安全

 1.可以再redis.conf組態檔里面修改密碼
 requirepass 123456 #可以在這里設定redis的密碼,默認是沒有密碼的
 
 2.可以用命令設定密碼
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.  #設定密碼后發現所有的命令都沒有權限了
127.0.0.1:6379> auth 123456   #使用密碼進行登錄
OK
127.0.0.1:6379> config get requirepass  #查看redis的密碼
1) "requirepass"
2) "123456"
127.0.0.1:6379> config set requirepass "1234567"   #用命令設定redis的密碼
 

7.CLIENTS 限制(客戶端連接相關)

maxclients 10000  #設定能連上redis的最大客戶端的數量

maxmemory <bytes> # redis 配置最大的記憶體容量

maxmemory-policy noeviction # 記憶體到達上限之后的處理策略
6種策略
    1、volatile-lru:只對設定了過期時間的key進行LRU(默認值)
    2、allkeys-lru : 洗掉lru演算法的key
    3、volatile-random:隨機洗掉即將過期key
    4、allkeys-random:隨機洗掉
    5、volatile-ttl : 洗掉即將過期的
    6、noeviction : 永不過期,回傳錯誤


8.APPEND ONLY 模式 aof配置

appendonly no # 默認是不開啟aof模式的,默認是使用rdb方式持久化的,在大部分所有的情況下,rdb完全夠用!
appendfilename "appendonly.aof" # 持久化的檔案的名字

# appendfsync always # 每次修改都會 sync,消耗性能
appendfsync everysec # 每秒執行一次 sync,可能會丟失這1s的資料!
# appendfsync no # 不執行 sync,這個時候作業系統自己同步資料,速度最快!

九、Redis持久化 ---RDB

面試和作業,持久化都是重點!

Redis 是記憶體資料庫,如果不將記憶體中的資料庫狀態保存到磁盤,那么一旦服務器行程退出,服務器中的資料庫狀態也會消失,所以 Redis 提供了持久化功能!

持久化:在指定時間間隔內將記憶體資料存入磁盤中,斷電也能恢復資料,使用快照檔案讀到記憶體中,

RDB:讀寫檔案

RDB(Redis DataBase)

什么是RDB

用在主從復制中,rdb就是備用的,在從機上面,

image-20220324141702115

在指定的時間間隔內將記憶體中的資料集快照寫入磁盤,也就是行話講的Snapshot快照,它恢復時是將快 照檔案直接讀到記憶體里,

Redis會單獨創建(fork)一個子行程來進行持久化,會先將資料寫入到一個臨時檔案中,待持久化程序 都結束了,再用這個臨時檔案替換上次持久化好的檔案,整個程序中,主行程是不進行任何IO操作的, 這就確保了極高的性能,如果需要進行大規模資料的恢復,且對于資料恢復的完整性不是非常敏感,那 RDB方式要比AOF方式更加的高效,RDB的缺點是最后一次持久化后的資料可能丟失,我們默認的就是 RDB,一般情況下不需要修改這個配置!

有時候在生產環境我們會將這個檔案進行備份!

rdb保存的檔案是dump.rdb 都是在我們的組態檔中快照中進行配置的!

image-20220324141756113

觸發機制

  1. save的規則滿足的情況下,會自動觸發rdb原則
  2. 執行flushall命令,也會觸發我們的rdb原則
  3. 退出redis,也會自動產生rdb檔案

備份就自動生成一個 dump.rdb

image-20220324142016256

1.save

使用 save 命令,會立刻對當前記憶體中的資料進行持久化 ,但是會阻塞,也就是不接受其他操作了;

由于 save 命令是同步命令,會占用Redis的主行程,若Redis資料非常多時,save命令執行速度會非常慢,阻塞所有客戶端的請求,

示意圖:

image-20220324142131032

2.flushall命令

flushall 命令也會觸發持久化 ;

觸發持久化規則
滿足配置條件中的觸發條件 ;

可以通過組態檔對 Redis 進行設定, 讓它在“ N 秒內資料集至少有 M 個改動”這一條件被滿足時, 自動進行資料集保存操作,

image-20220324142221719

image-20220324142228047

3.bgsave

bgsave 是異步進行,進行持久化的時候,redis 還可以將繼續回應客戶端請求 ;

示意圖:

image-20220324142257843

bgsave和save對比

命令 save bgsave
IO型別 同步 異步
阻塞? 是(阻塞發生在fock(),通常非常快)
復雜度 O(n) O(n)
優點 不會消耗額外的記憶體 不阻塞客戶端命令
缺點 阻塞客戶端命令 需要fock子行程,消耗記憶體

如果恢復rdb檔案!

1、只需要將rdb檔案放在我們redis啟動目錄就可以,redis啟動的時候會自動檢查dump.rdb 恢復其中的資料!
2、查看需要存在的位置

127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/bin" # 如果在這個目錄下存在 dump.rdb 檔案,啟動就會自動恢復其中的資料

優缺點

優點:

  1. 適合大規模的資料恢復
  2. 對資料的完整性要求不高

缺點:

  1. 需要一定的時間間隔進行操作,如果redis意外宕機了,這個最后一次修改的資料就沒有了,
  2. fork行程的時候,會占用一定的內容空間,

十、Redis持久化 ---AOF

AOF(Append Only File)(記錄檔案)

將我們的所有命令都記錄下來,history,恢復的時候就把這個檔案全部在執行一遍!

AOF 是什么

image-20220324190240629

以日志的形式來記錄每個寫操作,將Redis執行過的所有指令記錄下來(讀操作不記錄),只許追加檔案 但不可以改寫檔案,redis啟動之扯訓讀取該檔案重新構建資料,換言之,redis重啟的話就根據日志檔案 的內容將寫指令從前到后執行一次以完成資料的恢復作業

Aof保存的是 appendonly.aof 檔案

append

image-20220324190425627

默認是不開啟的,我們需要手動進行配置!我們只需要將 appendonly 改為yes就開啟了 aof!

重啟,redis 就可以生效了! 如果這個 aof 檔案有錯位,這時候 redis 是啟動不起來的嗎,我們需要修復這個aof檔案

開啟后會生成appendonly.aof檔案

image-20220324193341798

在里面寫入資料后

image-20220324193458390

看 appendonly.aof 里面會有 寫資料的記錄

image-20220324193451343

如果這個aof檔案有錯誤,這時候redis是啟動不起來的,需要我們修復這個aof檔案

redis給我們提供了一個工具 redis-check-aof --fix

aof組態檔出錯,連接時會出現這個問題

image-20220324194107333

用redis-check-aof --fix 進行恢復

image-20220324194359390

如果檔案正常,重啟就可以直接恢復了

image-20220324194427748

重寫規則說明

aof 默認就是檔案的無限追加,檔案會越來越大!

image-20220324195337310

如果 aof 檔案大于 64m,太大了! fork一個新的行程來將我們的檔案進行重寫!

優點和缺點!

appendonly no # 默認是不開啟aof模式的,默認是使用rdb方式持久化的,在大部分所有的情況下,
rdb完全夠用!
appendfilename "appendonly.aof" # 持久化的檔案的名字
# appendfsync always # 每次修改都會 sync,消耗性能
appendfsync everysec # 每秒執行一次 sync,可能會丟失這1s的資料!
# appendfsync no # 不執行 sync,這個時候作業系統自己同步資料,速度最快!
# rewrite 重寫,

優點: 1、每一次修改都同步,檔案的完整會更加好!

2、每秒同步一次,可能會丟失一秒的資料

3、從不同步,效率最高的!

缺點:

1、相對于資料檔案來說,aof遠遠大于 rdb,修復的速度也比 rdb慢!

2、Aof 運行效率也要比 rdb 慢,所以我們redis默認的配置就是rdb持久化!

擴展:

1、RDB 持久化方式能夠在指定的時間間隔內對你的資料進行快照存盤

2、AOF 持久化方式記錄每次對服務器寫的操作,當服務器重啟的時候會重新執行這些命令來恢復原始 的資料,AOF命令以Redis 協議追加保存每次寫的操作到檔案末尾,Redis還能對AOF檔案進行后臺重 寫,使得AOF檔案的體積不至于過大,

3、只做快取,如果你只希望你的資料在服務器運行的時候存在,你也可以不使用任何持久化

4、同時開啟兩種持久化方式

  • 在這種情況下,當redis重啟的時候會優先載入AOF檔案來恢復原始的資料,因為在通常情況下AOF 檔案保存的資料集要比RDB檔案保存的資料集要完整,
  • RDB 的資料不實時,同時使用兩者時服務器重啟也只會找AOF檔案,那要不要只使用AOF呢?作者 建議不要,因為RDB更適合用于備份資料庫(AOF在不斷變化不好備份),快速重啟,而且不會有 AOF可能潛在的Bug,留著作為一個萬一的手段,

5、性能建議

  • 因為RDB檔案只用作后備用途,建議只在Slave上持久化RDB檔案,而且只要15分鐘備份一次就夠 了,只保留 save 900 1 這條規則,
  • 如果Enable AOF ,好處是在最惡劣情況下也只會丟失不超過兩秒資料,啟動腳本較簡單只load自 己的AOF檔案就可以了,代價一是帶來了持續的IO,二是AOF rewrite 的最后將 rewrite 程序中產 生的新資料寫到新檔案造成的阻塞幾乎是不可避免的,只要硬碟許可,應該盡量減少AOF rewrite 的頻率,AOF重寫的基礎大小默認值64M太小了,可以設到5G以上,默認超過原大小100%大小重 寫可以改到適當的數值,
  • 如果不Enable AOF ,僅靠 Master-Slave Repllcation 實作高可用性也可以,能省掉一大筆IO,也 減少了rewrite時帶來的系統波動,代價是如果Master/Slave 同時倒掉,會丟失十幾分鐘的資料, 啟動腳本也要比較兩個 Master/Slave 中的 RDB檔案,載入較新的那個,微博就是這種架構,

十一、RDB和AOP選擇

RDB 和 AOF 對比

RDB AOF
啟動優先級
體積
恢復速度
資料安全性 丟資料 根據策略決定

如何選擇使用哪種持久化方式?

一般來說, 如果想達到足以媲美 PostgreSQL 的資料安全性, 你應該同時使用兩種持久化功能,

如果你非常關心你的資料, 但仍然可以承受數分鐘以內的資料丟失, 那么你可以只使用 RDB 持久化,

有很多用戶都只使用 AOF 持久化, 但并不推薦這種方式: 因為定時生成 RDB 快照(snapshot)非常便于進行資料庫備份, 并且 RDB 恢復資料集的速度也要比 AOF 恢復的速度要快,

image-20220324200316734

十二:Redis訂閱發布

Redis 發布訂閱(pub/sub)是一種訊息通信模式:發送者(pub)發送訊息,訂閱者(sub)接收訊息,如:微信、 微博、關注系統!

Redis 客戶端可以訂閱任意數量的頻道,

訂閱/發布訊息圖:

第一個:訊息發送者, 第二個:頻道 第三個:訊息訂閱者!

image-20220324200807753

下圖展示了頻道 channel1 , 以及訂閱這個頻道的三個客戶端 —— client2 、 client5 和 client1 之間的 關系:

image-20220324200851719

當有新訊息通過 PUBLISH 命令發送給頻道 channel1 時, 這個訊息就會被發送給訂閱它的三個客戶端:

image-20220324200915239

命令

這些命令被廣泛用于構建即時通信應用,比如網路聊天室(chatroom)和實時廣播、實時提醒等,

image-20220324200954082

測驗

訂閱端:

127.0.0.1:6379> SUBSCRIBE jihu2  #訂閱一個頻道jihu2
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "jihu2"
3) (integer) 1
#等待讀取推送的資訊
1) "message"
2) "jihu2"
3) "hello beauty women"  #自動接收jihu2發布的資訊
1) "message"
2) "jihu2"
3) "hello redis"

發送端:

[root@localhost bin]# redis-cli -p 6379 #發布者發布訊息到頻道
127.0.0.1:6379> PUBLISH jihu2 "hello beauty women"  #發布者發布訊息到頻道
(integer) 1
127.0.0.1:6379> PUBLISH jihu2 "hello redis"
(integer) 1

原理

Redis是使用C實作的,通過分析 Redis 原始碼里的 pubsub.c 檔案,了解發布和訂閱機制的底層實作,籍此加深對 Redis 的理解,

Redis 通過 PUBLISH 、SUBSCRIBE 和 PSUBSCRIBE 等命令實作發布和訂閱功能,

微信:

通過 SUBSCRIBE 命令訂閱某頻道后,redis-server 里維護了一個字典,字典的鍵就是一個個 頻道!, 而字典的值則是一個鏈表,鏈表中保存了所有訂閱這個 channel 的客戶端,SUBSCRIBE 命令的關鍵, 就是將客戶端添加到給定 channel 的訂閱鏈表中,

image-20220324202031676

通過 PUBLISH 命令向訂閱者發送訊息,redis-server 會使用給定的頻道作為鍵,在它所維護的 channel 字典中查找記錄了訂閱這個頻道的所有客戶端的鏈表,遍歷這個鏈表,將訊息發布給所有訂閱者,

缺點

  1. 如果一個客戶端訂閱了頻道,但自己讀取訊息的速度卻不夠快的話,那么不斷積壓的訊息會使redis輸出緩沖區的體積變得越來越大,這可能使得redis本身的速度變慢,甚至直接崩潰,
  2. 這和資料傳輸可靠性有關,如果在訂閱方斷線,那么他將會丟失所有在短線期間發布者發布的訊息,

使用場景

1、實時訊息系統!

2、事實聊天!(頻道當做聊天室,將資訊回顯給所有人即可!)

3、訂閱,關注系統都是可以的!

稍微復雜的場景,我們就會使用訊息中間件MQ處理,

收藏 關注 評論

轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/449123.html

標籤:NoSQL

上一篇:模擬鍵盤上的向上箭頭鍵(或其他擴展鍵)

下一篇:Redis最新超詳細版教程通俗易懂

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • GPU虛擬機創建時間深度優化

    **?桔妹導讀:**GPU虛擬機實體創建速度慢是公有云面臨的普遍問題,由于通常情況下創建虛擬機屬于低頻操作而未引起業界的重視,實際生產中還是存在對GPU實體創建時間有苛刻要求的業務場景。本文將介紹滴滴云在解決該問題時的思路、方法、并展示最終的優化成果。 從公有云服務商那里購買過虛擬主機的資深用戶,一 ......

    uj5u.com 2020-09-10 06:09:13 more
  • 可編程網卡芯片在滴滴云網路的應用實踐

    **?桔妹導讀:**隨著云規模不斷擴大以及業務層面對延遲、帶寬的要求越來越高,采用DPDK 加速網路報文處理的方式在橫向縱向擴展都出現了局限性。可編程芯片成為業界熱點。本文主要講述了可編程網卡芯片在滴滴云網路中的應用實踐,遇到的問題、帶來的收益以及開源社區貢獻。 #1. 資料中心面臨的問題 隨著滴滴 ......

    uj5u.com 2020-09-10 06:10:21 more
  • 滴滴資料通道服務演進之路

    **?桔妹導讀:**滴滴資料通道引擎承載著全公司的資料同步,為下游實時和離線場景提供了必不可少的源資料。隨著任務量的不斷增加,資料通道的整體架構也隨之發生改變。本文介紹了滴滴資料通道的發展歷程,遇到的問題以及今后的規劃。 #1. 背景 資料,對于任何一家互聯網公司來說都是非常重要的資產,公司的大資料 ......

    uj5u.com 2020-09-10 06:11:05 more
  • 滴滴AI Labs斬獲國際機器翻譯大賽中譯英方向世界第三

    **桔妹導讀:**深耕人工智能領域,致力于探索AI讓出行更美好的滴滴AI Labs再次斬獲國際大獎,這次獲獎的專案是什么呢?一起來看看詳細報道吧! 近日,由國際計算語言學協會ACL(The Association for Computational Linguistics)舉辦的世界最具影響力的機器 ......

    uj5u.com 2020-09-10 06:11:29 more
  • MPP (Massively Parallel Processing)大規模并行處理

    1、什么是mpp? MPP (Massively Parallel Processing),即大規模并行處理,在資料庫非共享集群中,每個節點都有獨立的磁盤存盤系統和記憶體系統,業務資料根據資料庫模型和應用特點劃分到各個節點上,每臺資料節點通過專用網路或者商業通用網路互相連接,彼此協同計算,作為整體提供 ......

    uj5u.com 2020-09-10 06:11:41 more
  • 滴滴資料倉庫指標體系建設實踐

    **桔妹導讀:**指標體系是什么?如何使用OSM模型和AARRR模型搭建指標體系?如何統一流程、規范化、工具化管理指標體系?本文會對建設的方法論結合滴滴資料指標體系建設實踐進行解答分析。 #1. 什么是指標體系 ##1.1 指標體系定義 指標體系是將零散單點的具有相互聯系的指標,系統化的組織起來,通 ......

    uj5u.com 2020-09-10 06:12:52 more
  • 單表千萬行資料庫 LIKE 搜索優化手記

    我們經常在資料庫中使用 LIKE 運算子來完成對資料的模糊搜索,LIKE 運算子用于在 WHERE 子句中搜索列中的指定模式。 如果需要查找客戶表中所有姓氏是“張”的資料,可以使用下面的 SQL 陳述句: SELECT * FROM Customer WHERE Name LIKE '張%' 如果需要 ......

    uj5u.com 2020-09-10 06:13:25 more
  • 滴滴Ceph分布式存盤系統優化之鎖優化

    **桔妹導讀:**Ceph是國際知名的開源分布式存盤系統,在工業界和學術界都有著重要的影響。Ceph的架構和演算法設計發表在國際系統領域頂級會議OSDI、SOSP、SC等上。Ceph社區得到Red Hat、SUSE、Intel等大公司的大力支持。Ceph是國際云計算領域應用最廣泛的開源分布式存盤系統, ......

    uj5u.com 2020-09-10 06:14:51 more
  • es~通過ElasticsearchTemplate進行聚合~嵌套聚合

    之前寫過《es~通過ElasticsearchTemplate進行聚合操作》的文章,這一次主要寫一個嵌套的聚合,例如先對sex集合,再對desc聚合,最后再對age求和,共三層嵌套。 Aggregations的部分特性類似于SQL語言中的group by,avg,sum等函式,Aggregation ......

    uj5u.com 2020-09-10 06:14:59 more
  • 爬蟲日志監控 -- Elastc Stack(ELK)部署

    傻瓜式部署,只需替換IP與用戶 導讀: 現ELK四大組件分別為:Elasticsearch(核心)、logstash(處理)、filebeat(采集)、kibana(可視化) 下載均在https://www.elastic.co/cn/downloads/下tar包,各組件版本最好一致,配合fdm會 ......

    uj5u.com 2020-09-10 06:15:05 more
最新发布
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:33:24 more
  • MySQL中binlog備份腳本分享

    關于MySQL的二進制日志(binlog),我們都知道二進制日志(binlog)非常重要,尤其當你需要point to point災難恢復的時侯,所以我們要對其進行備份。關于二進制日志(binlog)的備份,可以基于flush logs方式先切換binlog,然后拷貝&壓縮到到遠程服務器或本地服務器 ......

    uj5u.com 2023-04-20 08:28:06 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:27:27 more
  • 快取與資料庫雙寫一致性幾種策略分析

    本文將對幾種快取與資料庫保證資料一致性的使用方式進行分析。為保證高并發性能,以下分析場景不考慮執行的原子性及加鎖等強一致性要求的場景,僅追求最終一致性。 ......

    uj5u.com 2023-04-20 08:26:48 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:26:35 more
  • 云時代,MySQL到ClickHouse資料同步產品對比推薦

    ClickHouse 在執行分析查詢時的速度優勢很好的彌補了MySQL的不足,但是對于很多開發者和DBA來說,如何將MySQL穩定、高效、簡單的同步到 ClickHouse 卻很困難。本文對比了 NineData、MaterializeMySQL(ClickHouse自帶)、Bifrost 三款產品... ......

    uj5u.com 2023-04-20 08:26:29 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:25:13 more
  • Redis 報”OutOfDirectMemoryError“(堆外記憶體溢位)

    Redis 報錯“OutOfDirectMemoryError(堆外記憶體溢位) ”問題如下: 一、報錯資訊: 使用 Redis 的業務介面 ,產生 OutOfDirectMemoryError(堆外記憶體溢位),如圖: 格式化后的報錯資訊: { "timestamp": "2023-04-17 22: ......

    uj5u.com 2023-04-20 08:24:54 more
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:24:03 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:23:11 more