目录

第一章-Redis概述

1介绍和安装

黑马实用教程:https://www.bilibili.com/video/BV1CJ411m7Gc?p=23

笔记网址:https://blog.csdn.net/branwel/article/details/103897256

介绍

关系型数据库通过外键关联来建立表与表之间的关系。

非关系型数据库通常指数据以对象的形式存储在数据库中,而对象之间的关系通过每个对象自身的属性来决定。Nosql Not-Only SQL

Redis 运行快速的原因

  1. 完全基于内存操作
  2. 数据结构简单,数据操作也简单
  3. 使用多路I/O复用模型

关系型数据库发展的原因,之前的关系型数据库,关系复杂,扩展性差,不容易维护,不便于大规模集群。磁盘IO性能低下。

redis作用:应用基于海量用户和海量数据前提下的数据处理问题。 一般用于,高频,波段性比较强的热点信息。

安装

Linux:https://redis.io/download window:安装和教程https://www.runoob.com/redis/redis-install.html

还可以安装桌面可视化管理工具进行操作,比如redis desktop manager

开启服务:

redis一般都是在Linux下使用,这里只是作基础的学习。 在redis解压目录下:cmd+空格 (通过window+r用cd切换到目录也可以)。

1输入:redis-server.exe redis.windows.conf 会出现一个redis图像 2不要关闭上一个窗口,再开一个。切换到redis目录 输入:redis-cli.exe -h 127.0.0.1 -p 6379 看到前缀变为127.0..:6379 OK

获取配置 redis 127.0.0.1:6379>输入CONFIG GET 配置项的名称 获取全部配置 CONFIG GET *

也就是redis.windows.conf Window下的配置

关闭客户端:shutdown

2数据格式

在redis操作中成功为1,失败为0

类型名称 redis中类型
字符串 string
列表 list
散列 hash
集合 set
有序集合 sorted_set

1string

key的命名习惯 表名:主键名:主键值:字段名称

单条操作

  1. 增:set key value
  2. 查:get key
  3. 删:del key

多条操作

  1. 增改:mset key value [key1 value1]
  2. 查:mget key [key1]

** 其他命令**

strlen key //获取字符串长度 append key value //有则追加,无则新建 setnx key value //不存在就设置,存在就不设置 incr key //自增 1 incrby key num //给key的值增加num(int 类型),num 正数则为加,num 为负数 则为减 incrbyfloat key num //给key的值增加num(float 类型) decr key //自减 1 decrby key num //给key的值减num setex key second value //设置key的值为value存活时间为second秒 psetex key millisecond value //设置key的值为value存活时间为millisecond毫秒

2Hash

** 单条操作**

  1. 增:hset key field value
  2. 查:hget key field
  3. 删:hdel key field

** 多条操作**

  1. 增:hmset key field value [field1 value2]
  2. 查:hmget key field [field1]

其他命令

hgetall key //获取key的全部的字段和值 hlen key //获取key的值的数量 hexists key field //是否存在field hkeys key //所有key的字段(field) hvals key //所有key的值 hincrby key field num //给key的field的值增加num (num 为int值) hincrybyfloat key field num //给key的field的值增加num (num 为float) hsetnx key field value //存在不设置,不存在设置


注意

Hash类型的value只能存字符串,不允许再嵌套其他类型,如果数据为空为Nil 每个Hash可以存储232−1 2^{32}-12 32 −1个键的值对 Hash类型十分贴近对象的数据存储,并且可以灵活添加、删除对象属性。但Hash类型设计并不是存在量而设计的,切记不可滥用,更不可将Hash作为对象列表使用 hgetall操作可以获取全部属性,如果内部field过多,遍历整体数据时效率会降低,很有可能成为数据访问的瓶颈

3list

保存多个数据,底层用双向链表存储结构实现。

key为集合名

** 添加**

  1. lpush key value [value1] //从左添加
  2. rpush key value [value1] //从右添加
1
2
3
lpush mylist a1 a2 a3 a4  //从左添加
lrange mylist 0 3					
a4 a3 a2 a1 

获取

  1. lrange key start stop(为-1时获取所有数据)
  2. lindex key index
  3. llen key

** 获取并移除**

  1. lpop key //从左出
  2. rpop key //从右出

