1. 程式人生 > 實用技巧 >Redis - 快取技術

Redis - 快取技術

本文內容

1. redis
	1. 概念
	2. 下載安裝
	3. 命令操作
		1. 資料結構
	4. 持久化操作
	5. 使用Java客戶端操作redis

Redis

1. 概念: redis是一款高效能的NOSQL系列的非關係型資料庫


	1.1.什麼是NOSQL
		NoSQL(NoSQL = Not Only SQL),意即“不僅僅是SQL”,是一項全新的資料庫理念,泛指非關係型的資料庫。
		隨著網際網路web2.0網站的興起,傳統的關係資料庫在應付web2.0網站,特別是超大規模和高併發的SNS型別的web2.0純動態網站已經顯得力不從心,暴露了很多難以克服的問題,而非關係型的資料庫則由於其本身的特點得到了非常迅速的發展。NoSQL資料庫的產生就是為了解決大規模資料集合多重資料種類帶來的挑戰,尤其是大資料應用難題。

		1.1.1.	NOSQL和關係型資料庫比較
			優點:
				1)成本:nosql資料庫簡單易部署,基本都是開源軟體,不需要像使用oracle那樣花費大量成本購買使用,相比關係型資料庫價格便宜。
				2)查詢速度:nosql資料庫將資料儲存於快取之中,關係型資料庫將資料儲存在硬碟中,自然查詢速度遠不及nosql資料庫。
				3)儲存資料的格式:nosql的儲存格式是key,value形式、文件形式、圖片形式等等,所以可以儲存基礎型別以及物件或者是集合等各種格式,而資料庫則只支援基礎型別。
				4)擴充套件性:關係型資料庫有類似join這樣的多表查詢機制的限制導致擴充套件很艱難。

			缺點:
				1)維護的工具和資料有限,因為nosql是屬於新的技術,不能和關係型資料庫10幾年的技術同日而語。
				2)不提供對sql的支援,如果不支援sql這樣的工業標準,將產生一定使用者的學習和使用成本。
				3)不提供關係型資料庫對事務的處理。

		1.1.2.	非關係型資料庫的優勢:
			1)效能NOSQL是基於鍵值對的,可以想象成表中的主鍵和值的對應關係,而且不需要經過SQL層的解析,所以效能非常高。
			2)可擴充套件性同樣也是因為基於鍵值對,資料之間沒有耦合性,所以非常容易水平擴充套件。

		1.1.3.	關係型資料庫的優勢:
			1)複雜查詢可以用SQL語句方便的在一個表以及多個表之間做非常複雜的資料查詢。
			2)事務支援使得對於安全效能很高的資料訪問要求得以實現。對於這兩類資料庫,對方的優勢就是自己的弱勢,反之亦然。

		1.1.4.	總結
			關係型資料庫與NoSQL資料庫並非對立而是互補的關係,即通常情況下使用關係型資料庫,在適合使用NoSQL的時候使用NoSQL資料庫,
			讓NoSQL資料庫對關係型資料庫的不足進行彌補。
			一般會將資料儲存在關係型資料庫中,在nosql資料庫中備份儲存關係型資料庫的資料

	1.2.主流的NOSQL產品
		•	鍵值(Key-Value)儲存資料庫
				相關產品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
				典型應用: 內容快取,主要用於處理大量資料的高訪問負載。 
				資料模型: 一系列鍵值對
				優勢: 快速查詢
				劣勢: 儲存的資料缺少結構化
		•	列儲存資料庫
				相關產品:Cassandra, HBase, Riak
				典型應用:分散式的檔案系統
				資料模型:以列簇式儲存,將同一列資料存在一起
				優勢:查詢速度快,可擴充套件性強,更容易進行分散式擴充套件
				劣勢:功能相對侷限
		•	文件型資料庫
				相關產品:CouchDB、MongoDB
				典型應用:Web應用(與Key-Value類似,Value是結構化的)
				資料模型: 一系列鍵值對
				優勢:資料結構要求不嚴格
				劣勢: 查詢效能不高,而且缺乏統一的查詢語法
		•	圖形(Graph)資料庫
				相關資料庫:Neo4J、InfoGrid、Infinite Graph
				典型應用:社交網路
				資料模型:圖結構
				優勢:利用圖結構相關演算法。
				劣勢:需要對整個圖做計算才能得出結果,不容易做分散式的叢集方案。
	1.3 什麼是Redis
		Redis是用C語言開發的一個開源的高效能鍵值對(key-value)資料庫,官方提供測試資料,50個併發執行100000個請求,讀的速度是110000次/s,寫的速度是81000次/s ,且Redis通過提供多種鍵值資料型別來適應不同場景下的儲存需求,目前為止Redis支援的鍵值資料型別如下:
			1) 字串型別 string
			2) 雜湊型別 hash
			3) 列表型別 list
			4) 集合型別 set
			5) 有序集合型別 sortedset
		1.3.1 redis的應用場景
			•	快取(資料查詢、短連線、新聞內容、商品內容等等)
			•	聊天室的線上好友列表
			•	任務佇列。(秒殺、搶購、12306等等)
			•	應用排行榜
			•	網站訪問統計
			•	資料過期處理(可以精確到毫秒
			•	分散式叢集架構中的session分離


2. 下載安裝
	1. 官網:https://redis.io
	2. 中文網:http://www.redis.net.cn/
	3. 解壓直接可以使用:
		* redis.windows.conf:配置檔案
		* redis-cli.exe:redis的客戶端
		* redis-server.exe:redis伺服器端
	
3. 命令操作
	1. redis的資料結構:
		* redis儲存的是:key,value格式的資料,其中key都是字串,value有5種不同的資料結構
			* value的資料結構:
				1) 字串型別 string
				2) 雜湊型別 hash : map格式  
				3) 列表型別 list : linkedlist格式。支援重複元素
				4) 集合型別 set  : 不允許重複元素
				5) 有序集合型別 sortedset:不允許重複元素,且元素有順序

	2. 字串型別 string
		1. 儲存: set key value
			127.0.0.1:6379> set username zhangsan
			OK
		2. 獲取: get key
			127.0.0.1:6379> get username
			"zhangsan"
		3. 刪除: del key
			127.0.0.1:6379> del age
			(integer) 1
	3. 雜湊型別 hash
		1. 儲存: hset key field value
			127.0.0.1:6379> hset myhash username lisi
			(integer) 1
			127.0.0.1:6379> hset myhash password 123
			(integer) 1
		2. 獲取: 
			* hget key field: 獲取指定的field對應的值
				127.0.0.1:6379> hget myhash username
				"lisi"
			* hgetall key:獲取所有的field和value
				127.0.0.1:6379> hgetall myhash
				1) "username"
				2) "lisi"
				3) "password"
				4) "123"
				
		3. 刪除: hdel key field
			127.0.0.1:6379> hdel myhash username
			(integer) 1
	
	4. 列表型別 list:可以新增一個元素到列表的頭部(左邊)或者尾部(右邊)
		1. 新增:
			1. lpush key value: 將元素加入列表左表
				
			2. rpush key value:將元素加入列表右邊
				
				127.0.0.1:6379> lpush myList a
				(integer) 1
				127.0.0.1:6379> lpush myList b
				(integer) 2
				127.0.0.1:6379> rpush myList c
				(integer) 3
		2. 獲取:
			* lrange key start end :範圍獲取
				127.0.0.1:6379> lrange myList 0 -1
				1) "b"
				2) "a"
				3) "c"
		3. 刪除:
			* lpop key: 刪除列表最左邊的元素,並將元素返回
			* rpop key: 刪除列表最右邊的元素,並將元素返回

	5. 集合型別 set : 不允許重複元素
		1. 儲存:sadd key value
			127.0.0.1:6379> sadd myset a
			(integer) 1
			127.0.0.1:6379> sadd myset a
			(integer) 0
		2. 獲取:smembers key:獲取set集合中所有元素
			127.0.0.1:6379> smembers myset
			1) "a"
		3. 刪除:srem key value:刪除set集合中的某個元素	
			127.0.0.1:6379> srem myset a
			(integer) 1
	6. 有序集合型別 sortedset:不允許重複元素,且元素有順序.每個元素都會關聯一個double型別的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。

		1. 儲存:zadd key score value
			127.0.0.1:6379> zadd mysort 60 zhangsan
			(integer) 1
			127.0.0.1:6379> zadd mysort 50 lisi
			(integer) 1
			127.0.0.1:6379> zadd mysort 80 wangwu
			(integer) 1
		2. 獲取:zrange key start end [withscores]
			127.0.0.1:6379> zrange mysort 0 -1
			1) "lisi"
			2) "zhangsan"
			3) "wangwu"

			127.0.0.1:6379> zrange mysort 0 -1 withscores
			1) "zhangsan"
			2) "60"
			3) "wangwu"
			4) "80"
			5) "lisi"
			6) "500"
		3. 刪除:zrem key value
			127.0.0.1:6379> zrem mysort lisi
			(integer) 1

	7. 通用命令
		1. keys * : 查詢所有的鍵
		2. type key : 獲取鍵對應的value的型別
		3. del key:刪除指定的key value


