數據庫討論(二):Redis在Java項目中使用(從安裝到運用)

目錄

本來是只寫在Java中的使用,有時間就多寫點(感謝帥哥同事的建議)。
- redis安裝與使用
- 基本數據類型介紹
-JEDIS基本數據操作
- 事務處理
- 主從複製
- 發佈訂閱


安裝和使用

簡介

Redis是一個使用C語言寫成的,開源key-value數據庫。它是一個小而美的數據庫,主要用在內存緩存中,讀寫性能極佳,緩存與簡單是其市場定位。
Redis與其他key-value的緩存產品有以下三個特點:
①、支持數據持久化。它可以將內存中的數據保存在磁盤中,重啓設備之後也能再次加載使用。
②、Redis不僅僅支持簡單的key-value類型數據,同時還提供list,set,zset,hash等數據結構的存儲。
③、Redis支持數據備份,即master-slave模式的數據備份

優勢

①、性能極高。Redis讀取的速度是110000次/s,寫入速度是81000次/s。
②、豐富的數據類型。Redis支持二進制案例的Strings,Lists,Hashes,Sets及Ordered Sets數據類型操作。
③、原子性。Redis的所有操作都是原子性的,同時Redis還支持對幾個操作全合併後的原子性執行
④、豐富的特性。Redis支持publish/subscribe,通知,key過期等等特性。

安裝
1.下載安裝包
1) windows安裝包下載。由於Redis官網不支持windows環境,請在gitHub下載最新的Redis版本的Redis-x64-3.2.100.msi文件。(同級目錄已提供安裝包)
2) Linux安裝包下載。登錄Redis官網下載
啓動
啓動本地服務有兩種選擇
一是直接在命令窗口啓動
1) 打開cmd命令窗口進入Redis的安裝路徑
(如果想方便的話可以把路徑配入環境變量)
2) 運行 redis-server.exe redis.windows.conf命令(redis version信息出現表示啓動成功)
3) 不要關掉啓動窗口,另起一個命令窗口。一樣需要先進入Redis的安裝路徑,
運行redis-cli.exe -h 127.0.0.1 -p 6379。連接信息結果出現所示則表示連接成功(輸入quit退出)
注:若是配置綁定爲本機IP地址或設置密碼,則上述命令應把127.0.0.1修改爲本機IP地址並加上密碼。例:redis-cli.exe -h 182.207.114.27 -p 6379 -a 123456
可視化工具
RedisDesktopManager工具(同級目錄已提供安裝包)
(自主選擇可以不裝,也可以選擇其它的可視化工具)
redis-desktop-manager-0.8.8.384.exe
安裝完成後點擊Connection to Redis Service
參數如下
Name:機器別名
Host:服務器IP (本機127.0.0.1)
Port:服務器端口 (默認6379)
Auth:若有密碼則輸入,若無則留空
點擊Test Connection,彈出成功連接即可

數據類型

概括

Redis支持五種數據類型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

key上的指令

1.exists key 測試指定key是否存在。返回1表示存在,0不存在
2.del key1 key2…keyN 刪除指定key。返回刪除key的數量,0表示沒有刪除任何key
3.type key 返回指定key的value類型。返回none表示不存在
4.keys pattern 返回匹配指定模式的所有key(支持* ? 等方式).如右圖例子
5.rename oldkey newkey 重命名key,如果newkey存在,將會被覆蓋。
6.renamenx oldkey newkey 同上。但是如果newkey存在,則返回失敗。
7.dbsize 返回當前數據庫的key數量
8.expire key seconds 爲key指定過期時間,單位是秒。返回1成功,0表示key不存在
9.ttl key 返回剩餘key的過期時間,單位是秒。返回-1表示該key沒有設置過期時間,-2表示 key不存在
10.select db-index 選擇連接數據庫。默認連接數據庫0,redis默認數據庫是0-15共16個
11.move key db-index 將key從當前數據庫移動到指定數據庫。
12.flushdb 刪除當前數據庫中的所有key。此方法不會失效,慎用。
13.flushall 刪除所有數據庫中的所有key。更加慎用。

String

1.String是redis最基本的類型,而且String類型是二進制安全的。
2.redis的String可以包含任何數據。包括jpg圖片或者序列化對象。
3.String最大上限是1G字節

