Redis分佈式緩存配置和使用

Redis

Redis是一個分佈式緩存系統。具有內存緩存和持久化雙重功能。可以作爲nosql數據庫使用。

一、緩存解決的問題

因爲頻繁的數據庫請求,會導致數據庫壓力過大,並且請求速度較慢(比如:數據庫隔離級別爲了避免髒讀和幻讀,會加入讀寫鎖機制,從而導致訪問出現延遲等待現象)。總而言之,緩存是爲了減輕數據庫的壓力。

  • 應用場景案例

“點贊”功能是網站非常普遍的一個功能。但是問題出現:點贊需要記錄數據,但是點讚的相應速度要非常高,點讚的併發也是非常高的。如何解決這種併發大、響應速度快的需求呢?

二、安裝

  • 先安裝gcc工具(gcc是linux下的一個編譯器,可以用來編譯c、c++等代碼)
yum -y install gcc //-y表示自動安裝
  • 安裝Redis
wget http://download.redis.io/releases/redis-2.8.17.tar.gz //下載redis安裝包
tar xzf redis-2.8.17.tar.gz
cd redis-2.8.17
make
  • BUG
使用 make MALLOC=libc 進行編譯。MALLOC表示C語言中的動態分配內存函數,libc就是庫文件

三、啓動

  • 啓動服務器
src/redis-server
  • 啓動客戶端
src/redis-cli
  • 測試
[javen@localhost src]$ ./redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> ping ok
"ok"
127.0.0.1:6379> set name "zhangsan" EX 10 //EX 10表示10秒後過期
OK
127.0.0.1:6379> get name
"zhangsan"
127.0.0.1:6379> get name
(nil)

//數據庫1000萬數據量,Redis中存儲了20萬的數據,怎麼保證20萬全是熱點數據?

四、使用

既然Redis是緩存,那麼它的作用呢,就只有存儲數據和獲取數據

4.1、存儲字符串

set key value
get key

4.2、存儲Hash值

hash存儲,一般可以用來存儲Java中的一個完整的自定義對象(比如dto)。

//hmset是存儲hash值的指令,
//user是當前hash的key
//name "zhangsan" age 23 sex "nan" 是 key對應的值
127.0.0.1:6379> hmset user name "zhangsan" age 23 sex "nan"
OK
//hmget獲取hash中的某一個屬性的值
127.0.0.1:6379> hmget user name
1) "zhangsan"
127.0.0.1:6379> hmget user age
1) "23"
//hgetall是獲取hash中的所有屬性對應的值
127.0.0.1:6379> hgetall user
1) "name"
2) "zhangsan"
3) "age"
4) "23"
5) "sex"
6) "nan"

4.3、存儲List列表(有序列表)

//lpush用來存儲一個列表的命令。interesting是列表的名稱,"basketball"列表中的值
127.0.0.1:6379> lpush interesting "basketball"
(integer) 1
127.0.0.1:6379> lpush interesting "football" "ball"
(integer) 3
//lrange輸出列表中的數據的命令, interesting就是列表的名稱 。 0 2是列表的開始輸出索引和結束索引。
127.0.0.1:6379> lrange interesting 0 2
1) "ball"
2) "football"
3) "basketball"

4.4、存儲Set集合(無序集合)

sadd key member //存數據
smembers key //取數據

案例:
127.0.0.1:6379> sadd strset "a" "b" "c"
(integer) 3
127.0.0.1:6379> smembers strset
1) "b"
2) "c"
3) "a"

4.5、存儲zset集合(有序集合)

zadd key score member  (score是一個數字,zset就是通過這個數字進行排序,可以重複)
ZRANGEBYSCORE key 0 1000 //通過分數排序輸出

注意:存入set集合中的數據是唯一的。有序集合,如果存入重複數據,則後一個數據的score會覆蓋原有數據的score。

五、發佈訂閱者模式

兩個客戶端,一個是發佈者、另一個是訂閱者。發佈者發佈的消息,訂閱者能收到。
SUBSCRIBE java  //訂閱Java頻道的消息
publish Java “消息” //向Java頻道發送消息

六、事務

Redis中的事務具備隔離性和原子性。

隔離性:一個事務在執行過程中不會被其他請求所打斷執行。

原子性:要麼全部成功、要麼全部失敗(跟關係型數據的事務原子性有一定理解上的差異。Redis的事務不存在回滾機制,所以這句話跟傳統意義的效果有一定的差異)。

multi //開啓事務
exec //結束事務

說明:Redis事務是沒有事務回滾概念。因爲Redis語法簡單,不會出現運行異常。即使出現語法異常是不會導致事務回滾。

七、安全配置

 config set requirepass 123456 //配置redis的密碼
 config get requirepass //查看密碼

 驗證密碼:auth 123456
 帶密碼登錄:./redis-cli -h 127.0.0.1 -p 6379  -a 123456   // -h表示主機IP,-p端口,-a密碼