4. 持久化
	1. redis是一個記憶體資料庫,當redis伺服器重啟,獲取電腦重啟,資料會丟失,我們可以將redis記憶體中的資料持久化儲存到硬碟的檔案中。
	2. redis持久化機制:
		1. RDB:預設方式,不需要進行配置,預設就使用這種機制
			* 在一定的間隔時間中,檢測key的變化情況,然後持久化資料
			1. 編輯redis.windwos.conf檔案
				#   after 900 sec (15 min) if at least 1 key changed
				save 900 1
				#   after 300 sec (5 min) if at least 10 keys changed
				save 300 10
				#   after 60 sec if at least 10000 keys changed
				save 60 10000
				
			2. 重新啟動redis伺服器,並指定配置檔名稱
				D:\JavaWeb2018\day23_redis\資料\redis\windows-64\redis-2.8.9>redis-server.exe redis.windows.conf	
			
		2. AOF:日誌記錄的方式,可以記錄每一條命令的操作。可以每一次命令操作後,持久化資料
			1. 編輯redis.windwos.conf檔案
				appendonly no(關閉aof) --> appendonly yes (開啟aof)
				
				# appendfsync always : 每一次操作都進行持久化
				appendfsync everysec : 每隔一秒進行一次持久化
				# appendfsync no	 : 不進行持久化

