1. 程式人生 > >REDIS基礎學習筆記

REDIS基礎學習筆記

後端 毫秒 賦值 ora centos7 如果 基礎用法 del 數據庫

一. 背景說明

學習每一種新技術之前,我都會盡量提醒自己從這三個方面依次遞進的去學:是什麽,能幹嘛?為什麽用它?怎麽用?
Redis是什麽?一項基於分布式緩存的nosql數據庫技術。能幹嘛?做分布式緩存唄。
為什麽用它?一般來說,每一項新技術的出現,都有其歷史背景及使命(要解決的問題),技術來源於問題。為什麽已經有了傳統的關系型數據庫,還要非關系型數據庫幹啥?毋庸置疑,肯定是原有的老技術有其無法避免的缺點及弊端,即使很有可能總體來說它已經很優秀了。傳統的關系型數據庫,如Oracle、Mysql、SQLServer、DB2,基本上都是把數據主要存放位置放在磁盤上,在一些大數據量,高並發的情況下,磁盤的讀寫速度已經無法滿足需求了,我們迫切需要一個基於更快的物理硬件如內存的數據庫。於是Redis就應運而生了。

怎麽用?後面自然會說,在這之前先按正常節奏一步步的了解並同時學習用法。

二. 用法

2.1 安裝和啟動

首先需要註意下的是,據說Redis在Windows Server中的性能表現要比Linux中差很多。所以,條件允許的話,盡可能的選擇Linux平臺。如果選擇了Linux平臺的話,需要另外註意的一個事情是:Redis是用C語言編寫的,而我們下載的Redis一般源碼安裝程序,所以在這之前你需要確保系統裏有合適的編譯器。gcc或gcc-c++都行,我的系統(CentOS7)裏是之前自己都安裝了:

[root@qingxin ~]# rpm -qa gcc*
gcc-4.8.5-28.el7_5.1.x86_64
gcc-c++-4.8.5-28.el7_5.1.x86_64

沒有的話先自行安裝下:

yum install gcc gcc-c++

環境都準備好了之後就可以用下載下來的文件開始安裝了:
解壓

[root@qingxin software]# tar -zxvf redis-4.0.9.tar.gz

進入到解壓出來的目錄裏面,編譯

[root@qingxin redis-4.0.9]# make

安裝到指定目錄

[root@qingxin redis-4.0.9]# make PREFIX=/usr/local/redis-4.0.9/ install

復制配置文件到安裝目錄

[root@qingxin redis-4.0.9]# cp redis.conf /usr/local/redis-4.0.9/bin/

到這裏我們的Redis安裝基本就算完成了,接下來可以嘗試啟動Redis了。
關於Redis的啟動主要是要知道兩種啟動方式:前端啟動和後端啟動。Redis默認使用前端啟動方式,這樣當你運行redis-server啟動了之後,它會一直卡在哪裏,必須另外開一個session使用redis-cli去訪問它,這樣挺麻煩的。所以推薦後端啟動方式:修改redis.conf配置文件,把daemonize由no改成yes,這樣redis-server就可以在後臺跑著了。(為了方便建議將Redis安裝目錄加入PATH裏)

[root@qingxin bin]# redis-server redis.conf

檢查是否啟動成功:

[root@qingxin bin]# ps -aux|grep redis-server

訪問直接使用redis-cli,帶密碼的話使用-a參數。關閉也是使用redis-cli,只是多加一個shutdown。

2.2 Jedis的使用

Redis本身的使用來講主要就是一堆命令。在說哪些之前,先說說Jedis。Jedis對於redis就類似於JDBC於Mysql、Oracle。都是對數據庫訪問的接口,在Java中要想直接操作數據庫都需要通過這類接口。

  1. 單實例連接
    首先說明下,因為還沒說其他數據類型,所以暫時只用String類型的做測試。這一個和下一個例子我們只引入jedis,commons-pool,junit做測試:
    技術分享圖片

單實例連接比較簡單,做完之後,我們會看下使用連接池方式怎麽寫。

package com.zqx;
import org.junit.Test;
import redis.clients.jedis.Jedis;
public class TestJedis {
@Test
public void testJedisSingle(){
Jedis jedis = new Jedis("192.168.15.142",6379);
jedis.set("username","zqx");
String user = jedis.get("username") + "," + jedis.get("country");
System.out.println(user);
jedis.close();
}
}

這裏需要註意,因為我這裏的測試程序是跑在windows系統裏的。而我的redis-server運行在我的Linux虛擬機裏,所以要想看到測試效果,需要先確保能從windows訪問到虛擬機裏的redis。要做到這一點,需要註意兩個問題:
一. redis.conf裏的bind項改成0.0.0.0以讓任意IP都能訪問
二. Linux的防火墻開啟對redis默認6379端口的訪問,

[root@qingxin bin]# iptables -A INPUT -ptcp --dport 6379 -j ACCEPT

因為centosos7默認使用firewalld管理了,我的系統是centos7的,所以用下面這個

[root@qingxin bin]# firewall-cmd --permanent --zone=public --add-port=6379/tcp

改完記得重啟一下服務:

[root@qingxin bin]# systemctl restart firewalld

[root@qingxin bin]# service iptables restart

做完這些本地測試下能否正常連接:
技術分享圖片

代碼測試結果:
技術分享圖片

驗證一下數據是否寫入到redis-server了:
技術分享圖片

  1. 連接池連接
    連接池方式可以實現對連接的共享和復用,以提高連接資源的利用率。

     @Test
     public void testJedisPools(){
         JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
         //最大連接數
         jedisPoolConfig.setMaxTotal(30);
         //最大連接空閑數,即使沒有任何連接也可以保留的連接數
         jedisPoolConfig.setMaxIdle(2);
    
         JedisPool jedisPool = new JedisPool(jedisPoolConfig,"192.168.15.142",6379);
    
         Jedis jedis = null;
         try{
             jedis = jedisPool.getResource();
             jedis.set("username1","zhuqingxin");
             String user1 = jedis.get("username1")+","+jedis.get("country");
             System.out.println(user1);
         }catch (Exception e){
             throw e;
         }finally {
             if(jedis != null){
                 jedis.close();
             }
         }
     }

測試結果:
技術分享圖片

驗證一下寫入是否成功:
技術分享圖片

  1. 整合Spring
    首先第一步肯定是把Spring核心該導的包都導入進來了:
    技術分享圖片
    然後就可以寫測試代碼做測試了:
    ApplicationContext.xml


     <!-- 連接池配置 -->
     <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
         <!-- 最大連接數 -->
         <property name="maxTotal" value="30" />
         <!-- 最大空閑連接數 -->
         <property name="maxIdle" value="10" />
         <!-- 每次釋放連接的最大數目 -->
         <property name="numTestsPerEvictionRun" value="1024" />
         <!-- 釋放連接的掃描間隔(毫秒) -->
         <property name="timeBetweenEvictionRunsMillis" value="30000" />
         <!-- 連接最小空閑時間 -->
         <property name="minEvictableIdleTimeMillis" value="1800000" />
         <!-- 連接空閑多久後釋放, 當空閑時間>該值 且 空閑連接>最大空閑連接數 時直接釋放 -->
         <property name="softMinEvictableIdleTimeMillis" value="10000" />
         <!-- 獲取連接時的最大等待毫秒數,小於零:阻塞不確定的時間,默認-1 -->
         <property name="maxWaitMillis" value="1500" />
         <!-- 在獲取連接的時候檢查有效性, 默認false -->
         <property name="testOnBorrow" value="true" />
         <!-- 在空閑時檢查有效性, 默認false -->
         <property name="testWhileIdle" value="true" />
         <!-- 連接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 默認true -->
         <property name="blockWhenExhausted" value="false" />
     </bean>
    
     <!--配置 jedisPool -->
     <bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="close">
         <constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg>
         <constructor-arg name="host" value="192.168.15.142"/>
         <constructor-arg name="port" value="6379"/>
     </bean>

這種東西還是建議本地保存一份,用的時候copy過來改改就能用了。
TestSpringJedis.java

package com.zqx;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class TestSpringJedis {

    private ApplicationContext applicationContext;

    @Before
    public void init(){
        applicationContext = new ClassPathXmlApplicationContext("classpath:ApplicationContext.xml");
    }

    @Test
    public void testJedisPool(){
        JedisPool pool = (JedisPool) applicationContext.getBean("jedisPool");
        Jedis jedis = null;
        try{
            jedis = pool.getResource();
            jedis.set("username2","11242");
            String user2= jedis.get("username2") + "," + jedis.get("country");
            System.out.println(user2);
        }catch (Exception e){
            throw e;
        }finally {
            if (jedis!=null){
                jedis.close();
            }
        }
    }

}

測試結果:
技術分享圖片

驗證一下寫入是否成功:
技術分享圖片

另外,說一下,這裏的連接和連接池你不關閉,Spring也會幫你關的。
好的,到這裏,Jedis的基礎用法就差不多了,下面看看Redis的數據類型。

2.3 Redis數據類型

總的來說,Redis有5種數據類型:
String 字符串
list 列表
hash 哈希
set 集合 無序且不允許重復
zset 有序集合 有序不允許重復
這裏我們不扣概念,重點關註命令怎麽用。

  1. String類型
    其實我們前面一直用的就是String,主要就是用了set,get命令。再總的走一遍:
    技術分享圖片

Set是賦值,get是取值。getset是先取當前值,再賦值。Del是刪除,更新就再set一次。
除了這些還有一些實用的命令
遞增遞減(incr,decr,incrby,decrby):
技術分享圖片
追加(append):
技術分享圖片
獲取長度(strlen):
技術分享圖片
同時設置獲取多個值(mset,mget):
技術分享圖片
2 Hash類型
Hash表類似於關系型數據庫裏的一條記錄。一條記錄對應一個Hash類型的key,下面可以有多個屬性。
技術分享圖片
基本的hget,hset獲取設置單個field值,hmget,hmset操作多個屬性。Hgetall獲取所有屬性,hdel刪除一個或多個屬性。其他還有hkeys,hvals只獲取key,只獲取值。
技術分享圖片
3 List類型
Redis裏面的list類似於LinkedList,就像一個雙向循環鏈表。這個時候對其添加和刪除的操作就有幾種區別,在表頭插入,表尾插入,表頭刪除,表尾刪除。或者其實按它的實際命令來看,我們可以也可以把它看成一個雙向棧,對應的操作就變成了正向入棧,反向入棧,正向出棧,反向出棧了。