String上的指令

1.set key value 設置key對應的String類型的value。返回1表示成功,0失敗
2.setnx key value 同上。但是如果key已經存在,返回0。
3.get key 獲取key對應的value值。如果key不存在返回nil。
4.getset key value 設置key的值,並返回key的舊值。如果key不存在則返回nil.
5.mget key1 key2…keyN 一次獲取多個key的值。如果對應key不存在返回nil
6.mset key1 value1…keyN valueN 一次設置多個key的值
7.msetnx key1 value1…keyN valueN 同上,但不會覆蓋已經存在的key
8.incr key 對key的值做++操作並返回新的值。注意,如果操作值不是int類型則返回錯誤
9.decr key 對key的值做–操作。
10.incrby key integer 加指定值,key不存在時新增key,默認value爲0
11.decrby key integer 減指定值
12.append key value 對指定key的字符串累加
13.substr key start end 返回截取過的key字符串值。注意,這裏並不修改key的值

list

redis的list類型其實就是一個每個子元素都是String類型的雙向鏈表。我們可以通過push,pop操作從鏈表的頭部或尾部添加刪除元素。這使得list既可以用作棧,也可以用作隊列。

list上的指令

1.lpush key string 在key對應的list頭部添加字符串元素。返回1表示成功。
2.lpush key string 同上在尾部添加元素。
3.llen key 獲取key對應的list長度。如果key不存在返回0,不是list報錯。
4.lrange key start end 返回指定區間內的元素。下標從0開始,-1表示最後一個元素.
5.ltrim key start end 截取list,保留指定區間內的元素。
6.lset key index value 設置list中指定下標的元素值。下標不存在報錯
7.lrem key count value 從key對應的list中刪除count個和value相同的元素。count爲0時全部刪除
8.lpop key 從list頭部刪除元素,並返回刪除元素
9.rpop key 從list尾部刪除元素,並返回刪除元素

**Set

1.redis的set是String的無序集合。
2.set元素最大可以包含 2的32次方-1 個元素 。
3.set是通過Hash table實現的,Hash table會隨着添加或者刪除自動調整大小
4.set除了基本的添加刪除操作,還實現了取交集並集功能

Set上的指令

1.sadd key member 添加一個String元素到key對應的set集合中
2.srem key member 從key對應的set中刪除指定元素。
3.spop key 刪除並返回key對應set中隨機一個元素。
4.srandmember key 返回key對應set中隨機一個元素,但不刪除元素。
5.smove source destination member 從source對應的set中移除member並添加到destination
6.scard key 返回set的元素個數。
7.sismember key member 判讀member是否在set中。
8.sinter key1 key2…keyN 返回指定key的交集
9.sinterstore destination key1 key2…keyN 同上。但會將交集放到destination中
10.sunion key1 key2…keyN 返回指定key的並集
11.sunionstore destination key1 key2…keyN同上。但會將交集放到destination中12.sdiff key1 key2…keyN返回指定key的差集
13.sdiffstore destination key1 key2…keyN同上。但會將交集放到destination中
14.smembers key 返回key對應set的所有元素,結果是無序的

Sorted set

和set一樣sorted set也是String類型元素的集合,不同的是每個元素都會關聯一個double類型的score。sorted set會按照score排序,這樣就可以有序的獲取集合中的元素。

Sorted set上的指令

1.zadd key score member 添加元素到集合,元素在集合中存在則更新對應score。
2.zrem key member 刪除指定元素,1表示成功,如果元素不存在返回0。
3.zincrby key incr member 增加對應member的score值,然後移動元素並保持集合有序。返回更新後的score值。
4.zrank key member 返回指定元素在集合中的排名(下標,非score)。集合中的元素是按score從小到大排序的
5.zrevrank key member 同上,但是集合中的元素是按score從大到小排序。
6.zrange key start end 類似lrange操作從集合中取指定區間的元素。
7.zrevrange key start end 同上,但是返回結果是按照score逆序的
8.zrangebyscore key min max 返回集合中score在給定區間的元素
9.zcount key min max 返回集合中score在給定區間的數量
10.zcard key 返回集合中元素個數
11.zscore key member 返回指定元素對應的score
12.zremrangebyrank key start stop 刪除集合中排名在指定區間的元素
13.zremrangebyscore key start stop 刪除集合中score在指定區間的元素