5. Java客戶端 Jedis
	* Jedis: 一款java操作redis資料庫的工具.
	* 使用步驟:
		1. 下載jedis的jar包
		2. 使用
			//1. 獲取連線
    		Jedis jedis = new Jedis("localhost",6379);
   			//2. 操作
   			jedis.set("username","zhangsan");
    		//3. 關閉連線
    		jedis.close();

	
	* Jedis操作各種redis中的資料結構
		1) 字串型別 string
			set
			get
			
			 //1. 獲取連線
	        Jedis jedis = new Jedis();//如果使用空參構造,預設值 "localhost",6379埠
	        //2. 操作
	        //儲存
	        jedis.set("username","zhangsan");
	        //獲取
	        String username = jedis.get("username");
	        System.out.println(username);
	
	        //可以使用setex()方法儲存可以指定過期時間的 key value
	        jedis.setex("activecode",20,"hehe");//將activecode:hehe鍵值對存入redis,並且20秒後自動刪除該鍵值對
	
	        //3. 關閉連線
	        jedis.close();

		2) 雜湊型別 hash : map格式  
			hset
			hget
			hgetAll
			//1. 獲取連線
	        Jedis jedis = new Jedis();//如果使用空參構造,預設值 "localhost",6379埠
	        //2. 操作
	        // 儲存hash
	        jedis.hset("user","name","lisi");
	        jedis.hset("user","age","23");
	        jedis.hset("user","gender","female");
	
	        // 獲取hash
	        String name = jedis.hget("user", "name");
	        System.out.println(name);
	
	
	        // 獲取hash的所有map中的資料
	        Map<String, String> user = jedis.hgetAll("user");
	
	        // keyset
	        Set<String> keySet = user.keySet();
	        for (String key : keySet) {
	            //獲取value
	            String value = user.get(key);
	            System.out.println(key + ":" + value);
	        }
	
	        //3. 關閉連線
	        jedis.close();


		3) 列表型別 list : linkedlist格式。支援重複元素
			lpush / rpush
			lpop / rpop
			lrange start end : 範圍獲取
			
			 //1. 獲取連線
	        Jedis jedis = new Jedis();//如果使用空參構造,預設值 "localhost",6379埠
	        //2. 操作
	        // list 儲存
	        jedis.lpush("mylist","a","b","c");//從左邊存
	        jedis.rpush("mylist","a","b","c");//從右邊存
	
	        // list 範圍獲取
	        List<String> mylist = jedis.lrange("mylist", 0, -1);
	        System.out.println(mylist);
	        
	        // list 彈出
	        String element1 = jedis.lpop("mylist");//c
	        System.out.println(element1);
	
	        String element2 = jedis.rpop("mylist");//c
	        System.out.println(element2);
	
	        // list 範圍獲取
	        List<String> mylist2 = jedis.lrange("mylist", 0, -1);
	        System.out.println(mylist2);
	
	        //3. 關閉連線
	        jedis.close();


		4) 集合型別 set  : 不允許重複元素
			sadd
			smembers:獲取所有元素

			//1. 獲取連線
	        Jedis jedis = new Jedis();//如果使用空參構造,預設值 "localhost",6379埠
	        //2. 操作
	
	
	        // set 儲存
	        jedis.sadd("myset","java","php","c++");
	
	        // set 獲取
	        Set<String> myset = jedis.smembers("myset");
	        System.out.println(myset);
	
	        //3. 關閉連線
	        jedis.close();
		5) 有序集合型別 sortedset:不允許重複元素,且元素有順序
			zadd
			zrange

			//1. 獲取連線
	        Jedis jedis = new Jedis();//如果使用空參構造,預設值 "localhost",6379埠
	        //2. 操作
	        // sortedset 儲存
	        jedis.zadd("mysortedset",3,"亞瑟");
	        jedis.zadd("mysortedset",30,"後裔");
	        jedis.zadd("mysortedset",55,"孫悟空");
	
	        // sortedset 獲取
	        Set<String> mysortedset = jedis.zrange("mysortedset", 0, -1);
	
	        System.out.println(mysortedset);
	
	
	        //3. 關閉連線
	        jedis.close();


	
	* jedis連線池: JedisPool
		* 使用:
			1. 建立JedisPool連線池物件
			2. 呼叫方法 getResource()方法獲取Jedis連線
				//0.建立一個配置物件
		        JedisPoolConfig config = new JedisPoolConfig();
		        config.setMaxTotal(50);
		        config.setMaxIdle(10);
		
		        //1.建立Jedis連線池物件
		        JedisPool jedisPool = new JedisPool(config,"localhost",6379);
		
		        //2.獲取連線
		        Jedis jedis = jedisPool.getResource();
		        //3. 使用
		        jedis.set("hehe","heihei");
		
		
		        //4. 關閉 歸還到連線池中
		        jedis.close();
		
		* 連線池工具類
			public class JedisPoolUtils {

			    private static JedisPool jedisPool;
			
			    static{
			        //讀取配置檔案
			        InputStream is = JedisPoolUtils.class.getClassLoader().getResourceAsStream("jedis.properties");
			        //建立Properties物件
			        Properties pro = new Properties();
			        //關聯檔案
			        try {
			            pro.load(is);
			        } catch (IOException e) {
			            e.printStackTrace();
			        }
			        //獲取資料,設定到JedisPoolConfig中
			        JedisPoolConfig config = new JedisPoolConfig();
			        config.setMaxTotal(Integer.parseInt(pro.getProperty("maxTotal")));
			        config.setMaxIdle(Integer.parseInt(pro.getProperty("maxIdle")));
			
			        //初始化JedisPool
			        jedisPool = new JedisPool(config,pro.getProperty("host"),Integer.parseInt(pro.getProperty("port")));
			
			
			
			    }
			
			
			    /**
			     * 獲取連線方法
			     */
			    public static Jedis getJedis(){
			        return jedisPool.getResource();
			    }
			}

案例:

案例需求:
	1. 提供index.html頁面,頁面中有一個省份 下拉列表
	2. 當 頁面載入完成後 傳送ajax請求,載入所有省份


* 注意:使用redis快取一些不經常發生變化的資料。
	* 資料庫的資料一旦發生改變,則需要更新快取。
		* 資料庫的表執行 增刪改的相關操作,需要將redis快取資料情況,再次存入
		* 在service對應的增刪改方法中,將redis資料刪除。