从左边移除count个指定数据

lrem key count value

  1. list具有索引的概念,但是操作数据时通常以队列的形式进行入队出队操作(或以栈的形式进行入栈出栈操作)

规定时间内,(超过时间返回nil,可以有从多个集合获取)获取并移除数据

blopop key1 key2 timout

4set

存储大量数据,查询方面有更高效率

增:sadd key member [member1] 查:smembers key 删:srem key member [member1] 获取总量:scard key 判定是否存在:sismember key member 随机获取(原集合保留):srandmember key [count] 随机获取(原集合不保留):spop key 集合交集:sinter key key1 key2 集合并集:sunion key key1 key2 集合差集:sdiff key key1 key2 存储集合交集:sinterstore destination key key1 key2 存储集合并集:sunionstore destination key key1 key2 存储集合差集:sdiffstore destination key key1 key2 集合元素移动:smove source destination member

应用场景:访问量统计

公司对旗下新的网站做推广,统计网站的PV (访问量) ,UV (独立访客) ,IP (独立IP)。 PV:网站被访问次数,可通过刷新页面提高访问量 UV:网站被不同用户访问的次数,可通过cookie统计访问量,相同用户切换IP地址, UV不变 IP:网站被不同IP地址访问的总次数,可通过IP地址统计访问量,相同IP不同用户访问, IP不变

5sorted_set

zset (sorted set:有序集合)。

增:zadd key score member [score1 member1] 删:zrem key member [member1]

加上withscores意思是会先显示score 再显示member

获取全部(正序):zrange key start stop [withscores] 获取全部(倒序):zrevrange key start stop [withscores]

按条件查(正序):zrangebyscore key min max [withscore limit] 按条件查(倒序):zrevrangebyscore key max min [withscore limit]

按条件删除(索引):zremrangebyrank key start stop 按条件删除(积分):zremrangebyscore key min max 获取集合总量:zcard key | zcount key min max 存储集合交集: zinterstore destination numkeys key key1 存储集合并集:zunionstore destination numkeys key key1 获取索引(正序):zrank key member 获取索引(倒序):zrevrank key member score值获取:zscore key member score值修改:zincrby key num member

3通用的key

删除:del key 判断是否存在:exists key 获取key类型:type key

指定有效期: expire key seconds pexpire key milliseconds expireat key timestamp pexpireat key milliseconds-timestamp

获取有效期: ttl key pttl key

设置永久:persist key 查询key:key pattern //*,?,[] 重命名:rename key newkey 新名称不存在时,才改 renamenx key newkey 对key排序:sort

key pattern匹配符号解释: // *任意数量任意符号,?一个任意符号 []匹配一个指定字符

keys *查询所有,keys it* 查询以it开头

4数据库通用操作

为了解决随着数据量增大,出现大量数据以及对应key,可能出现的数据名类似。

redis为每个服务提供16个数据库,从0到15 每个数据库之间数据相互独立。

  1. 选择数据库:select index
  2. 数据移动:move key db
  3. 数据库大小(查看库中有多少key):dbsize
  4. 数据清除:
    1. 单库删除:flushdb
    2. 多库删除:flushall

redis安全 默认连接redis不需要密码 config set requirepass xxxx设置密码 config get requirepass 查看密码 若无则是:“requirepass” "” auth xxx验证密码

第二章-Redis高级

0-Jedis

加入jar包

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!--jedis客户端依赖jar redis-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>
<!--使用jedis连接池-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.8.0</version>
</dependency>

Jedis简单使用

1
2
3
4
5
6
7
8
9
//单机连接redis
Jedis jedis=new Jedis("localhost",6379);

//操作redis
String string1 = jedis.get("string1");
jedis.set("test","汉字能不能用?123456");//redis数据库用cmd窗口会显示编码,java取出汉字

//关闭
jedis.close();

jedis连接池工具类(redis.properties) 使用配置文件,能够解耦。

redis.properties

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
ADDR="localhost"
PORT = 6379
密码**
AUTH = null

连接实例的最大连接数**
MAX_ACTIVE = 1024

控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。**
MAX_IDLE = 200

等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException**
MAX_WAIT = 2000

连接超时的时间 **
TIMEOUT = 2400

在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;**
TEST_ON_BORROW = true