Hash

1.redis hash是一個string類型的field和value的映射表。
2.hash特別適合用於存儲對象。相較於將每個對象的每個字段存成單個string類型。將一個對象存儲在hash類型中會佔用更少的內存,並且可以更方便的存取整個對象。

Hash上的指令

: 1.hset key field value 設置hash field爲指定值,如果key不存在,則先創建
2.hget key field 獲取指定hash field的value。
3.hmget key field1 field2…fieldN 獲取全部指定hash field的value
4.hmset key field1 value1…fieldN valueN 同時設置hash的多個field
5.hincrby key field integer 將指定的hash field加上定值。
6.hexists key field 測試指定的field是否存在。
7.hdel key field 刪除指定的hash field。
8.hlen key 返回指定hash的field數量。
9.hkeys key 返回hash的所有field。
10.hvals key 返回hash的所有value
11.hgetall key 返回hash的所有field和value。

JEDIS基本數據操作

準備

Redis做一些直觀型的數據(我用的工具)
這裏寫圖片描述

連接

迴歸基本的數據庫訪問步驟
* 1、加載驅動: JadDecompiler JadclipseSourceMapper JadclipseClassFileEditor
* 2、建立連接 : Connection : URL + port (+password)
* 3、生成語句對象: new Jedis() 或者 JedisPool.getResource()
* 4、執行並返回: jedis.set(),jedis.add(),jedis.del()…
* 5、關閉流: jedis.close() 或者 JedisPool.returnResource()

package com.redis.util;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * jedis-2.9.0.jar(redis官方提供的Java架包)
 * commons-pool2-2.4.2.jar,連接池架包(由於我們項目使用到了連接池,所以使用此架包)
 * @author xxg20
 *
 */
public class RedisClient {
    // 本地服務器IP
    //private static String ADDR = "182.207.114.27";
    private static String ADDR ="127.0.0.1";
    // Redis端口號
    private static Integer PORT = 6379;
    // 訪問密碼
    //private static String AUTH = "123456";
    // 可用最大連接數
    private static Integer MAX_ACTIVE = 100;
    // 最大空閒連接數
    private static Integer MAX_IDLE = 10;
    // 最長等待時間
    private static Integer MAX_WAIT = 10000;
    // 超時時間
    private static Integer TIMEOUT = 30000;
    // 在獲取Redis連接時,自動檢測連接是否有效
    private static Boolean TEST_ON_BORROW = true;

    // 連接池
    private static JedisPool jedisPool;

