- redis介紹
- 什麼是NoSQL
NoSQL,泛指非關係型的數據庫,NoSQL即Not-Only SQL,它可以作爲關係型數據庫的良好補充。隨着互聯網web2.0網站的興起,非關係型的數據庫現在成了一個極其熱門的新領域,非關係數據庫產品的發展非常迅速。而傳統的關係數據庫在應付web2.0網站,特別是超大規模和高併發類型的web2.0純動態網站已經顯得力不從心,暴露了很多難以克服的問題,例如:
1、High performance - 對數據庫高併發讀寫的需求
web2.0網站要根據用戶個性化信息來實時生成動態頁面和提供動態信息,所以基本上無法使用動態頁面靜態化技術,因此數據庫併發負載非常高,往往要達到每秒上萬次讀寫請求。關係數據庫應付上萬次SQL查詢還勉強頂得住,但是應付上萬次SQL寫數據請求,硬盤IO就已經無法承受了。其實對於普通的BBS網站,往往也存在對高併發寫請求的需求,例如網站的實時統計在線用戶狀態,記錄熱門帖子的點擊次數,投票計數等,因此這是一個相當普遍的需求。
2、Huge Storage - 對海量數據的高效率存儲和訪問的需求
類似Facebook,twitter,Friendfeed這樣的網站,每天用戶產生海量的用戶動態,以Friendfeed爲例,一個月就達到了2.5億條用戶動態,對於關係數據庫來說,在一張2.5億條記錄的表裏面進行SQL查詢,效率是極其低下乃至不可忍受的。再例如大型web網站的用戶登錄系統,例如騰訊,盛大,動輒數以億計的帳號,關係數據庫也很難應付。
3、High Scalability && High Availability- 對數據庫的高可擴展性和高可用性的需求
在基於web的架構當中,數據庫是最難進行橫向擴展的,當一個應用系統的用戶量和訪問量與日俱增的時候,數據庫卻沒有辦法像web server和app server那樣簡單的通過添加更多的硬件和服務節點來擴展性能和負載能力。對於很多需要提供24小時不間斷服務的網站來說,對數據庫系統進行升級和擴展是非常痛苦的事情,往往需要停機維護和數據遷移。
NoSQL數據庫的產生就是爲了解決大規模數據集合多重數據種類帶來的挑戰,尤其是大數據應用難題。
一些主流的NOSQL產品:
NoSQL數據庫的四大分類如下:
- 鍵值(Key-Value)存儲數據庫
相關產品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
典型應用: 內容緩存,主要用於處理大量數據的高訪問負載。
數據模型: 一系列鍵值對
優勢: 快速查詢
劣勢: 存儲的數據缺少結構化
- 列存儲數據庫
相關產品:Cassandra, HBase, Riak
典型應用:分佈式的文件系統
數據模型:以列簇式存儲,將同一列數據存在一起
優勢:查找速度快,可擴展性強,更容易進行分佈式擴展
劣勢:功能相對侷限
- 文檔型數據庫
相關產品:CouchDB、MongoDB
典型應用:Web應用(與Key-Value類似,Value是結構化的)
數據模型: 一系列鍵值對
優勢:數據結構要求不嚴格
劣勢:查詢性能不高,而且缺乏統一的查詢語法
- 圖形(Graph)數據庫
相關數據庫:Neo4J、InfoGrid、Infinite Graph
典型應用:社交網絡
數據模型:圖結構
優勢:利用圖結構相關算法。
劣勢:需要對整個圖做計算才能得出結果,不容易做分佈式的集羣方案。
-
- redis歷史發展
2008年,意大利的一家創業公司Merzia推出了一款基於MySQL的網站實時統計系統LLOOGG,然而沒過多久該公司的創始人 Salvatore Sanfilippo便 對MySQL的性能感到失望,於是他決定親自爲LLOOGG量身定做一個數據庫,並於2009年開發完成,這個數據庫就是Redis。 不過Salvatore Sanfilippo並不滿足只將Redis用於LLOOGG這一款產品,而是希望更多的人使用它,於是在同一年Salvatore Sanfilippo將Redis開源發佈,並開始和Redis的另一名主要的代碼貢獻者Pieter Noordhuis一起繼續着Redis的開發,直到今天。
Salvatore Sanfilippo自己也沒有想到,短短的幾年時間,Redis就擁有了龐大的用戶羣體。Hacker News在2012年發佈了一份數據庫的使用情況調查,結果顯示有近12%的公司在使用Redis。國內如新浪微博、街旁網、知乎網,國外如GitHub、Stack Overflow、Flickr等都是Redis的用戶。
VMware公司從2010年開始贊助Redis的開發, Salvatore Sanfilippo和Pieter Noordhuis也分別在3月和5月加入VMware,全職開發Redis。
-
- 什麼是redis
Redis是用C語言開發的一個開源的高性能鍵值對(key-value)數據庫。它通過提供多種鍵值數據類型來適應不同場景下的存儲需求,目前爲止Redis支持的鍵值數據類型如下:
字符串類型 String json
散列類型 Map
列表類型 List
集合類型 set
有序集合類型。 sortSet
-
- redis的應用場景
緩存(數據查詢、短連接、新聞內容、商品內容等等)。(最多使用)
分佈式集羣架構中的session分離。
聊天室的在線好友列表。
任務隊列。(秒殺、搶購、12306等等)
應用排行榜。
網站訪問統計。
數據過期處理(可以精確到毫秒)
-
- redis和memcached相比獨特之處:
1.redis可以用來做存儲(storge), 而memccached是用來做緩存(cache)
這個特點主要因爲其有”持久化”的功能.
2. 存儲的數據有”結構”,對於memcached來說,存儲的數據,只有1種類型--”字符串”,而 redis則可以存儲字符串,鏈表,哈希結構,集合,有序集合.
Redis的優勢:
和其他NoSQL產品相比,Redis的易用性極高,因此對於那些有類似產品使用經驗的 開發者來說,一兩天,甚至是幾個小時之後就可以利用Redis來搭建自己的平臺了。
在解決了很多通用性問題的同時,也爲一些個性化問題提供了相關的解決方案,如 索 引引擎、統計排名、消息隊列服務等。
1:數據結構豐富
2:持久化
3:支持簡單事務
4:主從配置方便
- jedis
- jedis介紹
Redis不僅是使用命令來操作,現在基本上主流的語言都有客戶端支持,比如java、C、C#、C++、php、Node.js、Go等。
在官方網站裏列一些Java的客戶端,有Jedis、Redisson、Jredis、JDBC-Redis、等其中官方推薦使用Jedis和Redisson。 在企業中用的最多的就是Jedis,下面我們就重點學習下Jedis。
Jedis同樣也是託管在github上,地址:https://github.com/xetorthio/jedis
-
-
- jedis與spring整合
-
配置spring配置文件spring-redis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
<!-- 連接池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大連接數 -->
<property name="maxTotal" value="30" />
<!-- 最大空閒連接數 -->
<property name="maxIdle" value="10" />
<!-- 每次釋放連接的最大數目 -->
<property name="numTestsPerEvictionRun" value="1024" />
<!-- 釋放連接的掃描間隔(毫秒) -->
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<!-- 連接最小空閒時間 -->
<property name="minEvictableIdleTimeMillis" value="1800000" />
<!-- 連接空閒多久後釋放, 當空閒時間>該值 且 空閒連接>最大空閒連接數 時直接釋放 -->
<property name="softMinEvictableIdleTimeMillis" value="10000" />
<!-- 獲取連接時的最大等待毫秒數,小於零:阻塞不確定的時間,默認-1 -->
<property name="maxWaitMillis" value="1500" />
<!-- 在獲取連接的時候檢查有效性, 默認false -->
<property name="testOnBorrow" value="true" />
<!-- 在空閒時檢查有效性, 默認false -->
<property name="testWhileIdle" value="true" />
<!-- 連接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 默認true -->
<property name="blockWhenExhausted" value="false" />
</bean>
<!-- redis單機 通過連接池 -->
<bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="close">
<constructor-arg name="poolConfig" ref="jedisPoolConfig"/>
<constructor-arg name="host" value="127.0.0.1"/>
<constructor-arg name="port" value="6379"/>
</bean>
</beans>
- 數據類型--string
- redis string介紹
redis中沒有使用C語言的字符串表示,而是自定義一個數據結構叫SDS(simple dynamic string)即簡單動態字符串。
c語言對字符串的存儲是使用字符數組,遇到'\0'字符則認爲字符串結束,redis的字符串可以存儲任何類型的數據,因爲任何類型數據都可以表示成二進制,sds結構中的char buf[]就是存儲了二進制數據。
-
- 命令
1. SET/GET/APPEND/STRLEN:
/> redis-cli #執行Redis客戶端工具。
redis 127.0.0.1:6379> exists mykey #判斷該鍵是否存在,存在返回1,否則返回0。
(integer) 0
redis 127.0.0.1:6379> append mykey "hello" #該鍵並不存在,因此append命令返回當前Value的長度。
(integer) 5
redis 127.0.0.1:6379> append mykey " world" #該鍵已經存在,因此返回追加後Value的長度。
(integer) 11
redis 127.0.0.1:6379> get mykey #通過get命令獲取該鍵,以判斷append的結果。
"hello world"
redis 127.0.0.1:6379> set mykey "this is a test" #通過set命令爲鍵設置新值,並覆蓋原有值。
OK
redis 127.0.0.1:6379> get mykey
"this is a test"
redis 127.0.0.1:6379> strlen mykey #獲取指定Key的字符長度,等效於C庫中strlen函數。
(integer) 14
2. INCR/DECR/INCRBY/DECRBY:
redis 127.0.0.1:6379> set mykey 20 #設置Key的值爲20
OK
redis 127.0.0.1:6379> incr mykey #該Key的值遞增1
(integer) 21
redis 127.0.0.1:6379> decr mykey #該Key的值遞減1
(integer) 20
redis 127.0.0.1:6379> del mykey #刪除已有鍵。
(integer) 1
redis 127.0.0.1:6379> decr mykey #對空值執行遞減操作,其原值被設定爲0,遞減後的值爲-1
(integer) -1
redis 127.0.0.1:6379> del mykey
(integer) 1
redis 127.0.0.1:6379> incr mykey #對空值執行遞增操作,其原值被設定爲0,遞增後的值爲1
(integer) 1
redis 127.0.0.1:6379> set mykey hello #將該鍵的Value設置爲不能轉換爲整型的普通字符串。
OK
redis 127.0.0.1:6379> incr mykey #在該鍵上再次執行遞增操作時,Redis將報告錯誤信息。
(error) ERR value is not an integer or out of range
redis 127.0.0.1:6379> set mykey 10
OK
redis 127.0.0.1:6379> decrby mykey 5
(integer) 5
redis 127.0.0.1:6379> incrby mykey 10
(integer) 15
set key value [ex 秒數] / [px 毫秒數]
如: set a 1 ex 10 , 10秒有效
Set a 1 px 9000 , 9秒有效
注: 如果ex,px同時寫,以後面的有效期爲準
如 set a 1 ex 100 px 9000, 實際有效期是9000毫秒
3. GETSET:
redis 127.0.0.1:6379> incr mycounter #將計數器的值原子性的遞增1
(integer) 1
#在獲取計數器原有值的同時,並將其設置爲新值,這兩個操作原子性的同時完成。
redis 127.0.0.1:6379> getset mycounter 0
"1"
redis 127.0.0.1:6379> get mycounter #查看設置後的結果。
"0"
4. SETEX:
redis 127.0.0.1:6379> setex mykey 10 "hello" #設置指定Key的過期時間爲10秒。
OK
#通過ttl命令查看一下指定Key的剩餘存活時間(秒數),0表示已經過期,-1表示永不過期。
redis 127.0.0.1:6379> ttl mykey
(integer) 4
redis 127.0.0.1:6379> get mykey #在該鍵的存活期內我們仍然可以獲取到它的Value。
"hello"
redis 127.0.0.1:6379> ttl mykey #該ttl命令的返回值顯示,該Key已經過期。
(integer) 0
redis 127.0.0.1:6379> get mykey #獲取已過期的Key將返回nil。
(nil)
5. SETNX:
redis 127.0.0.1:6379> del mykey #刪除該鍵,以便於下面的測試驗證。
(integer) 1
redis 127.0.0.1:6379> setnx mykey "hello" #該鍵並不存在,因此該命令執行成功。
(integer) 1
redis 127.0.0.1:6379> setnx mykey "world" #該鍵已經存在,因此本次設置沒有產生任何效果。
(integer) 0
redis 127.0.0.1:6379> get mykey #從結果可以看出,返回的值仍爲第一次設置的值。
"hello"
6. SETRANGE/GETRANGE:
redis 127.0.0.1:6379> set mykey "hello world" #設定初始值。
OK
redis 127.0.0.1:6379> setrange mykey 6 dd #從第六個字節開始替換2個字節(dd只有2個 字節)
(integer) 11
redis 127.0.0.1:6379> get mykey #查看替換後的值。
"hello ddrld"
redis 127.0.0.1:6379> setrange mykey 20 dd #offset已經超過該Key原有值的長度了, 該命令將會在末尾補0。
(integer) 22
redis 127.0.0.1:6379> get mykey #查看補0後替換的結果。
"hello ddrld\x00\x00\x00\x00\x00\x00\x00\x00\x00dd"
redis 127.0.0.1:6379> del mykey #刪除該Key。
(integer) 1
redis 127.0.0.1:6379> setrange mykey 2 dd #替換空值。
(integer) 4
redis 127.0.0.1:6379> get mykey #查看替換空值後的結果。
"\x00\x00dd"
redis 127.0.0.1:6379> set mykey "0123456789" #設置新值。
OK
redis 127.0.0.1:6379> getrange mykey 1 2 #截取該鍵的Value,從第一個字節開始,到第 二個字節結束。
"12"
redis 127.0.0.1:6379> getrange mykey 1 20 #20已經超過Value的總長度,因此將截取第 一個字節後面的所有字節。
"123456789"
-
- 應用場景
1億個用戶, 每個用戶 登陸/做任意操作 ,記爲 今天活躍,否則記爲不活躍
每週評出: 有獎活躍用戶: 連續7天活動
每月評,等等...
思路:
Userid dt active
1 2013-07-27 1
1 2013-0726 1
如果是放在表中, 1:表急劇增大,2:要用group ,sum運算,計算較慢
1: 記錄用戶登陸:
每天按日期生成一個位圖, 用戶登陸後,把user_id位上的bit值置爲1
2: 把1周的位圖 and 計算,
位上爲1的,即是連續登陸的用戶
- 數據類型--hash
- 使用string的問題
假設有User對象以JSON序列化的形式存儲到Redis中,User對象有id,username、password、age、name等屬性,存儲的過程如下:
保存、更新:
User對象 à json(string) à redis
如果在業務上只是更新age屬性,其他的屬性並不做更新我應該怎麼做呢? 如果仍然採用上邊的方法在傳輸、處理時會造成資源浪費,下邊講的hash可以很好的解決這個問題。
-
- redis hash介紹
hash叫散列類型,它提供了字段和字段值的映射。字段值只能是字符串類型,不支持散列類型、集合類型等其它類型。如下:
-
- 命令
- 賦值
- 命令
HSET key field value 一次只能設置一個字段值
127.0.0.1:6379> hset user username zhangsan
(integer) 1
-----------------------------
HMSET key field value [field value ...] 一次可以設置多個字段值
127.0.0.1:6379> hmset user username lisi age 20
OK
-
-
- 取值
-
HGET key field 一次只能獲取一個字段值
127.0.0.1:6379> hget user username
"zhangsan“
----------------------------
HMGET key field [field ...] 一次可以獲取多個字段值
127.0.0.1:6379> hmget user username age
1) "20"
2) "lisi"
----------------------------
HGETALL key
127.0.0.1:6379> hgetall user
1) "age"
2) "20"
3) "username"
4) "lisi"
-
-
- 刪除字段
-
可以刪除一個或多個字段,返回值是被刪除的字段個數
HDEL key field [field ...]
127.0.0.1:6379> hdel user age
(integer) 1
127.0.0.1:6379> hdel user age name
(integer) 0
127.0.0.1:6379> hdel user age username
(integer) 1
-
-
- 增加數字
-
HINCRBY key field increment
127.0.0.1:6379> hincrby user age 2 將用戶的年齡加2
(integer) 22
127.0.0.1:6379> hget user age 獲取用戶的年齡
"22“
-
-
- 判斷字段是否存在
-
HEXISTS key field
127.0.0.1:6379> hexists user age 查看user中是否有age字段
(integer) 1
127.0.0.1:6379> hexists user name 查看user中是否有name字段
(integer) 0
HSETNX key field value
當字段不存在時賦值,類似HSET,區別在於如果字段已經存在,該命令不執行任何操作。
127.0.0.1:6379> hsetnx user age 30 如果user中沒有age字段則設置age值爲30,否則不做任何操作
(integer) 0
-
-
- 只獲取字段名或字段值
-
HKEYS key
HVALS key
127.0.0.1:6379> hmset user age 20 name lisi
OK
127.0.0.1:6379> hkeys user
1) "age"
2) "name"
127.0.0.1:6379> hvals user
1) "20"
2) "lisi"
-
-
- 獲取字段數量
-
HLEN key
127.0.0.1:6379> hlen user
(integer) 2
-
- 應用
- 商品信息
- 應用
商品id、商品名稱、商品描述、商品庫存、商品好評
定義商品信息的key:
商品1001的信息在 redis中的key爲:items:1001
存儲商品信息
192.168.101.3:7003> HMSET items:1001 id 3 name apple price 999.9
OK
獲取商品信息
192.168.101.3:7003> HGET items:1001 id
"3"
192.168.101.3:7003> HGETALL items:1001
1) "id"
2) "3"
3) "name"
4) "apple"
5) "price"
6) "999.9"
- 數據類型--list
- ArrayList與LinkedList的區別
ArrayList使用數組方式存儲數據,所以根據索引查詢數據速度快,而新增或者刪除元素時需要設計到位移操作,所以比較慢。
LinkedList使用雙向鏈接方式存儲數據,每個元素都記錄前後元素的指針,所以插入、刪除數據時只是更改前後元素的指針指向即可,速度非常快,然後通過下標查詢元素時需要從頭開始索引,所以比較慢,但是如果查詢前幾個元素或後幾個元素速度比較快。
-
- redis list介紹
列表類型(list)可以存儲一個有序的字符串列表,常用的操作是向列表兩端添加元素,或者獲得列表的某一個片段。
列表類型內部是使用雙向鏈表(double linked list)實現的,所以向列表兩端添加元素的時間複雜度爲0(1),獲取越接近兩端的元素速度就越快。這意味着即使是一個有幾千萬個元素的列表,獲取頭部或尾部的10條記錄也是極快的。
設計一個字符串類:客戶有時需要知道字符串的長度。
所以有兩種設計GetLength()函數的方法
1。每次客戶詢問長度,都用循環檢測串長,即for(i=0;str[i]!=0;++i)
這樣效率低 時間複雜度O(n)
2 每次串內容改變時計算長度,算好後存起來,以後客戶需要知道字符串的長度就直接把變量值返回,這樣效率高 時間複雜度O(1)
-
- 命令
- 向列表兩端增加元素。
- 命令
LPUSH key value [value ...]
RPUSH key value [value ...]
向列表左邊增加元素
127.0.0.1:6379> lpush list:a 1 2 3
(integer) 3
向列表右邊增加元素
127.0.0.1:6379> rpush list:a 4 5 6
(integer) 6
-
-
- 查看列表
-
LRANGE key start stop
LRANGE命令是列表類型最常用的命令之一,獲取列表中的某一片段,將返回start、stop之間的所有元素(包含兩端的元素),索引從0開始。索引可以是負數,如:“-1”代表最後邊的一個元素。
127.0.0.1:6379> lrange list:a 0 2
1) "3"
2) "2"
3) "1"
-
-
- 從列表兩端彈出元素
-
LPOP key
RPOP key
LPOP命令從列表左邊彈出一個元素,會分兩步完成,第一步是將列表左邊的元素從列表中移除,第二步是返回被移除的元素值。
127.0.0.1:6379> lpop list:a
"3“
127.0.0.1:6379> rpop list:a
"6“
-
-
- 獲取列表中元素的個數
-
LLEN key
127.0.0.1:6379> llen list:a
(integer) 6
-
-
- 刪除列表中指定的值
-
LREM key count value
LREM命令會刪除列表中前count個值爲value的元素,返回實際刪除的元素個數。根據count值的不同,該命令的執行方式會有所不同:
- 當count>0時, LREM會從列表左邊開始刪除。
- 當count<0時, LREM會從列表後邊開始刪除。
- 當count=0時, LREM刪除所有值爲value的元素。
#爲後面的示例準備測試數據。
redis 127.0.0.1:6379> lpush mykey a b c d a c
(integer) 6
#從頭部(left)向尾部(right)變量鏈表,刪除2個值等於a的元素,返回值爲實際刪除的數量。
redis 127.0.0.1:6379> lrem mykey 2 a
(integer) 2
#看出刪除後鏈表中的全部元素。
redis 127.0.0.1:6379> lrange mykey 0 -1
1) "c"
2) "d"
3) "c"
4) "b"
-
-
- 獲得/設置指定索引的元素值
-
LINDEX key index
LSET key index value
#獲取索引值爲1(頭部的第二個元素)的元素值。
redis 127.0.0.1:6379> lindex mykey 1
"d"
#將索引值爲1(頭部的第二個元素)的元素值設置爲新值e。
redis 127.0.0.1:6379> lset mykey 1 e
OK
#查看是否設置成功。
redis 127.0.0.1:6379> lindex mykey 1
"e"
#索引值6超過了鏈表中元素的數量,該命令返回nil。
redis 127.0.0.1:6379> lindex mykey 6
(nil)
#設置的索引值6超過了鏈表中元素的數量,設置失敗,該命令返回錯誤信息。
redis 127.0.0.1:6379> lset mykey 6 hh
(error) ERR index out of range
#僅保留索引值0到2之間的3個元素,注意第0個和第2個元素均被保留。
redis 127.0.0.1:6379> ltrim mykey 0 2
OK
#查看trim後的結果。
redis 127.0.0.1:6379> lrange mykey 0 -1
1) "c"
2) "e"
3) "c"
-
-
- 向列表中插入元素
-
LINSERT key BEFORE|AFTER pivot value
該命令首先會在列表中從左到右查找值爲pivot的元素,然後根據第二個參數是BEFORE還是AFTER來決定將value插入到該元素的前面還是後面。
#刪除該鍵便於後面的測試。
redis 127.0.0.1:6379> del mykey
(integer) 1
#爲後面的示例準備測試數據。
redis 127.0.0.1:6379> lpush mykey a b c d e
(integer) 5
#在a的前面插入新元素a1。
redis 127.0.0.1:6379> linsert mykey before a a1
(integer) 6
#查看是否插入成功,從結果看已經插入。注意lindex的index值是0-based。
redis 127.0.0.1:6379> lindex mykey 0
"e"
#在e的後面插入新元素e2,從返回結果看已經插入成功。
redis 127.0.0.1:6379> linsert mykey after e e2
(integer) 7
#再次查看是否插入成功。
redis 127.0.0.1:6379> lindex mykey 1
"e2"
#在不存在的元素之前或之後插入新元素,該命令操作失敗,並返回-1。
redis 127.0.0.1:6379> linsert mykey after k a
(integer) -1
#爲不存在的Key插入新元素,該命令操作失敗,返回0。
redis 127.0.0.1:6379> linsert mykey1 after a a2
(integer) 0
- 將元素從一個列表轉移到另一個列表中
RPOPLPUSH source destination
#刪除該鍵,以便於後面的測試。
redis 127.0.0.1:6379> del mykey
(integer) 1
#從鏈表的尾部插入參數中給出的values,插入順序是從左到右依次插入。
redis 127.0.0.1:6379> rpush mykey a b c d
(integer) 4
#通過lrange的可以獲悉rpush在插入多值時的插入順序。
redis 127.0.0.1:6379> lrange mykey 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
#該鍵已經存在並且包含4個元素,rpushx命令將執行成功,並將元素e插入到鏈表的尾部。
redis 127.0.0.1:6379> rpushx mykey e
(integer) 5
#通過lindex命令可以看出之前的rpushx命令確實執行成功,因爲索引值爲4的元素已經是新元素了。
redis 127.0.0.1:6379> lindex mykey 4
"e"
#由於mykey2鍵並不存在,因此該命令不會插入數據,其返回值爲0。
redis 127.0.0.1:6379> rpushx mykey2 e
(integer) 0
#在執行rpoplpush命令前,先看一下mykey中鏈表的元素有哪些,注意他們的位置關係。
redis 127.0.0.1:6379> lrange mykey 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
#將mykey的尾部元素e彈出,同時再插入到mykey2的頭部(原子性的完成這兩步操作)。
redis 127.0.0.1:6379> rpoplpush mykey mykey2
"e"
#通過lrange命令查看mykey在彈出尾部元素後的結果。
redis 127.0.0.1:6379> lrange mykey 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
#通過lrange命令查看mykey2在插入元素後的結果。
redis 127.0.0.1:6379> lrange mykey2 0 -1
1) "e"
#將source和destination設爲同一鍵,將mykey中的尾部元素移到其頭部。
redis 127.0.0.1:6379> rpoplpush mykey mykey
"d"
#查看移動結果。
redis 127.0.0.1:6379> lrange mykey 0 -1
1) "d"
2) "a"
3) "b"
4) "c"
-
- 應用
- 商品評論列表
- 應用
思路:
在redis中創建商品評論列表
用戶發佈商品評論,將評論信息轉成json存儲到list中。
用戶在頁面查詢評論列表,從redis中取出json數據展示到頁面。
定義商品評論列表key:
商品編號爲1001的商品評論key:items: comment:1001
192.168.56.143:6379> LPUSH items:comment:1001 '{"id":1,"name":"sa","date":1430295077289}'
- 數據類型--set
- redis set介紹
在集合中的每個元素都是不同的,且沒有順序。
集合類型和列表類型的對比:
集合類型的常用操作是向集合中加入或刪除元素、判斷某個元素是否存在等,由於集合類型的Redis內部是使用值爲空的散列表實現,所有這些操作的時間複雜度都爲0(1)。
Redis還提供了多個集合之間的交集、並集、差集的運算。
-
- 命令
1. SADD/SMEMBERS/SCARD/SISMEMBER:
#在Shell命令行下啓動Redis的客戶端程序。
/> redis-cli
#插入測試數據,由於該鍵myset之前並不存在,因此參數中的三個成員都被正常插入。
redis 127.0.0.1:6379> sadd myset a b c
(integer) 3
#由於參數中的a在myset中已經存在,因此本次操作僅僅插入了d和e兩個新成員。
redis 127.0.0.1:6379> sadd myset a d e
(integer) 2
#判斷a是否已經存在,返回值爲1表示存在。
redis 127.0.0.1:6379> sismember myset a
(integer) 1
#判斷f是否已經存在,返回值爲0表示不存在。
redis 127.0.0.1:6379> sismember myset f
(integer) 0
#通過smembers命令查看插入的結果,從結果可以,輸出的順序和插入順序無關。
redis 127.0.0.1:6379> smembers myset
1) "c"
2) "d"
3) "a"
4) "b"
5) "e"
#獲取Set集合中元素的數量。
redis 127.0.0.1:6379> scard myset
(integer) 5
2. SPOP/SREM/SRANDMEMBER/SMOVE:
#刪除該鍵,便於後面的測試。
redis 127.0.0.1:6379> del myset
(integer) 1
#爲後面的示例準備測試數據。
redis 127.0.0.1:6379> sadd myset a b c d
(integer) 4
#查看Set中成員的位置。
redis 127.0.0.1:6379> smembers myset
1) "c"
2) "d"
3) "a"
4) "b"
#從結果可以看出,該命令確實是隨機的返回了某一成員。
redis 127.0.0.1:6379> srandmember myset
"c"
#Set中尾部的成員b被移出並返回,事實上b並不是之前插入的第一個或最後一個成員。
redis 127.0.0.1:6379> spop myset
"b"
#查看移出後Set的成員信息。
redis 127.0.0.1:6379> smembers myset
1) "c"
2) "d"
3) "a"
#從Set中移出a、d和f三個成員,其中f並不存在,因此只有a和d兩個成員被移出,返回爲2。
redis 127.0.0.1:6379> srem myset a d f
(integer) 2
#查看移出後的輸出結果。
redis 127.0.0.1:6379> smembers myset
1) "c"
#爲後面的smove命令準備數據。
redis 127.0.0.1:6379> sadd myset a b
(integer) 2
redis 127.0.0.1:6379> sadd myset2 c d
(integer) 2
#將a從myset移到myset2,從結果可以看出移動成功。
redis 127.0.0.1:6379> smove myset myset2 a
(integer) 1
#再次將a從myset移到myset2,由於此時a已經不是myset的成員了,因此移動失敗並返回0。
redis 127.0.0.1:6379> smove myset myset2 a
(integer) 0
#分別查看myset和myset2的成員,確認移動是否真的成功。
redis 127.0.0.1:6379> smembers myset
1) "b"
redis 127.0.0.1:6379> smembers myset2
1) "c"
2) "d"
3) "a"
3. SDIFF/SDIFFSTORE/SINTER/SINTERSTORE:
#爲後面的命令準備測試數據。
redis 127.0.0.1:6379> sadd myset a b c d
(integer) 4
redis 127.0.0.1:6379> sadd myset2 c
(integer) 1
redis 127.0.0.1:6379> sadd myset3 a c e
(integer) 3
#myset和myset2相比,a、b和d三個成員是兩者之間的差異成員。再用這個結果繼續和myset3進行差異比較,b和d是myset3不存在的成員。
redis 127.0.0.1:6379> sdiff myset myset2 myset3
1) "d"
2) "b"
#將3個集合的差異成員存在在diffkey關聯的Set中,並返回插入的成員數量。
redis 127.0.0.1:6379> sdiffstore diffkey myset myset2 myset3
(integer) 2
#查看一下sdiffstore的操作結果。
redis 127.0.0.1:6379> smembers diffkey
1) "d"
2) "b"
#從之前準備的數據就可以看出,這三個Set的成員交集只有c。
redis 127.0.0.1:6379> sinter myset myset2 myset3
1) "c"
#將3個集合中的交集成員存儲到與interkey關聯的Set中,並返回交集成員的數量。
redis 127.0.0.1:6379> sinterstore interkey myset myset2 myset3
(integer) 1
#查看一下sinterstore的操作結果。
redis 127.0.0.1:6379> smembers interkey
1) "c"
#獲取3個集合中的成員的並集。
redis 127.0.0.1:6379> sunion myset myset2 myset3
1) "b"
2) "c"
3) "d"
4) "e"
5) "a"
#將3個集合中成員的並集存儲到unionkey關聯的set中,並返回並集成員的數量。
redis 127.0.0.1:6379> sunionstore unionkey myset myset2 myset3
(integer) 5
#查看一下suiionstore的操作結果。
redis 127.0.0.1:6379> smembers unionkey
1) "b"
2) "c"
3) "d"
4) "e"
5) "a"
-
- 應用範圍:
1). 可以使用Redis的Set數據類型跟蹤一些唯一性數據,比如訪問某一博客的唯一IP地址信息。對於此場景,我們僅需在每次訪問該博客時將訪問者的IP存入Redis中,Set數據類型會自動保證IP地址的唯一性。
2). 充分利用Set類型的服務端聚合操作方便、高效的特性,可以用於維護數據對象之間的關聯關係。比如所有購買某一電子設備的客戶ID被存儲在一個指定的Set中,而購買另外一種電子產品的客戶ID被存儲在另外一個Set中,如果此時我們想獲取有哪些客戶同時購買了這兩種商品時,Set的intersections命令就可以充分發揮它的方便和效率的優勢了。
- 數據類型--sorted set
- redis sorted set介紹
在集合類型的基礎上有序集合類型爲集合中的每個元素都關聯一個分數,這使得我們不僅可以完成插入、刪除和判斷元素是否存在在集合中,還能夠獲得分數最高或最低的前N個元素、獲取指定分數範圍內的元素等與分數有關的操作。
在某些方面有序集合和列表類型有些相似。
1、二者都是有序的。
2、二者都可以獲得某一範圍的元素。
但是,二者有着很大區別:
1、列表類型是通過鏈表實現的,獲取靠近兩端的數據速度極快,而當元素增多後,訪問中間數據的速度會變慢。
2、有序集合類型使用散列表實現,所有即使讀取位於中間部分的數據也很快。
3、列表中不能簡單的調整某個元素的位置,但是有序集合可以(通過更改分數實現)
4、有序集合要比列表類型更耗內存。
-
- 命令
1. ZADD/ZCARD/ZCOUNT/ZREM/ZINCRBY/ZSCORE/ZRANGE/ZRANK:
#在Shell的命令行下啓動Redis客戶端工具。
/> redis-cli
#添加一個分數爲1的成員。
redis 127.0.0.1:6379> zadd myzset 1 "one"
(integer) 1
#添加兩個分數分別是2和3的兩個成員。
redis 127.0.0.1:6379> zadd myzset 2 "two" 3 "three"
(integer) 2
#0表示第一個成員,-1表示最後一個成員。WITHSCORES選項表示返回的結果中包含每個成員及其分數,否則只返回成員。
redis 127.0.0.1:6379> zrange myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "3"
#獲取成員one在Sorted-Set中的位置索引值。0表示第一個位置。
redis 127.0.0.1:6379> zrank myzset one
(integer) 0
#成員four並不存在,因此返回nil。
redis 127.0.0.1:6379> zrank myzset four
(nil)
#獲取myzset鍵中成員的數量。
redis 127.0.0.1:6379> zcard myzset
(integer) 3
#返回與myzset關聯的Sorted-Set中,分數滿足表達式1 <= score <= 2的成員的數量。
redis 127.0.0.1:6379> zcount myzset 1 2
(integer) 2
#刪除成員one和two,返回實際刪除成員的數量。
redis 127.0.0.1:6379> zrem myzset one two
(integer) 2
#查看是否刪除成功。
redis 127.0.0.1:6379> zcard myzset
(integer) 1
#獲取成員three的分數。返回值是字符串形式。
redis 127.0.0.1:6379> zscore myzset three
"3"
#由於成員two已經被刪除,所以該命令返回nil。
redis 127.0.0.1:6379> zscore myzset two
(nil)
#將成員one的分數增加2,並返回該成員更新後的分數。
redis 127.0.0.1:6379> zincrby myzset 2 one
"3"
#將成員one的分數增加-1,並返回該成員更新後的分數。
redis 127.0.0.1:6379> zincrby myzset -1 one
"2"
#查看在更新了成員的分數後是否正確。
redis 127.0.0.1:6379> zrange myzset 0 -1 WITHSCORES
1) "one"
2) "2"
3) "two"
4) "2"
5) "three"
6) "3"
2. ZRANGEBYSCORE/ZREMRANGEBYRANK/ZREMRANGEBYSCORE
redis 127.0.0.1:6379> del myzset
(integer) 1
redis 127.0.0.1:6379> zadd myzset 1 one 2 two 3 three 4 four
(integer) 4
#獲取分數滿足表達式1 <= score <= 2的成員。
redis 127.0.0.1:6379> zrangebyscore myzset 1 2
1) "one"
2) "two"
#獲取分數滿足表達式1 < score <= 2的成員。
redis 127.0.0.1:6379> zrangebyscore myzset (1 2
1) "two"
#-inf表示第一個成員,+inf表示最後一個成員,limit後面的參數用於限制返回成員的自己,
#2表示從位置索引(0-based)等於2的成員開始,取後面3個成員。
redis 127.0.0.1:6379> zrangebyscore myzset -inf +inf limit 2 3
1) "three"
2) "four"
#刪除分數滿足表達式1 <= score <= 2的成員,並返回實際刪除的數量。
redis 127.0.0.1:6379> zremrangebyscore myzset 1 2
(integer) 2
#看出一下上面的刪除是否成功。
redis 127.0.0.1:6379> zrange myzset 0 -1
1) "three"
2) "four"
#刪除位置索引滿足表達式0 <= rank <= 1的成員。
redis 127.0.0.1:6379> zremrangebyrank myzset 0 1
(integer) 2
#查看上一條命令是否刪除成功。
redis 127.0.0.1:6379> zcard myzset
(integer) 0
3. ZREVRANGE/ZREVRANGEBYSCORE/ZREVRANK:
#爲後面的示例準備測試數據。
redis 127.0.0.1:6379> del myzset
(integer) 0
redis 127.0.0.1:6379> zadd myzset 1 one 2 two 3 three 4 four
(integer) 4
#以位置索引從高到低的方式獲取並返回此區間內的成員。
redis 127.0.0.1:6379> zrevrange myzset 0 -1 WITHSCORES
1) "four"
2) "4"
3) "three"
4) "3"
5) "two"
6) "2"
7) "one"
8) "1"
#由於是從高到低的排序,所以位置等於0的是four,1是three,並以此類推。
redis 127.0.0.1:6379> zrevrange myzset 1 3
1) "three"
2) "two"
3) "one"
#由於是從高到低的排序,所以one的位置是3。
redis 127.0.0.1:6379> zrevrank myzset one
(integer) 3
#由於是從高到低的排序,所以four的位置是0。
redis 127.0.0.1:6379> zrevrank myzset four
(integer) 0
#獲取分數滿足表達式3 >= score >= 0的成員,並以相反的順序輸出,即從高到底的順序。
redis 127.0.0.1:6379> zrevrangebyscore myzset 3 0
1) "three"
2) "two"
3) "one"
#該命令支持limit選項,其含義等同於zrangebyscore中的該選項,只是在計算位置時按照相反的順序計算和獲取。
redis 127.0.0.1:6379> zrevrangebyscore myzset 4 0 limit 1 2
1) "three"
2) "two"
-
- 應用
根據商品銷售量對商品進行排行顯示,定義sorted set集合,商品銷售量爲元素的分數。
定義商品銷售排行榜key:items:sellsort
寫入商品銷售量:
商品編號1001的銷量是9,商品編號1002的銷量是10
192.168.101.3:7007> ZADD items:sellsort 9 1001 10 1002
商品編號1001的銷量加1
192.168.101.3:7001> ZINCRBY items:sellsort 1 1001
商品銷量前10名:
192.168.101.3:7001> ZRANGE items:sellsort 0 9 withscores
- keys命令
有3個通配符 *, ? ,[]
*: 通配任意多個字符 ?: 通配單個字符 [] 通配括號內的字符
1. KEYS/RENAME/DEL/EXISTS/MOVE/RENAMENX:
#在Shell命令行下啓動Redis客戶端工具。
/> redis-cli
#清空當前選擇的數據庫,以便於對後面示例的理解。
redis 127.0.0.1:6379> flushdb
OK
#添加String類型的模擬數據。
redis 127.0.0.1:6379> set mykey 2
OK
redis 127.0.0.1:6379> set mykey2 "hello"
OK
#添加Set類型的模擬數據。
redis 127.0.0.1:6379> sadd mysetkey 1 2 3
(integer) 3
#添加Hash類型的模擬數據。
redis 127.0.0.1:6379> hset mmtest username "stephen"
(integer) 1
#根據參數中的模式,獲取當前數據庫中符合該模式的所有key,從輸出可以看出,該命令在執行時並不區分與Key關聯的Value類型。
redis 127.0.0.1:6379> keys my*
1) "mysetkey"
2) "mykey"
3) "mykey2"
#刪除了兩個Keys。
redis 127.0.0.1:6379> del mykey mykey2
(integer) 2
#查看一下剛剛刪除的Key是否還存在,從返回結果看,mykey確實已經刪除了。
redis 127.0.0.1:6379> exists mykey
(integer) 0
#查看一下沒有刪除的Key,以和上面的命令結果進行比較。
redis 127.0.0.1:6379> exists mysetkey
(integer) 1
#將當前數據庫中的mysetkey鍵移入到ID爲1的數據庫中,從結果可以看出已經移動成功。
redis 127.0.0.1:6379> move mysetkey 1
(integer) 1
#打開ID爲1的數據庫。
redis 127.0.0.1:6379> select 1
OK
#查看一下剛剛移動過來的Key是否存在,從返回結果看已經存在了。
redis 127.0.0.1:6379[1]> exists mysetkey
(integer) 1
#在重新打開ID爲0的缺省數據庫。
(注意: 一個redis進程,打開了不止一個數據庫, 默認打開16個數據庫,從0到15編號,
如果想打開更多數據庫,可以從配置文件修改)
redis 127.0.0.1:6379[1]> select 0
OK
#查看一下剛剛移走的Key是否已經不存在,從返回結果看已經移走。
redis 127.0.0.1:6379> exists mysetkey
(integer) 0
#準備新的測試數據。
redis 127.0.0.1:6379> set mykey "hello"
OK
#將mykey改名爲mykey1
redis 127.0.0.1:6379> rename mykey mykey1
OK
#由於mykey已經被重新命名,再次獲取將返回nil。
redis 127.0.0.1:6379> get mykey
(nil)
#通過新的鍵名獲取。
redis 127.0.0.1:6379> get mykey1
"hello"
#由於mykey已經不存在了,所以返回錯誤信息。
redis 127.0.0.1:6379> rename mykey mykey1
(error) ERR no such key
#爲renamenx準備測試key
redis 127.0.0.1:6379> set oldkey "hello"
OK
redis 127.0.0.1:6379> set newkey "world"
OK
#由於newkey已經存在,因此該命令未能成功執行。
redis 127.0.0.1:6379> renamenx oldkey newkey
(integer) 0
#查看newkey的值,發現它也沒有被renamenx覆蓋。
redis 127.0.0.1:6379> get newkey
"world"
2. PERSIST/EXPIRE/EXPIREAT/TTL:
#爲後面的示例準備的測試數據。
redis 127.0.0.1:6379> set mykey "hello"
OK
#將該鍵的超時設置爲100秒。
redis 127.0.0.1:6379> expire mykey 100
(integer) 1
#通過ttl命令查看一下還剩下多少秒。
redis 127.0.0.1:6379> ttl mykey
(integer) 97
#立刻執行persist命令,該存在超時的鍵變成持久化的鍵,即將該Key的超時去掉。
redis 127.0.0.1:6379> persist mykey
(integer) 1
#ttl的返回值告訴我們,該鍵已經沒有超時了。
redis 127.0.0.1:6379> ttl mykey
(integer) -1
#爲後面的expire命令準備數據。
redis 127.0.0.1:6379> del mykey
(integer) 1
redis 127.0.0.1:6379> set mykey "hello"
OK
#設置該鍵的超時被100秒。
redis 127.0.0.1:6379> expire mykey 100
(integer) 1
#用ttl命令看一下當前還剩下多少秒,從結果中可以看出還剩下96秒。
redis 127.0.0.1:6379> ttl mykey
(integer) 96
#重新更新該鍵的超時時間爲20秒,從返回值可以看出該命令執行成功。
redis 127.0.0.1:6379> expire mykey 20
(integer) 1
#再用ttl確認一下,從結果中可以看出果然被更新了。
redis 127.0.0.1:6379> ttl mykey
(integer) 17
#立刻更新該鍵的值,以使其超時無效。
redis 127.0.0.1:6379> set mykey "world"
OK
#從ttl的結果可以看出,在上一條修改該鍵的命令執行後,該鍵的超時也無效了。
redis 127.0.0.1:6379> ttl mykey
(integer) -1
3. TYPE/RANDOMKEY/SORT:
#由於mm鍵在數據庫中不存在,因此該命令返回none。
redis 127.0.0.1:6379> type mm
none
#mykey的值是字符串類型,因此返回string。
redis 127.0.0.1:6379> type mykey
string
#準備一個值是set類型的鍵。
redis 127.0.0.1:6379> sadd mysetkey 1 2
(integer) 2
#mysetkey的鍵是set,因此返回字符串set。
redis 127.0.0.1:6379> type mysetkey
set
#返回數據庫中的任意鍵。
redis 127.0.0.1:6379> randomkey
"oldkey"
#清空當前打開的數據庫。
redis 127.0.0.1:6379> flushdb
OK
#由於沒有數據了,因此返回nil。
redis 127.0.0.1:6379> randomkey
(nil)
- 服務器命令
- ping
測試連接是否存活
redis 127.0.0.1:6379> ping
PONG
//執行下面命令之前,我們停止redis 服務器
redis 127.0.0.1:6379> ping
Could not connect to Redis at 127.0.0.1:6379: Connection refused
//執行下面命令之前,我們啓動redis 服務器
not connected> ping
PONG
redis 127.0.0.1:6379>
第一個ping 時,說明此連接正常
第二個ping 之前,我們將redis 服務器停止,那麼ping 是失敗的
第三個ping 之前,我們將redis 服務器啓動,那麼ping 是成功的
- echo
在命令行打印一些內容
redis 127.0.0.1:6379> echo HongWan
"HongWan"
redis 127.0.0.1:6379>
- select
選擇數據庫。Redis 數據庫編號從0~15,我們可以選擇任意一個數據庫來進行數據的存取。
redis 127.0.0.1:6379> select 1
OK
redis 127.0.0.1:6379[1]> select 16
(error) ERR invalid DB index
redis 127.0.0.1:6379[16]>
當選擇16 時,報錯,說明沒有編號爲16 的這個數據庫
- quit
退出連接。
redis 127.0.0.1:6379> quit
- dbsize
返回當前數據庫中key 的數目。
redis 127.0.0.1:6379> dbsize
(integer) 18
redis 127.0.0.1:6379>
結果說明此庫中有18 個key
- info
獲取服務器的信息和統計。
redis 127.0.0.1:6379> info
redis_version:2.2.12
redis_git_sha1:00000000
redis_git_dirty:0
arch_bits:32
multiplexing_api:epoll
process_id:28480
uptime_in_seconds:2515
uptime_in_days:0
- flushdb
刪除當前選擇數據庫中的所有key。
redis 127.0.0.1:6379> dbsize
(integer) 18
redis 127.0.0.1:6379> flushdb
OK
redis 127.0.0.1:6379> dbsize
(integer) 0
redis 127.0.0.1:6379>
在本例中我們將0 號數據庫中的key 都清除了。
- flushall
刪除所有數據庫中的所有key。
redis 127.0.0.1:6379[1]> dbsize
(integer) 1
redis 127.0.0.1:6379[1]> select 0
OK
redis 127.0.0.1:6379> flushall
OK
redis 127.0.0.1:6379> select 1
OK
redis 127.0.0.1:6379[1]> dbsize
(integer) 0
redis 127.0.0.1:6379[1]>
在本例中我們先查看了一個1 號數據庫中有一個key,然後我切換到0 號庫執行flushall 命令,結果1 號庫中的key 也被清除了,說是此命令工作正常。