数据库模式是16个数据库 0~15**
DEFAULT_DATABASE = 0

RedisUtil工具类

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
package com.lx.redis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.ResourceBundle;


public class RedisUtil {

    //服务器IP地址
    private static String ADDR = "localhost";

    //端口
    private static int PORT = 6379;

    //密码
    private static String AUTH = null;

    //连接实例的最大连接数
    private static int MAX_ACTIVE = 1024;

    //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
    private static int MAX_IDLE = 200;

    //等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException
    private static int MAX_WAIT = 10000;

    //连接超时的时间  
    private static int TIMEOUT = 10000;

    // 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
    private static boolean TEST_ON_BORROW = true;

    private static JedisPool jedisPool = null;
    //数据库模式是16个数据库 0~15
    public static  Integer DEFAULT_DATABASE = null;
    /**
     * 初始化Redis连接池
     */

    static {

        try {
            ResourceBundle rb=ResourceBundle.getBundle("redis");
            ADDR=rb.getString("ADDR");
            PORT=Integer.valueOf(rb.getString("PORT"));
            MAX_ACTIVE=Integer.valueOf(rb.getString("MAX_ACTIVE"));
            MAX_IDLE=Integer.valueOf(rb.getString("MAX_IDLE"));
            MAX_WAIT=Integer.valueOf(rb.getString("MAX_WAIT"));
            TIMEOUT=Integer.valueOf(rb.getString("TIMEOUT"));
            TEST_ON_BORROW=Boolean.valueOf(rb.getString("TEST_ON_BORROW"));
            DEFAULT_DATABASE=Integer.valueOf(rb.getString("DEFAULT_DATABASE"));


            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,DEFAULT_DATABASE);

        } catch (Exception e) {

            e.printStackTrace();
        }

    }

    /**
     * 获取Jedis实例
     */

    public synchronized static Jedis getJedis() {

        try {

            if (jedisPool != null) {
                Jedis resource = jedisPool.getResource();
                System.out.println("redis--服务正在运行: "+resource.ping());
                return resource;
            } else {
                return null;
            }

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

    /***
     *
     * 释放资源
     */

    public static void returnResource(final Jedis jedis) {
        if(jedis != null) {
            jedis.close();
        }
    }
}

0-整合spring

先加入jar包依赖

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!--redis-->
 <dependency>
   <groupId>org.springframework.data</groupId>
   <artifactId>spring-data-redis</artifactId>
   <version>2.1.9.RELEASE</version>
 </dependency>

 <dependency>
   <groupId>redis.clients</groupId>
   <artifactId>jedis</artifactId>
   <version>2.9.0</version>
</dependency>

1redis.properties

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
redis.maxTotal=10
redis.maxIdle=5
redis.maxWaitMillis=2000
redis.testOnBorrow=true
redis.host=localhost
redis.port=6379
redis.timeout=5000
redis.password=null
redis.testWhileIdle=true 
redis.timeBetweenEvictionRunsMillis=30000  
redis.numTestsPerEvictionRun=50 

2spring-dao.xml 注意没有密码,这是整合的mabatis-plus

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
    <context:property-placeholder location="classpath*:redis.properties,classpath*:database.properties"/>

    <!-- 配置整合mybatis -->
    <!-- 1.关联数据库文件 -->


    <!-- 2.数据库连接池 -->
    <!--数据库连接池
dbcp  半自动化操作  不能自动连接
c3p0  自动化操作(自动的加载配置文件 并且设置到对象里面)
druid
    -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 配置连接池属性 -->
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- c3p0连接池的私有属性 -->
        <property name="maxPoolSize" value="30"/>
        <property name="minPoolSize" value="10"/>
        <!-- 关闭连接后不自动commit -->
        <property name="autoCommitOnClose" value="false"/>
        <!-- 获取连接超时时间 10s-->
        <property name="checkoutTimeout" value="10000"/>
        <!-- 当获取连接失败重试次数 -->
        <property name="acquireRetryAttempts" value="2"/>
        <!--最大空闲时间,3600秒内未使用则连接被丢弃。若为0则永不丢弃。默认值: 0 -->
        <property name="maxIdleTime" value="7200"/>
        <!--每120秒检查所有连接池中的空闲连接。Default: 0 -->
        <property name="idleConnectionTestPeriod" value="120"/>
    </bean>

    <!-- 3.配置SqlSessionFactory对象 -->
    <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <!-- 注入数据库连接池 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置MyBaties全局配置文件:mybatis-config.xml -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--也可以不配置MyBatis全局配置文件,再这里说明-->
        <property name="mapperLocations" value="classpath:com/lsl/mapper/xml/*.xml"></property>
        <property name="typeAliasesPackage" value="com.lsl.entity"></property>
    </bean>

    <!-- 4.配置扫描Dao接口包,动态实现Dao接口注入到spring容器中 -->
    <!--解释 : https://www.cnblogs.com/jpfss/p/7799806.html-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 注入sqlSessionFactory -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!-- 给出需要扫描Dao接口包 -->
        <property name="basePackage" value="com.lsl.mapper"/>
    </bean>

    <!-- redis数据源 -->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- 最大空闲数 -->
        <property name="maxIdle" value="${redis.maxIdle}" />
        <!-- 最大空连接数 -->
        <property name="maxTotal" value="${redis.maxTotal}" />
        <!-- 最大等待时间 -->
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
        <!-- 返回连接时,检测连接是否成功 -->
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />
    </bean>

    <!-- Spring-redis连接池管理工厂 -->
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <!-- IP地址 -->
        <property name="hostName" value="${redis.host}" />
        <!-- 端口号 -->
        <property name="port" value="${redis.port}" />
     <!--   <property name="password" value="${redis.password}" />-->
        <!-- 超时时间 默认2000-->
        <property name="timeout" value="${redis.timeout}" />
        <!-- 连接池配置引用 -->
        <property name="poolConfig" ref="poolConfig" />
        <!-- usePool:是否使用连接池 -->
        <property name="usePool" value="true"/>
    </bean>

    <!-- redis template definition -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="jedisConnectionFactory" />
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
        </property>
        <property name="hashKeySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <property name="hashValueSerializer">
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
        </property>
        <!--开启事务  -->
        <property name="enableTransactionSupport" value="true"></property>
    </bean>

</beans>

3使用时,在service中使用就好

template使用:https://blog.csdn.net/sinat_22797429/article/details/89196933

1
2
@Autowired
RedisTemplate template;

注意: 存放取出,全部使用Spring。或者对象。

redisTemplate.opsForHash().increment(“lsl1”,“2”,15);进行修改时,若是hset进去的会报错,ERR,因为其采用的序列号方式不一样,在使用increment修改时候,存放的时候也要这个存放。

1、linux下使用

1-安装

redis6要先更新c++(然后重启服务器)

1
2
3
4
5
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils

echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile

安装

1
2
3
4
5
6
7
8
$ wget http://download.redis.io/releases/redis-6.0.4.tar.gz
$ tar xzf redis-6.0.4.tar.gz  #解压
$ cd redis-6.0.4			
$ make   #编译 make MALLOC=libc
$ make install #安装

# redis-server 启动服务器
# redis-cli    启动客户端

服务器换端口启动 redis-server –port 端口号6380

这时客户端启动 就需要指定端口号 redis-cli -p 6380

2-配置文件

redis配置文件在redis-6.0.0.4下的redis-conf,文件原配置文件不要改动,由于里面大量注解,所以需要重新复制一个配置文件。

1
cat redis.conf  | grep -v "#" | grep -v "^$" > redis-6379.conf

编辑新创建的配置文件redis-6379.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
port 6379
以守护进程方式启动**
daemonize yes 
logfile "redis-6379.log"

文件存放位置,日志文件,本地存储文件**
dir /home/redis-log

rdb持久化方式 save指令相关配置 持久化的文件名称**
dbfilename dump-端口号.rdb

存储到本地数据库时 是否压缩,默认yes 用LZF压缩**
rdbcompression yes

设置时 是否RDB文件格式校验,读写都进行,默认开启,关闭肯有虽居损坏风险**
rdbchecksum yes

900秒内 若有一个1key发生变化就进行保存 可多设几个形成互补**
save 900 1

是否开启AOP默认不开启**
appendonly yes/no 
AOF写数据策略**
appendfsync always/everysec/no 
AOF文件名称**
appendfilename appendonly-端口号.aof文件名称

AOF重写**
自动重写的最小值**
auto-aof-rewrite-min-size size   
自动重写百分比**
auto-aof-rewrite-percentage percentage 
当前大小,和重写最小值有关**
 aof_current_size     
基础大小,和重写百分比有关  (当前大小-基础大小)基础大小>自动重写百分比**
 aof_base_size   

以配置文件启动服务器

1
2
3
redis-server redis-6379.conf
可以在redis-6.0.0.4下创建一个conf文件夹,用来存放各个不同的配置文件。**
redis-server conf/redis-6379.conf

2、持久化

持久化:利用永久性存储介质将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持款化。

为什么持久化:防止数据意外丢失,确保数据安全性。


持久化方式

1快照(RDB)

将当前数据状态进行保存,快照形式,存储数据结果,存储格式简单,关注点在数据。 10100011100这样的二进制的数据(快照)。

1快照(RDB)保存command为:在客户端使用save save命令是阻塞式的,直到完成持久化,若时间过长,后面指令就不会执行。(线上不推荐使用),可以用bgsave

关于RDB持久化配置都在配置文件中。

自动save配置:save second changes save 900 1 在900秒内,若有一个Key发生变化就保存。

缺点:

  • 基于快照思想,每次读写都是全部数据,当数据量巨大时,效率非 常低。
  • 大数据量下的IO性能较低
  • 基于fork创建子进程,内存产生额外消耗
  • 宕机带来的数据丢失风险

2日志(AOF)

append only file

将数据的操作过程进行保存,日志形式,存储操作过程,存储格式复杂,关注点在数据的操作过程。

像文本编辑器的Ctrl+Z Ctrl+Y都是记录的过程。

●AOF(append only file)持久化: 以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中命令达到恢复数据的目的。 与RDB相比可以简单描述为改记录数据为记录数据产生的过程 ●AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式

AOF写数据的三种策略:always(每次)、everysec(每秒)、no(系统控制)

若使用AOP需要在配置文件配置 appendonly yes/no 是否开启AOP默认不开启 appendfsync always/everysec/no 写数据策略 appendfilename appendonly-端口号.aof文件名称


AOF重写 降低磁盘使用量,提高持久化效率,提高数据恢复效率。

手动:bgrewriteaof。

自动:配置文件配置。

  • auto-aof-rewrite-min-size size 自动重写的最小值
  • auto-aof-rewrite-percentage percentage 自动重写百分比

与其对比的参数为

  • aof_current_size 当前大小,和重写最小值有关
  • aof_base_size 基础大小,和百分比有关

其触发条件有两个:

  • 1 最小值>当前大小
  • 2 (当前大小-基础大小)基础大小>自动重写百分比

如何选择

RDB当数据量大时,保存快照速度很慢。恢复速度快,适合阶段性有效的程序(比如游戏等,丢失一小时数据可以接受)。

若对数据敏感,采用默认AOF,使用everysecond,每秒fsync一次,可保证出现问题时,最多丢失0-1秒数据。由于AOF文件存储体积大,恢复速度慢。

也可以都打开,优先用AOF

3、事务

事务基本操作

开启:multi

操作,从开启到执行,中间的操作,都会放到一个事务中执行。

在事务过程中若发生问题,可以取消事务,discard

错误1:若语法格式有问题 比如set s1 asb写成seta s1 asb,那么整个事务中的所有命令都不会执行。

错误2:若命令执行出错误,如对List进行incr操作, 正确语句会执行,错误将不会执行。需要程序员自己在程序中回滚。。

执行:exec

3-1、事务-锁

问题:商品的补货有两个人可以进行,若一个人进行了补货,那么其他人就不再进行,如何知道其他人已经补货了呢?

watch key1 key2 对key进行加锁监控,若发生变化就不执行接下来的事务。

unwatch 取消所有key的监视

注意,watch必须在一个事务开启前进行

还可以使用setnx lock-key value设置一个公共锁,操作完毕删除。 但若宕机,就可能产生死锁,可以使用。 expire lock-key second 设置时间-秒 pexpire lock-key millisecond设置时间-毫秒

操作通常是毫秒/微秒,所以需要测试得出。

第三章-redis部署

1主从复制

就是做一个redis集群,由master负责写,slave负责读,提高服务器读写负载能力。

负载均衡:基于主从结构,配合读写分离,由slave分担master负载, 并根据需求的变化,改变slave的数量,通过多个从节点分担数据读取负载,提高Redis服务器并发量与数据吞吐量。


one master ,two slave

主从复制

redis默认是主数据,所以master无需配置,我们只需要修改slave的配置即可。

slave配置

slave配置文件中,设置 port selfPort6390 自己的端口号 slaveof masterIP masterPort 主人的IP地址和端口号

如果master有密码,需要设置:Masterauth 密码

连接成功后可以info replication查看连接信息

2哨兵sentinel

**哨兵模式sentinel /ˈsentɪnl/ **

一台主机,两台从机,3/5/7奇数哨兵。

Redis sentinel 是一个分布式系统中监控 redis 主从服务器,并在主服务器下线时自动进行故障转移。其中三个特性:

监控(Monitoring):Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。

哨兵模式启动:哨兵

1
2
3
4
5
6
7
8
#启动哨兵
redis-server sentinel/sentinel-26379.conf --sentinel

#启动服务器
redis-server sentinel/redis6379.conf

#启动客户端
redis-cli -p 6379

windos下5.0redis若启动哨兵不报错,就说明启动成功,只是没有第一次启动的信息。

3集群

集群工作方式:在redis的每一个节点上,都有这么两个东西,一个是插槽(slot),它的的取值范围是:0-16383。还有一个就是cluster,可以理解为是一个集群管理的插件。当我们的存取的key到达的时候,redis会根据crc16的算法得出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。


1安装rubyinstaller-2.3.3.exe,修改安装地址,三个都勾选。 2在redis地址gem install redis 3官网下载源码:redis-trib.rb(当前redis版本的源码src下) 然后放到redis文件夹下 4修改配置文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#修改端口号
port 6379

#不为yes那么在使用JedisCluster集群代码获取的时候,会报错
cluster-enabled yes

#nodes-6379.conf 是为该节点的配置信息,这里使用 nodes-端口.
#conf命名方法,服务启动后会在目录生成该文件

cluster-config-file nodes-6379.conf
#调整为  15000,不设置那么在创建集群的时候,不会超时。
cluster-node-timeout 15000
appendonly yes

5启动这几个服务器后,进行集群操作。 nredis-trib.rb create –replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 replicas 1 表示每个主数据库拥有从数据库个数为1。master节点不能少于3个,所以我们用了6个redis 然后输入YES。

6启动客户端进行测试redis-cli -h 127.0.0.1 -p 6379 -c set s1 123发现端口号改变就说明集群成功。

4redis注意

雪崩、穿透、击穿。

雪崩

指redis中很多数据在同一时间过期,导致大量请求同一时刻请求到数据库。

解决:设置不同的过期时间,在过期时间上加随机数。


穿透

产生这个原因是大量的外部攻击,例如:请求大量id不存在的用户进行登录频繁请求接口,导致请求穿透redis频繁请求数据库。

解决:使用bloom过滤器,把用户Id先读入到bloom过滤器,然后对请求进行拦截,bloom过滤器会进行判断,过滤器判断不存在,那么一定不存在。过滤器判断存在,也有可能不存在,bloom会存在一定的误判率。

布隆过滤器的原理是,当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在


击穿

是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

解决:

可以使用互斥锁更新,保证同一个进程中针对同一个数据不会并发请求到 DB,减小 DB 压力。

使用随机退避方式,失效时随机 sleep 一个很短的时间,再次查询,如果失败再执行更新。

针对多个热点 key 同时失效的问题,可以在缓存时使用固定时间加上一个小的随机数,避免大量热点 key 同一时刻失效。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
//压力测试 线程池 
CyclicBarrier cyclicBarrier = new CyclicBarrier(1000);

ExecutorService executorService = Executors.newFixedThreadPool(1000);
for (int i = 0; i <1000 ; i++) {
  executorService.execute(new PressureThread(1,userMapper,redisTemplate,
                                             cyclicBarrier));
}
executorService.shutdown();

while(!executorService.isTerminated()){
}
System.out.println("程序结束");

cyclicBarrier.await();