Redis是一種面向“鍵/值”對數據類型的內存數據庫,可以滿足我們對海量數據的讀寫需求。
redis的鍵只能是字符串
redis的值支持多種數據類型:
1:字符串 string
2:哈希 hash
3:字符串列表 list
4:字符串集合 set 不重複,無序
5:有序集合sorted set ,不重複,有序
6:HyperLogLogs 結構(redis2.8.9版本纔有,用來做基數統計的算法。)
特點:
高性能(Redis能讀的速度是110000次/s,寫的速度是81000次/s)
原子性
持久存儲(兩種方式RDB/快照,AOF/日誌)
主從結構(master-slave,高可用)
應用:
適合高併發和實時請求的應用場景。
新浪微博
hash:關注列表,粉絲列表
string:微博數,粉絲數(避免使用select count(*) from...)
sorted set:TopN,熱門微博
redis安裝部署(單機)
解壓:tar -zxvf redis-2.8.19.tar.gz
進入到redis根目錄執行make編譯
繼續執行make install 安裝 會將redis/src下的可執行文件複製到/usr/local/bin下,方便執行
執行redis-server啓動redis 默認端口6379 也可以啓動時指定端口 redis-server --port 端口號
指定啓動時使用的配置文件 redis-server redis.conf [--port 6379]
也可以讓redis在後臺運行
修改配置文件redis.conf
daemonize yes(後臺運行)
logfile /usr/local/redis/log(日誌文件)
啓動 redis-server redis.conf 後臺運行
使用redis客戶端工具訪問數據庫
redis客戶端
redis-cli -h 127.0.0.1 -p 6379
關閉數據庫服務
redis-cli shutdown (停止前redis會持久化數據到磁盤)
多數據庫
每個數據庫對外都是以一個從0開始的遞增數字命名,不支持自定義的
redis默認支持16個數據庫,可以通過修改databases參數來修改這個默認值
redis默認選擇的是0號數據庫
SELECT 數字: 可以切換數據庫
多個數據庫之間並不是完全隔離的 執行flushall會清空所有redis數據庫數據
redis基礎命令:
keys 表達式(?,* ,[],\?) keys後可以添加表達式,滿足該表達式的鍵將被返回
? 匹配任意字符
* 匹配0個或任意多個字符
[]可以匹配區間字符 例如[a-d] 即a b c d四個字符 [1,2,3,4] 即1或2或3或4
\?匹配? 斜線是轉義字符,匹配問號
redis大小寫不敏感
判斷一個鍵是否存在
exists key
刪除鍵
del key
del key1 key2 刪除多個
批量刪除(在linux命令行執行)
redis-cli keys "key*" | xargs redis-cli del
redis-cli del `redis-cli keys "key*"`
獲得鍵值的數據類型type
返回值可能是這五種類型(string,hash,list,set,zset)
redis的help命令
"help @<group>" 返回對應類型的所有命令 group爲五種類型
"help <command>" for help on <command>
"help <tab>" to get a list of possible help topics
"quit" to exit
redis數據類型之string
字符串類型是redis中最基本的數據類型,它能存儲任何形式的字符串,包含二進制數據,甚至是一張圖片(二進制內
容)。一個字符串類型的值存儲的最大容量是1GB (redis2.8.9版本)
命令
set/get
mset/mget 同時設置和獲取多個變量的值 mset name1 zhangsan name2 lisi mget name1 name2
incr/decr/incrby/decrby/incrbyfloat 使用help查詢用法
append 用法 append name welcome 給name變量中的值後追加welcome
strlen 返回string類型變量存儲的值的長度
redis數據類型之hash
hash類型的值存儲了字段和字段值的映射,字段值只能是字符串,不支持其他數據類型。hash類型的鍵至多可以包
含2的32次方減1個字段。
hash類型適合存儲對象:如圖:1-1和1-2
redis可以爲任何鍵增減字段而不影響其他鍵
命令
hset/hget/hmset/hmget/hgetall(hsetnx)
HSET user1 name zhangsan 設置user1的name爲zhangsan
HMSET user1 name zhangsan age 20 設置user1的name爲zhangsan,age爲20
hgetall user1 獲取user1中所有屬性和值
hexists,判斷鍵中的屬性是否存在
hincrby(hash類型沒有hincr命令)
hdel 刪除字段
hkeys/hvals
hlen
其餘命令使用help查看幫助使用
redis數據類型之list
list是一個有序的字符串列表,列表內部實現是使用雙向列表(linked list)實現的。
list還可以作爲隊列使用
一個列表類型的鍵最多能容納2的32次方減1個元素。
命令
lpush/rpush/lpop/rpop
llen/lrange
lrem(lrem key count value)count分爲三種情況
lrem list1 0 zhangsan 刪除list1中所有值爲zhangsan的元素
lrem list1 2 zhangsan 從左側開始刪除2個值爲zhangsan的元素
lrem list1 -2 zhangsan 從右側開始刪除2個值爲zhangsan的元素
lindex/lset/ltrim/linsert
rpoplpush:將元素從一個列表轉到另一個列表
redis數據類型之set
set集合中的數據都是不重複的,無序的,一個集合類型鍵可以存儲至多2的32次方減1個字符串
set集合類型和list列表類型的相似之處
命令
sadd/srem/smembers/sismember
sdiff/sinter/sunion
sdiffstore/sinterstore/sunionstore
scard/srandmember/spop
redis數據類型之sorted set
有序集合,在集合類型的基礎上爲集合中的每個元素都關聯了一個分數,這樣可以很方便的獲得分數最高的N個元素
(topN)。
有序集合類型和列表類型的差異:見備註
命令
zadd/zscore/zrange/zrevrange/zrangebyscore
zincrby/zcard/zcount/zrem/zremrangebyrank
zremrangebyscore/zrank/zrevrank
zinterstore/zunionstore
zrangebyscore setname 10 20 返回setname中分數大於等於10小於等於20的元素
zrangebyscore setname (10 (20返回setname中分數大於10小於20的元素
擴展:+inf(正無窮) -inf(負無窮)
zadd zset +inf zhangsan 給zset集合中添加分數爲正無窮的元素zhangsan
set命令
(如果 key 已經持有其他值, SET 就覆寫舊值,無視類型。)
Java訪問Redis
使用jedis第三方jar包操作redismvn依賴
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.6.0</version>
</dependency>
String host = "hadoop1";
int port = 6379;
@Test
public void testSet(){
Jedis jedis = new Jedis(host, port);
String result = jedis.set("hello", "world");
System.out.println(result);
jedis.close();
}
@Test
public void testGet(){
Jedis jedis = new Jedis(host, port);
String result = jedis.get("hello");
System.out.println(result);
jedis.close();
}
/**
* 單服務器連接池方式
*/
@Test
public void testPool(){
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(100);//總鏈接數
poolConfig.setMaxIdle(10);//最大空閒鏈接
poolConfig.setMaxWaitMillis(5000);//最大超時時間
poolConfig.setTestOnBorrow(true);//應用該配置時是否測試該實例是否可用
JedisPool jedisPool = new JedisPool(poolConfig, host, port);
Jedis jedis = jedisPool.getResource();
String result = jedis.get("hello");
System.out.println(result);
jedisPool.returnResource(jedis);
}
/**
* 分佈式多服務器連接池方式
*/
@Test
public void testMorePool(){
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(100);//總鏈接數
poolConfig.setMaxIdle(10);//最大空閒鏈接
poolConfig.setMaxWaitMillis(5000);//最大超時時間
poolConfig.setTestOnBorrow(true);//應用該配置時是否測試該實例是否可用
List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
shards.add(new JedisShardInfo(host, port));//List中每個元素代表一臺redis服務器
//連接池中會存在多個服務器的實例
ShardedJedisPool jedisPools = new ShardedJedisPool(poolConfig, shards);
ShardedJedis jedis = jedisPools.getResource();
String result = jedis.get("hello");
System.out.println(result);
jedisPools.returnResource(jedis);
}
redis的事務(transaction)
redis中的事務是一組命令的集合。事務同命令一樣都是redis的最小執行單元。
原理:先將屬於一個事務的命令發送給redis,然後再讓redis依次執行這些命令,要麼都執行,要麼都不執行。
應用場景
一組命令必須同時都執行,或者都不執行。
我們想要保證一組命令在執行的過程之中不被其它命令插入。
命令
multi 事務開始
.....
exec 事務結束,開始執行事務中的命令
discard 放棄事務
錯誤處理
1:語法錯誤:致命的錯誤,事務中的所有命令都不會執行
2:運行錯誤:不會影響事務中其他命令的執行
Redis 不支持回滾(roll back)
因爲不需要對回滾進行支持,所以redis的內部纔可以保持簡單且快速
watch命令
作用:監控一個或者多個鍵,當被監控的鍵值被修改後阻止之後的一個事務的執行。
但是不能保證其它客戶端不修改這一鍵值,所以我們需要在事務執行失敗後重新執行事務中的命令。
注意:執行完事務的exec命令之後,watch就會取消對所有鍵值的監控
unwatch:取消監控
/**
* 事物和監控綜合使用,保證執行結果的正確性
*/
@Test
public void TestTransAndWatch(){
Jedis jedis = new Jedis(host, port);
//接收事物執行返回結果
List<Object> exec = null;
//如果執行結果爲null,代表hello變量已被其他客戶端修改,當前事務執行失敗,需要重新執行
do{
exec = executeTransaction(jedis);
System.out.println("執行結果:" + exec);
}while(exec == null);
}
private List<Object> executeTransaction(Jedis jedis){
//監控hello變量,如果在事物中命令提交執行前,hello變量被別的客戶端修改,終止執行當前事物所有命令
//每次事物結束後監控就會失效,所以每次開啓事物都要添加監控
jedis.watch("hello");
//開啓事物
Transaction transaction = jedis.multi();
transaction.set("hello", "world1");
//睡眠5秒等待其他客戶端修改hello變量
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//提交事物,執行所有命令,返回執行結果
List<Object> exec = transaction.exec();
return exec;
}
redis中鍵的生存時間(expire)
redis中可以使用expire命令設置一個鍵的生存時間,到時間後redis會自動刪除它。
expire 設置失效時間(單位/秒)
persist 取消失效時間
ttl/pttl 查看鍵的剩餘時間
ttl返回值有三種:1.剩餘失效時間 2.-1(永久鍵) 3.-2(已失效被刪除)
pexpire設置失效時間(單位/毫秒)
expireat [key] unix時間戳1351858600
pexpireat [key] unix時間戳(毫秒)1351858700000
應用:
限時的優惠活動
網站數據緩存(對於一些需要定時更新的數據)
網站訪客訪問頻率限制(例如:1分鐘最多訪問10次) 設置用戶ip爲key,有效時間爲60秒,60秒內控制訪問次數
redis中數據的排序(sort)
sort命令可以對列表類型,集合類型和有序集合類型鍵進行排序。
sort key [desc]
by 參考鍵(參考鍵可以是字符串類型或者是hash類型的某個字段,hash類型的格式爲:鍵名->字段名)
示例:---數據初始化-----
list中元素 3 1 2
set score:1 70
set score:2 90
set score:3 80
------------------------
sort list by score:* 排序list時參考score:*鍵,*爲佔位符,會被list中元素值替換,即score:1 score:2 score:3
就會參考score:*中的值進行排序。該例可以理解爲id分別是1,2,3的學生,對應的分數分別是70,90,80,安分數從大到
小排列 執行sort list by score:* desc 結果爲 2 3 1
如果參考鍵中不帶*號則不排序
如果某個元素的參考鍵不存在,則默認參考鍵的值爲0
擴展 get參數
get參數的規則和by參數的規則一樣
get # (返回元素本身的值)
剛纔執行sort list by score:* desc 結果爲 2 3 1 這樣是返回了list中元素本身,如果還需要返回score:*中對應的分數的
話需要使用get 即sort list by score:* get # getscore:* 其中get #爲list中元素本身的值getscore:*爲與其對應的
score:*中的值
擴展 store參數
使用store 參數可以把sort的排序結果保存到指定的鍵中
性能優化
1:儘可能減少待排序鍵中元素的數量
2:使用limit參數只獲取需要的數據
3:如果要排序的數據數量很大,儘可能使用store參數將結果緩存。
“發佈/訂閱”模式
發佈:publish
例:publish channel message
訂閱:subscribe
例:subscribe channel [.....]
取消訂閱:unsubscribe (客戶端未實現,可以使用Java Api執行)
例:unsubscribe [channel]
按照規則訂閱:psubscribe 表達式
例:psubscribe channel? (?代表匹配任意一個字符)
按照規則取消訂閱:punsubscribe
注意:使用punsubscribe命令只能退訂通過psubscribe 訂閱的規則,不能退訂直接通過subscribe 訂閱的規則。
/**
* 消息發佈
*/
@Test
public void publish(){
Jedis jedis = new Jedis(host, port);
jedis.publish("channel1", "hello");
}
/**
* 消息訂閱
*/
@Test
public void subscribe(){
Jedis jedis = new Jedis(host, port);
JedisPubSub jedisPubSub = new JedisPubSub() {
//取消訂閱時
@Override
public void onUnsubscribe(String channel, int arg1) {
System.out.println("onUnsubscribe");
System.out.println("channel : " + channel);
System.out.println("arg1 : " + arg1);
}
//訂閱時
@Override
public void onSubscribe(String channel, int arg1) {
System.out.println("onSubscribe");
System.out.println("channel : " + channel);
System.out.println("arg1 : " + arg1);
}
//取消規則訂閱時
@Override
public void onPUnsubscribe(String arg0, int arg1) {
System.out.println("onPUnsubscribe");
System.out.println("arg0 : " + arg0);
System.out.println("arg1 : " + arg1);
}
//規則訂閱時
@Override
public void onPSubscribe(String arg0, int arg1) {
System.out.println("onPSubscribe");
System.out.println("arg0 : " + arg0);
System.out.println("arg1 : " + arg1);
}
//收到規則訂閱消息時
@Override
public void onPMessage(String arg0, String arg1, String arg2) {
System.out.println("onPMessage");
System.out.println("arg0 : " + arg0);
System.out.println("arg1 : " + arg1);
System.out.println("arg2 : " + arg2);
}
//收到訂閱消息時
@Override
public void onMessage(String channel, String message) {
System.out.println("onMessage");
System.out.println("channel : " + channel);
System.out.println("message : " + message);
this.unsubscribe();//取消訂閱
}
};
jedis.subscribe(jedisPubSub, "channel1");
}