Redis最新超詳細版教程通俗易懂 一、Nosql概述 為什麼使用Nosql 1、單機Mysql時代 90年代,一個網站的訪問量一般不會太大,單個資料庫完全夠用。隨著用戶增多,網站出現以下問題 數據量增加到一定程度,單機資料庫就放不下了 數據的索引(B+ Tree),一個機器記憶體也存放不下 訪問量變 ...
Redis最新超詳細版教程通俗易懂
一、Nosql概述
為什麼使用Nosql
1、單機Mysql時代
90年代,一個網站的訪問量一般不會太大,單個資料庫完全夠用。隨著用戶增多,網站出現以下問題
-
數據量增加到一定程度,單機資料庫就放不下了
-
數據的索引(B+ Tree),一個機器記憶體也存放不下
-
訪問量變大後(讀寫混合),一臺伺服器承受不住。
2、Memcached(緩存) + Mysql + 垂直拆分(讀寫分離)
網站80%的情況都是在讀,每次都要去查詢資料庫的話就十分的麻煩!所以說我們希望減輕資料庫的壓力,我們可以使用緩存來保證效率!
優化過程經歷了以下幾個過程:
- 優化資料庫的數據結構和索引(難度大)
- 文件緩存,通過IO流獲取比每次都訪問資料庫效率略高,但是流量爆炸式增長時候,IO流也承受不了
- MemCache,當時最熱門的技術,通過在資料庫和資料庫訪問層之間加上一層緩存,第一次訪問時查詢資料庫,將結果保存到緩存,後續的查詢先檢查緩存,若有直接拿去使用,效率顯著提升。
3、分庫分表 + 水平拆分 + Mysql集群
4、如今最近的年代
如今信息量井噴式增長,各種各樣的數據出現(用戶定位數據,圖片數據等),大數據的背景下關係型資料庫(RDBMS)無法滿足大量數據要求。Nosql資料庫就能輕鬆解決這些問題。
目前一個基本的互聯網項目
為什麼要用NoSQL ?
用戶的個人信息,社交網路,地理位置。用戶自己產生的數據,用戶日誌等等爆髮式增長!
這時候我們就需要使用NoSQL資料庫的,Nosql可以很好的處理以上的情況!
什麼是NoSQL
NoSQL
NoSQL = Not Only SQL (不僅僅是SQL)
關係型資料庫:列+行,同一個表下數據的結構是一樣的。
非關係型資料庫:數據存儲沒有固定的格式,並且可以進行橫向擴展。
NoSQL泛指非關係型資料庫,隨著web2.0互聯網的誕生,傳統的關係型資料庫很難對付web2.0時代!尤其是超大規模的高併發的社區,暴露出來很多難以剋服的問題,NoSQL在當今大數據環境下發展的十分迅速,Redis是發展最快的。
很多的數據類型用戶的個人信息,社交網路,地理位置。這些數據類型的存儲不需要一個固定的格式! 不需要多月的操作就可以橫向擴展的! Map<String,Object> 使用鍵值對來控制。
NoSQL特點
- 方便擴展(數據之間沒有關係,很好擴展!)
- 大數據量高性能(Redis一秒可以寫8萬次,讀11萬次,NoSQL的緩存記錄級,是一種細粒度的緩存,性能會比較高!)
- 數據類型是多樣型的!(不需要事先設計資料庫,隨取隨用)
- 傳統的 RDBMS 和 NoSQL
傳統的 RDBMS(關係型資料庫)
- 結構化組織
- SQL
- 數據和關係都存在單獨的表中 row col
- 操作,數據定義語言
- 嚴格的一致性
- 基礎的事務
- ...
Nosql
- 不僅僅是數據
- 沒有固定的查詢語言
- 鍵值對存儲,列存儲,文檔存儲,圖形資料庫(社交關係)
- 最終一致性
- CAP定理和BASE
- 高性能,高可用,高擴展
- ...
瞭解:3V + 3高
大數據時代的3V :主要是描述問題的
- 海量Velume
- 多樣Variety
- 實時Velocity
大數據時代的3高 : 主要是對程式的要求
- 高併發
- 高可擴
- 高性能
真正在公司中的實踐:NoSQL + RDBMS 一起使用才是最強的。
啊里巴巴演進分析
推薦閱讀:阿裡雲的這群瘋子:https://yq.aliyun.com/articles/653511
如果你未來相當一個架構師:沒有什麼是加一層解決不了的!
# 商品信息
- 一般存放在關係型資料庫:Mysql,淘寶內部使用的Mysql都是經過內部改動的不是大家用的mysql。
# 商品描述、評論(文字居多)
- 文檔型資料庫:MongoDB
# 圖片
- 分散式文件系統 FastDFS
- 淘寶:TFS
- Google: GFS
- Hadoop: HDFS
- 阿裡雲: oss
# 商品關鍵字 用於搜索
- 搜索引擎:solr,elasticsearch
- 阿裡:Isearch 多隆
---所有苦逼的人都有一段苦逼的歲月~但是你只要像SB一樣的去堅持,終將牛逼!
# 商品熱門的波段信息
- 記憶體資料庫:Redis,Tair、Memcache
# 商品交易,外部支付介面
- 第三方應用
大型互聯網應用問題:
- 數據類型太多了!
- 數據源繁多,經常重構!
- 數據要改造,大面積改造
解決問題:
NoSQL的四大分類
KV鍵值對:
- 新浪:Redis
- 美團: Redis+Tair
- 阿裡、百度:Redis + memecache
文檔行資料庫(bson格式和json一樣):
- MongoDB(一般必須要掌握)
- 基於分散式文件存儲的資料庫。C++編寫,用於處理大量文檔。
- MongoDB是RDBMS和NoSQL的中間產品。MongoDB是非關係型資料庫中功能最豐富的,NoSQL中最像關係型資料庫的資料庫。
- ConthDB
列存儲資料庫
- HBase
- 分散式文件系統
圖關係資料庫
- Neo4j、InfoGrid
四者對比
二、Redis入門
概述
Redis是什麼
Redis(Remote Dictionary Server ),即遠程字典服務。
是一個開源的使用ANSI C語言編寫、支持網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。
與memcached一樣,為了保證效率,數據都是緩存在記憶體中。區別的是redis會周期性的把更新的數據寫入磁碟或者把修改操作寫入追加的記錄文件,並且在此基礎上實現了master-slave(主從)同步。
Redis能該乾什麼?
- 記憶體存儲、持久化,記憶體是斷電即失的,所以需要持久化(RDB、AOF)
- 高效率、用於高速緩衝
- 發佈訂閱系統
- 地圖信息分析
- 計時器、計數器(eg:瀏覽量)
- ................
特性
-
多樣的數據類型
-
持久化
-
集群
-
事務
….....
下載與安裝
中文網: http://www.redis.cn/
下載地址:通過官網下載即可
註意:window在Github上下載
Redis推薦都是在Linux伺服器上搭建的,我們都是基於Linux學習!
Windows安裝
下載地址:https://github.com/MSOpenTech/redis/releases
然後選擇你喜歡的版本zip或msi下載,這裡建議下載 3.0.504 版本,因為 3.2.100 不是穩定版本
1.解壓安裝包
2.雙擊redis-server.exe啟動redis伺服器,雙擊redis-cli.exe打開redis客戶端(用來執行命令,訪問伺服器的)
3.啟動redis-cli.exe測試
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
3.進入解壓後的文件,我們可以看到redis的配置文件
4.基本的環境安裝
yum install gcc-c++
gcc -v #查看安裝的版本
make #執行make
5.redis的預設安裝路徑 :usr/local/bin
6.將redis配置文件,複製到我們當前目錄下 (當前目錄為/usr/local/bin/)
7.redis預設不是後臺啟動的,修改配置文件
8.啟動redis服務
9.使用redis-cli 進行連接測試
10.查看redis的進程是否開啟
11.關閉redis服務 shutdown
12.在此查看進程是否存在
13.後面我們會使用單擊多Reids啟動集群
測試性能
redis-benchmark:Redis官方提供的性能測試工具,參數選項如下:
簡單測試:
#測試: 100個併發連接,100000請求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
測試結果:
基礎的知識
redis預設有16個資料庫
預設使用的是第0個
可以使用select進行切換資料庫
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)
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億個元素)。
首先我們列表,可以經過規則定義將其變為隊列、棧、雙端隊列等
正如圖Redis中List是可以進行雙端操作的,所以命令也就分為了LXXX和RLLL兩類,有時候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基數統計
什麼是基數?
簡介
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
進行解鎖。
如果修改失敗,獲取最新的值就好
註意:每次提交執行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());
}
}
輸出結果:
常用的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!