192.168.15.142:6379> lpush lkey1 1 2 3
(integer) 3
192.168.15.142:6379> lrange lkey1 0 -1
1) "3"
2) "2"
3) "1"
192.168.15.142:6379> rpush lkey1 4 5 6
(integer) 6
192.168.15.142:6379> lrange lkey1 0 -1
1) "3"
2) "2"
3) "1"
4) "4"
5) "5"
6) "6"
192.168.15.142:6379> llen lkey1
(integer) 6

基礎命令就是lpush,rpush,對應正向入棧,反向入棧。對應的出棧操作就是lpop,rpop,都是類似的就不演示了,llen可以獲取總數,還有lrem可以移除元素。其實到這裏已經能發現,redis的命令不少,全部記下來也不容易,但就和學linux命令一樣,我們很多時候還是需要用到什麽查什麽,但是基本的一些命令還是最好能記下來。
需要註意一下的是lrange,我試了下redis裏list好像只能正向遍歷,沒有rrange的說法。lrange的用法,需要自己去試下就明白了,我總結起來是:

格式: lrange key start end
end為負數表示逆向遍歷,此時start也為負數且小於end,則按順序遍歷下來。
為負數還大於end則取不到元素,為正數則兩邊按自己的索引規則來

最常用的是: lrange key 0 -1取所有的元素。
1.1.3.4 Set和Zset類型
這裏有個問題先記一下:redis會自動把中文轉為Unicode存儲。
Set表示無序集合,ZSet表示有序集合,Zset多一個score的概念。

192.168.15.142:6379> sadd skey1 1 2 3 4 5
(integer) 5
192.168.15.142:6379> smembers skey1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
192.168.15.142:6379> sadd skey1 1 3 4
(integer) 0
192.168.15.142:6379> smembers skey1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
192.168.15.142:6379> sismember skey1 3
(integer) 1
192.168.15.142:6379> sadd skey2 3 4  6 7
(integer) 4
192.168.15.142:6379> sdiff skey1 skey2
1) "1"
2) "2"
3) "5"
192.168.15.142:6379> sinter skey1 skey2
1) "3"
2) "4"
192.168.15.142:6379> sunion skey1 skey2
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
192.168.15.142:6379>

Sadd添加,srem移除。Smembers查看所有,sismember判斷是否存在某元素。Sdiff取差集,sinter取交集,sunion取並集。下面繼續簡單說下下有序集合的用法,不再演示:

zadd zkey1 10 zhangsan 20 lisi 30 wangwu 往有序集合裏面添加元素
zrange zkey1 0 -1 查看有序集合所有元素
zrem zkey1 wangwu
zrange zkey1 0 -1 withscores 帶分數(排名)的查看元素
zrevrange zkey1 0 -1 withscores 帶分數的降序(從大到小)查看元素

可以看到redis數據類型相關命令很多,但也很簡單很類似。關鍵在於自己去試,去查。

4 Redis通用命令
只說兩個:keys 和 ping。
Keys可以使用通配符實現模糊查詢,ping可以檢查redis-server是否掛掉。
技術分享圖片

2.4 Redis持久化及主從復制

  1. 兩種持久化方式
    RDB:
    間隔固定時間去持久化一次
    速度較快
    AOF:
    實時保存
    大大拖慢redis系統的速度,比較雞肋。適用於特定情形。
    使用方法:
    修改配置文件,redis.conf 。 默認的rdb模式,存儲的數據都在dump.rdb文件裏面。
    配置文件裏appendonly 項為 no . 使用aof模式,把它改成yes,數據默認保存在
    appendonly.aof文件裏面。然後重啟一下就行了:./redis-server redis.conf

  2. 主從復制
    和前面說的兩種持久化方式相關,因為aof模式不適合主從復制,只適用於rbd模式。
    備份原理:

    從服務器發送sync請求命令,主redis發生dump.rdb文件,已經當前未持久化的緩存中所有寫命令,這樣就能保證主從一致。這種方式不適應與aof模式。從redis使用ping命令,遵從心跳機制,檢測主redis是否掛掉了,如果掛掉,則從redis臨時頂替,且從redis此時默認是只讀的,以保證主從一致。

用法演示:

在同一臺機器上模擬,把dump.rdb文件先刪掉或備份出來,保證沒有歷史數據。拷貝一份redis出來,主redis不需要做更改,從redis的配置文件裏,port修改一下避免沖突,另外取消slaveof項的配置,設置要同步那個主服務器。此時,先啟動主redis,後啟動從redis,在主redis裏面設置一些key,然後shutdown掉。進入從redis-cli,看看主redis裏面set過的key是否能夠get以驗證備份是否正常。

REDIS基礎學習筆記