    static{
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxTotal(MAX_ACTIVE);
            config.setMaxIdle(MAX_IDLE);
            config.setMaxWaitMillis(MAX_WAIT);
            config.setTestOnBorrow(TEST_ON_BORROW);
            //jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH);
            jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 獲取Redis資源
     * @return
     */
    public synchronized static Jedis getJedis(){
        try {
            if( jedisPool != null ){
                Jedis resource = jedisPool.getResource();
                return resource;
            }else{
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 釋放Redis資源
     */
    @SuppressWarnings("deprecation")
    public synchronized static void returnResource(Jedis jedis){
        if( jedisPool != null ){
            jedisPool.returnResource(jedis);
        }
    }

}
使用

基本數據類型的使用
* 1、String類型基本操作:
* 2、Hash類型基本操作:
* 3、List類型基本操作: 略
* 4、set類型基本操作: 略
* 5、Sorted set類型基本操作(有序集合): 略

/**
     * String類型基本操作
     */
    public static void testStringOpr(){
        Jedis jedis = RedisClient.getJedis();
        try {
            // 增加/修改
            jedis.set("testOne", "value");
            System.out.println("save String testOne:" + jedis.get("testOne"));
            // 拼接
            jedis.append("testOne", "1");
            System.out.println("append String testOne:" + jedis.get("testOne"));
            // 查詢
            String testOne = jedis.get("testOne");
            System.out.println("find String testOne:" + testOne);
            // 刪除
            jedis.del("testOne");
            System.out.println("delete String testOne:" + jedis.get("testOne"));
            // 判斷key是否存在
            boolean isNotExist = jedis.exists("testOne");
            System.out.println("isNotExist String testOne:" + isNotExist);

            // 若已知所存數據爲int型,則可調用下面方法進行加減操作
            jedis.set("testTwo", "0");
            System.out.println("old testTwo:" + jedis.get("testTwo"));
            jedis.incr("testTwo");
            System.out.println("new testTwo:" + jedis.get("testTwo"));
            jedis.del("testTwo");

            jedis.set("testThree", "value2");
            // 增加/更新 過期時間(以秒爲單位)
            jedis.expire("testThree", 30);
            // 查看剩餘生存時間
            System.out.println("testThree剩餘生命週期:"+jedis.ttl("testThree"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            RedisClient.returnResource(jedis);
        }
    }

再來一個

/**
     * Hash類型基本操作
     */
    public static void testHashOpr(){
        Jedis jedis = RedisClient.getJedis();
        try {
            // 整個增加Map(此操作會直接覆蓋原有key對應的Map)
            Map<String, String> redisMap = new HashMap<String, String>();
            redisMap.put("name","zhangsan");
            redisMap.put("age","28");
            redisMap.put("sex","男");
            jedis.hmset("redisMap",redisMap);
            System.out.println("add Hash redisMap:" + jedis.hgetAll("redisMap"));
            // 判斷Hash的field是否存在
            boolean isNotExist = jedis.hexists("redisMap", "address");
            System.out.println("isNotExist String test1:" + isNotExist);
            // 獲取Hash的長度
            long hlen = jedis.hlen("redisMap");
            System.out.println("get Hash len:" + hlen);
            // 增加/修改
            jedis.hset("redisMap", "address", "廣東深圳小剛家");
            System.out.println("save Hash redisMap:" + jedis.hgetAll("redisMap"));
            // 查詢
            String name = jedis.hget("redisMap", "name");
            System.out.println("find Hash redisMap name:" + name);
            // 對int類型進行增減操作
            jedis.hincrBy("redisMap", "age", 2);
            System.out.println("intAdd Hash redisMap age:" + jedis.hget("redisMap", "age"));
            // 刪除
            jedis.hdel("redisMap", "address");
            System.out.println("delete Hash redisMap address:" + jedis.hget("redisMap", "address"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            RedisClient.returnResource(jedis);
        }
    }

事務處理

介紹

事務(Transaction)是指作爲單個邏輯工作單元執行的一系列操作。事務必須滿足ACID原則(原子性、一致性、隔離性和持久性)。
事務可能包括1~N條命令,當這些命令被作爲事務處理時,將會順序執行這些命令直到完成,並返回結果,如果中途有命令失敗,則會回滾所有操作。

Redis中的事務

這裏寫圖片描述

實現

/**
     * 事務控制
     */
    public static void testTransaction(){
        Jedis jedis = RedisClient.getJedis();
        // 開啓事務
        Transaction tx = jedis.multi();
        // 異常標誌
        boolean errFlag = false;
        try {
            // 設置 redis 字符串數據
            tx.set("test1", "value1");
//          int i = 6/0;// java異常
            tx.set("test2", "value2");
            // (在Jedis內部方法異常時,不會直接報錯。而是同樣放在隊列中,在事務提交時,不提交該條錯誤信息,其他正常隊列正常提交)
            tx.incr("test1");// 對字符串進行累加操作:Jedis內部方法異常 
            System.out.println("正常提交事務");
            List<Object> list = tx.exec();// 提交事務
            for (int i = 0; i < list.size(); i++) {
                System.out.println("find List list(" + i + "):" + list.get(i));
            }
        } catch (Exception e) {
            errFlag = true;
//          tx.discard();// 發生異常時取消事務(注意:如果在取消)
            e.printStackTrace();
        } finally{
            if(errFlag){
                System.out.println("發生異常時提交事務");
                tx.exec();// 提交事務
            }
            RedisClient.returnResource(jedis);
        }
    }

主從複製

概況

1、redis的複製功能是支持多個數據庫之間的數據同步。一類是主數據庫(master)一類是從數據庫(slave),主數據庫可以進行讀寫操作,當發生寫操作的時候自動將數據同步到從數據庫,而從數據庫一般是隻讀的,並接收主數據庫同步過來的數據,一個主數據庫可以有多個從數據庫,而一個從數據庫只能有一個主數據庫。

2、通過redis的複製功能可以很好的實現數據庫的讀寫分離,提高服務器的負載能力。主數據庫主要進行寫操作,而從數據庫負責讀操作。

過程

1:當一個從數據庫啓動時,會向主數據庫發送sync命令,
2:主數據庫接收到sync命令後會開始在後臺保存快照(執行rdb操作),並將保存期間接收到的命令緩存起來
3:當快照完成後,redis會將快照文件和所有緩存的命令發送給從數據庫。
4:從數據庫收到後,會載入快照文件並執行收到的緩存的命令。支持斷點續傳。

sentinel功能

redis的sentinel系統用於管理多個redis服務器,該系統主要執行三個任務:監控、提醒、自動故障轉移。
1、監控(Monitoring): Redis Sentinel實時監控主服務器和從服務器運行狀態,並且實現自動切換。
2、提醒(Notification):當被監控的某個 Redis 服務器出現問題時, Redis Sentinel 可以向系統管理員發送通知, 也可以通過 API 向其他程序發送通知。
3、自動故障轉移(Automatic failover): 當一個主服務器不能正常工作時,Redis Sentinel 可以將一個從服務器升級爲主服務器, 並對其他從服務器進行配置,讓它們使用新的主服務器。當應用程序連接Redis 服務器時, Redis Sentinel會告之新的主服務器地址和端口。

集羣中的主從複製

redis集羣是一個無中心的分佈式Redis存儲架構,可以在多個節點之間進行數據共享,解決了Redis高可用、可擴展等問題。redis集羣提供了以下兩個好處
1、將數據自動切分(split)到多個節點
2、當集羣中的某一個節點故障時,redis還可以繼續處理客戶端的請求。
一個 Redis 集羣包含 16384 個哈希槽(hash slot),數據庫中的每個數據都屬於這16384個哈希槽中的一個。集羣使用公式 CRC16(key) % 16384 來計算鍵 key 屬於哪個槽。集羣中的每一個節點負責處理一部分哈希槽。

集羣中的每個節點都有1個至N個複製品,其中一個爲主節點,其餘的爲從節點,如果主節點下線了,集羣就會把這個主節點的一個從節點設置爲新的主節點,繼續工作。這樣集羣就不會因爲一個主節點的下線而無法正常工作。

發佈訂閱

說明
發佈訂閱(PUB/SUB)是一種消息通信模式。訂閱者可以通過subscribe和psubscribe命令向redis server訂閱自己感興趣的消息類型,redis將消息類型稱爲通道(channel)。當發佈者通過publish命令向redis server發送特定類型的消息時,訂閱該消息類型的全部client都會收到此消息。這裏消息的傳遞是多對多的。一個client可以訂閱多個channel,也可以向多個channel發送消息。
訂閱發佈常用命令如下:
1.publish 發佈消息
2.subscribe 訂閱消息(單個)
3.unsubscribe 取消訂閱
4.psubscribe 訂閱匹配消息
5.punsubscribe 取消訂閱匹配消息
非管道處理
Redis是一個CS模式的tcp server,使用和http類似的請求響應協議。一個client可以通過一個socket連接發起多個請求命令。每個請求命令發出後client通常會阻塞並等待redis服務處理,redis處理完請求命令後會將結果通過響應報文返回給client。基本的通信過程如下:
Client: INCR X
Server: 1
Client: INCR X
Server: 2
Client: INCR X
Server: 3
Client: INCR X
Server: 4

上面四個命令需要8個tcp報文才能完成。由於通信會有網絡延遲,加入從client和server之間的包傳輸時間需要0.125秒。那麼上面的四個命令8個報文至少需要1秒才能完成。

管道(pipeline)處理
利用管道的方式從client打包多條命令一起發出,不需要等待單條命令的響應返回,而redis服務端在處理完多條命令後會將多條命令的處理結果打包到一起返回給客戶端。通信過程如下:
Client: INCR X
Client: INCR X
Client: INCR X
Client: INCR X
Server: 1
Server: 2
Server: 3
Server: 4

相當於管道處理將與Redis服務器之間的溝通結構由
request-> response、request-> response、request-> response…
改爲
request、、request、request->response、response、response…
可在數據量較大時減輕服務器壓力,增加數據訪問效率

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