Redis學習

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");
	}



發佈了84 篇原創文章 · 獲贊 3 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章