八、Java操作Redis

  • 準備

    關閉linux服務器的防火牆 
    修改redis的配置redis.conf文件中的bind 127.0.0.1註釋掉 
    啓動redis: ./src/redis-server redis.conf 
    關閉redis的保護模式:config set protected-mode no  (默認是yes

  • 存字符串

@Test    
public void testCase1(){        
//創建Redis的客戶端        
//參數1:IP地址,或者主機名稱        
//參數2:端口,redis的默認端口是6379        
Jedis jedis = new Jedis("192.168.72.188",6379);        
//配置登錄密碼        
jedis.auth("123456");        
//存儲字符串        
jedis.set("username","zhangsan");

/取出緩存中數據        
String username = jedis.get("username");        
System.out.println("緩存中的數據:"+username);

    }
  • 存集合
/**
     * 有序set集合
     */
    @Test
    public void testCase3(){
        Jedis jedis = new Jedis("192.168.72.188", 6379);
        jedis.auth("123456");
        //存入有序集合
        //參數1:集合的名稱
        //參數2:用來排序的分數,一個整數
        //參數3:集合中值
        jedis.zadd("set",0,"zhangsan");
        jedis.zadd("set",1,"lisi");
        //獲取集合中的數據
        Set<String> set = jedis.zrangeByScore("set", 0, 3);
        for (String str:set) {
            System.out.println(str);
        }
    }
  • 連接池
/**
     * Redis的連接池
     */
    @Test
    public void testCase5(){

        //創建連接池的參數對象
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        //最大等待時間
        jedisPoolConfig.setMaxWaitMillis(10000);
        //最大休眠時間
        jedisPoolConfig.setMaxIdle(10000);
        //連接池最大連接數
        jedisPoolConfig.setMaxTotal(1000);

        JedisPool jedisPool = new JedisPool(jedisPoolConfig,"192.168.72.188",6379);

        //獲取連接
        Jedis jedis = jedisPool.getResource();
        jedis.auth("123456");
        String username = jedis.get("username");
        System.out.println(username);
    }

九、Spring整合Redis

http://projects.spring.io/spring-data-redis/

<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>2.9.0</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-redis</artifactId>
      <version>1.8.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>4.3.9.RELEASE</version>
    </dependency>
  • redis配置
 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--配置連接池參數-->
    <bean id="redisConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxWaitMillis" value="10000"/>
    </bean>
    <!--配置Jedis工廠-->
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="port" value="6379"/>
    <property name="hostName" value="192.168.72.188"/>
    <property name="password" value="123456"/>
    <property name="poolConfig" ref="redisConfig"/>
    </bean>

    <!--配置Jedis模板,用來操作redis api的類-->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
            <property name="connectionFactory" ref="jedisConnectionFactory"/>
    </bean>
    </beans>
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.data.redis.core.ListOperations; 
import org.springframework.data.redis.core.RedisTemplate; 
import org.springframework.data.redis.core.StringRedisTemplate; 
import org.springframework.test.context.ContextConfiguration; 
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.HashMap; 
import java.util.List; 
import java.util.concurrent.TimeUnit;

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration("classpath:spring-redis.xml") 
public class SpringRedisTest {


    @Autowired    
    private StringRedisTemplate redisTemplate;


    @Test
    public void testCase1(){
    //存儲字符串
    //參數1:key
    //參數2:值 value
    //參數3:生命週期
    //參數4:生命週期的時間單位
    //redisTemplate.restore("name33","zhangsanspring".getBytes(),10000, TimeUnit.MILLISECONDS);
    //存儲字符串
    redisTemplate.boundValueOps("name33").append("zhangsan");
    }

    /**
    * 存儲列表
    */
    @Test
    public void testCase2(){

        //存列表數據
        redisTemplate.boundListOps("list2").leftPushAll("a","b","c","d");

        //獲取列表數據
        List<String> list2 = redisTemplate.boundListOps("list2").range(0, 10);
        for (String str : list2) {
            System.out.println(str);
            }
    }

    @Test
    public void testCase3(){
        HashMap<String, String> hashMap = new HashMap<String, String>(); 
        hashMap.put("name","zhangsan"); 
        redisTemplate.boundHashOps("hashdata").putAll(hashMap);
    } }

edis緩存流

十、BUG

redis.clients.jedis.exceptions.JedisDataException: MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.
 at redis.clients.jedis.Protocol.processError(Protocol.java:127)
 at redis.clients.jedis.Protocol.process(Protocol.java:161)
 at redis.clients.jedis.Protocol.read(Protocol.java:215)
 at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340)
 解決:
 config set stop-writes-on-bgsave-error no
redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: Connection refused: connect
 at redis.clients.jedis.Connection.connect(Connection.java:207)
 at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:93)
 at redis.clients.jedis.Connection.sendCommand(Connection.java:126)
 解決:
 將/redis-xxx/redis.conf文件中的bind 127.0.0.1註釋掉 
redis.clients.jedis.exceptions.JedisDataException: DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the loopback interface. If you want to connect from external computers to Redis you may adopt one of the following solutions: 1) Just disable protected mode sending the command 'CONFIG SET protected-mode no' from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent. 2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to 'no', and then restarting the server. 3) If you started the server manually just for testing, restart it with the '--protected-mode no' option. 4) Setup a bind address or an authentication password. NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside.
 at redis.clients.jedis.Protocol.processError(Protocol.java:127)
 at redis.clients.jedis.Protocol.process(Protocol.java:161)
 at redis.clients.jedis.Protocol.read(Protocol.java:215)
 at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340)
 at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:239)
 解決:
 執行命令:config set protected-mode no
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章