NoSql數據庫的學習————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.命令操作

3.1. redis的數據結構:

	redis存儲的是:key,value格式的數據,其中key都是字符串,value有5種不同的數據結構
		 value的數據結構:
			1) 字符串類型 string
			2) 哈希類型 hash : map格式  
			3) 列表類型 list : linkedlist格式。支持重複元素
			4) 集合類型 set  : 不允許重複元素
			5) 有序集合類型 sortedset:不允許重複元素,且元素有順序

在這裏插入圖片描述

3.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.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

3.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: 刪除列表最右邊的元素,並將元素返回

在這裏插入圖片描述

3.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

3.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

3.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();
			    }
			}

6.案例:

只貼出了關鍵的代碼:完整的會在github中給出

在這裏插入圖片描述

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


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

mysql數據庫的文件:

CREATE DATABASE day23; -- 創建數據庫
USE day23; 			   -- 使用數據庫
CREATE TABLE province(   -- 創建表
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(20) NOT NULL
	
);
-- 插入數據
INSERT INTO province VALUES(NULL,'北京');
INSERT INTO province VALUES(NULL,'上海');
INSERT INTO province VALUES(NULL,'廣州');
INSERT INTO province VALUES(NULL,'陝西');

代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/jquery-3.3.1.min.js"></script>
    
    <script>
        $(function(){
            //發送ajax請求,加載所有省份數據
            $.get("ProvinceServlet",{},function (data) {
                // [{"id":1,"name":"北京"},{"id":2,"name":"上海"},{"id":3,"name":"廣州"},{"id":4,"name":"陝西"}]
                //1.獲取select
                var province=$("#province")
                //2.遍歷json數組
                $(data).each(function () {
                    //3.創建<option>
                    var option="<option name='"+this.id+"'>"+this.name+"</option>"
                    //4.調用select的append追加option
                    province.append(option);
                })
            });
        })
    </script>
</head>
<body>
    <select id="province">
        <option>--請選擇省份--</option>
    </select>
</body>
</html>

Servlet代碼:

package Per.Belief.RedisStudy.web.servlet;
import Per.Belief.RedisStudy.domain.Province;
import Per.Belief.RedisStudy.service.ProvinceService;
import Per.Belief.RedisStudy.service.impl.ProvinceServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@WebServlet("/ProvinceServlet")
public class ProvinceServlet extends HttpServlet {

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ProvinceService service=new ProvinceServiceImpl();
//        List<Province> list=service.findAll();
//        ObjectMapper mapper=new ObjectMapper();
//        String json=mapper.writeValueAsString(list);
        String json=service.findAllJson();


        System.out.println(json);
        resp.setContentType("application/json;charset=utf-8");
        resp.getWriter().write(json);
    }
}

ProvinceServiceIpml代碼:

package Per.Belief.RedisStudy.service.impl;

import Per.Belief.RedisStudy.dao.ProvinceDao;
import Per.Belief.RedisStudy.dao.impl.ProvinceDaoImpl;
import Per.Belief.RedisStudy.domain.Province;
import Per.Belief.RedisStudy.jedis.util.JedisPoolUtils;
import Per.Belief.RedisStudy.service.ProvinceService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import redis.clients.jedis.Jedis;

import java.util.List;

public class ProvinceServiceImpl implements ProvinceService {
    ProvinceDao pd=new ProvinceDaoImpl();
    @Override
    public List<Province> findAll() {
        return pd.findAll();
    }
    @Override
    public String findAllJson(){
        //1.先從redis中查詢數據
        //1.1獲取redis客戶端連接
        Jedis jedis= JedisPoolUtils.getJedis();
        String province_json=jedis.get("province");

        //2判斷 province_json 數據是否爲null
        if(province_json==null||province_json.length()==0){
            //redis中沒有數據
            System.out.println("redis中沒有數據,查詢數據庫!");

            //2.1從數據中查詢
            List<Province> list= pd.findAll();

            ObjectMapper mapper=new ObjectMapper();
            try {
                province_json=mapper.writeValueAsString(list);

            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            //2.3 將json數據存入redis
            jedis.set("province",province_json);
            //歸還連接
            jedis.close();

        }else{
            System.out.println("redis中有數據,查詢緩存!");
        }
        return province_json;
    }
}

效果:
第一次進入:
在這裏插入圖片描述
在這裏插入圖片描述
第二次進入:

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章