Redis学习笔记

Redis

一、安装

建议将安装文件放置在 /opt 目录下

1、解压文件

tar -zxvf redis-6.2.4.tar.gz

2、安装依赖

yum -y install gcc-c++

#安装成功可查看版本
gcc -v


# make编译环境
cd redis-6.2.4
make

#安装
make install
  • redis默认安装路径

/usr/local/bin

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vY6ZajhM-1624354320929)(C:\Users\xulx\AppData\Roaming\Typora\typora-user-images\image-20210608134214323.png)]

3、将redis的配置文件复制到自定义的文件夹下【不影响原来的配置,方便还原】

#jerryconfig在/usr/local/bin目录下
cp /opt/redis-6.2.4/redis.conf jerryconfig/

二、配置和启动

1、配置Redis

注意:默认不是后启动的,若要启动需要修改配置文件

daemonize修改为yes 。就可以是后台启动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AqIXp9LA-1624354320931)(C:\Users\xulx\AppData\Roaming\Typora\typora-user-images\image-20210608135331004.png)]

2、启动 Redis

需要通过指定的配置文件启动

# 在bin下启动 redis
redis-server jerryconfig/redis.conf

3、测试连接

# redis-cli -h 主机名 -p 6379,本地(localhost)可以不加 -h 主机名
redis-cli -p 6379

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JoszyHch-1624354320936)(C:\Users\xulx\AppData\Roaming\Typora\typora-user-images\image-20210608140518456.png)]

4、关闭Redis

shutdown

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hiBFhjCb-1624354320937)(C:\Users\xulx\AppData\Roaming\Typora\typora-user-images\image-20210608142234121.png)]

三、压力测试

redis-benchmark

redis自带的压力测试工具

  • 参数说明

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pdeqvACJ-1624354320938)(C:\Users\xulx\AppData\Roaming\Typora\typora-user-images\image-20210608143807780.png)]

简单测试

# 进入启动目录
cd /usr/local/bin/

# 测试:100个并发连接,100000个请求
redis-benchmark -h localhost -p 6379 -c 1000 -n 100000
  • 测试结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mLnJpRYJ-1624354320939)(C:\Users\xulx\AppData\Roaming\Typora\typora-user-images\image-20210608145143516.png)]

四、基础知识

redis默认有16个数据库,【在配置文件中可以查看】,默认使用的是 第0个

1、基本命令

# 切换数据库
select 2

# 查看数据库大小
dbsize

# 清空当前数据库
flushdb

# 清空所有的数据库
flushall

# 判断字段是否存在
exist key

# 设置倒计时,时间以秒为单位
expire ttl

# 查看值的过期时间
ttl key

# 移除指定值
move key 1

# 查看值的类型
type key

2、基本数据类型

String

一个字符串类型的值最多能存储512M字节的内容。

# 设置值
set key value

# 取值
get key

# 查看所有的值
keys *

# 判断某个值是否存在
exists key

# 追加值,若原存在,则在原值的末尾追加,若原来不存在,则创建该值
append key value

# 获取字符串的长度
strlen key


# 步长相关
# 自增1
incr value
# 自减1
decr value
# 自定义自增步长
incrby value step
# 自定义自减步长
decr value step

# 字符串范围,start最小值为0,当end为-1时,即查看完整的字符串信息
getrange key start end

#替换字符串,offset是从哪里开始
setrange key offset value


# 设置过期时间,setex(set with expire)
setex key seconds value

# 如果不存在,再设置,setnx(set if not exist),如果存在,则创建失败【分布式锁中会使用】
setnx key value


# 批量设置或取值(原子性操作)
# 批量设置值
mset key1 value1 key2 value2

# 批量取值
mget key1 key2

#getset 先获取后设置值,如果值不存在则返回nil
getset key value
List

按插入顺序排序的字符串元素的集合。他们基本上就是链表(linked lists)。

# 将一个或多个值插入到列表的头部lpush list onelpush two three# 获取list的指定区间值lrange key start stop# 将一个或多个值插入到列表的尾部rpush list onerpush two three# 移除list的头部的值lpop key# 移除list的尾部的值rpop key# 通过某个下标获得list中的某一个值lindex key index# 获取list的长度llen key# 移除指定个数的值lrem key count value# 通过下标截取指定长度,截取结束只剩下截取的值ltrim key start stop# 组合操作,移除list的最后一个元素并将其放置另一个list中rpoplpush key source destination# 将list中指定下标的值替换为另外一个值,如果list或下表中的值不存在就会报错lset key index element# 在某个值的前面或后面插入指定的值linsert key before|after piovt element
Set

Set 是 String 的无序排列, set 的命令都是 s 开头的。Set的值是不能重复的

# 添加值(可以添加多个值)sadd key member# 查看指定set的所有值smembers key# 判断一个元素是否在set中,存在则返回1 ,不存在则返回 0sismember key member# 获取set元素中的值的个数scard key# 移除set中的指定值srem key member# 随机抽选出一个元素srandmember key# 随机抽选出指定个数的元素srandmember key count# 随机移除一个元素spop key# 随机移除指定个数的元素spop key count# 将一个指定的值,移动到另一个set中smove resource destination member# set的 差 集合sdiff key1 key2# set的 并 集合sunion key1 key2# set的 交 集合sinter key1 key2
Hash

命令以h开头,Hash 便于表示 objects,实际上,你可以放入一个 hash 的域数量实际上没有限制

# 设置hash值
hset key filed vlaue

# 获取hash值
hget key filed

# 设置多个hash值
hmset key filed1 value1 filed2 value2

# 获取多个hash值
hmget key filed1 value1 filed2 value2

# 获取hash所有的值
hgetall key

# 删除hash指定的字段
hdel key filed

# 获取hash的大小
hlen key

# 判断hash中的指定字段是否存在,存在返回1,否则0
hexists key filed

# 只获得所有的filed
hkeys key

# 只获得所有的value
hvals key

# 指定hash字段的增量
hincrby key filed increment

# 如果不存在,设置该值
hsetnx key filed value

hash更适合对象的存储,String更适合字符串的存储

Zset

有序集合

# 添加值zadd key socre member score member# 排序(降序)Zrevrangebyscore key min max [withscores]# 排序(升序),-inf代表负无穷,+inf代表正无穷zrangebyscore key min max [withscores]# 移除一个元素zrem key member# 获取集合中的个数zcard key# 获取指定区间的成员数量zcount key min max

3、三种特殊数据类型

Geospatial(地理位置)
# 添加地理位置geoadd key longitude latitude member [longitude latitude member]# 获取指定城市的经纬度geopos key member [member]#获取两个地理位置之间的直线距离geodist key member1 member2 [m|km|mi|ft]# 找出某个半径范围内的地点(人)georadius key logitude latitude raduis m|km|mi|ft# 找出指定位置指定半径范围内的成员georadiusbymember key member radius km|m|mi|ft# 返回一个或多个元素之间的geohash表示geohash key member [member ...]
  • geo 底层是zset 想要删除其中的某个成员数据,使用 zrem key member 移除
Hyperloglog

基数计算,这是一个基数统计算的算法,误差在0.81%

# 添加一组数据pfadd key member [member ...]# 合并多组数据pfmerge destkey sourcekey [sourcekey ...]# 计算基数数量pfcount key [key ...]
Bitmaps

位存储

使用场景:当目标对象只有两个状态的,都可以使用bitmaps来存储!

# 存储值setbit key offset value# 查看某个值getbit key offset# 统计数量(统计bit为 1 的数量)countbit key [start end]

五、Redis事务

redis事务的本质:一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行!

可以总结为:一次性、顺序性、排他性!

==redis的单条命令是保证原子性的,但是事务不保证原子性的!==redis的事务是没有隔离性的!所以不会出现数据的脏读和幻读!

正常执行事务

开启事务(multi)

命令入队(command…)

执行事务(exec)

# 开启事务
127.0.0.1:6666> multi
OK
127.0.0.1:6666(TX)> set k1 v1		# 入队
QUEUED
127.0.0.1:6666(TX)> set k2 v2
QUEUED
127.0.0.1:6666(TX)> get k2
QUEUED
127.0.0.1:6666(TX)> exec		# 执行事务
1) OK
2) OK
3) "v2"

放弃事务

127.0.0.1:6666> MULTI
OK
127.0.0.1:6666(TX)> set k1 v1
QUEUED
127.0.0.1:6666(TX)> get k1
QUEUED
127.0.0.1:6666(TX)> DISCARD		# 放弃事务
OK
127.0.0.1:6666> get k1
(nil)

异常

  • 编译型异常

    • 当在编译命令时出现错误,将不会执行所有的命令
  • 运行时异常

    • 当事务队列中存在语法性错误,其他的命令是可以正常执行的,只有错误的命令会抛出异常

六、SpringBoot整合Redis

1、导入依赖

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-redis</artifactId></dependency>

2、自定义RedisTemplate

@Configurationpublic class RedisConfig {    @Bean    @SuppressWarnings("all")    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {        RedisTemplate<String, Object> template = new RedisTemplate();        template.setConnectionFactory(redisConnectionFactory);        //此处设置自定义的内容,设置序列化        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);        ObjectMapper objectMapper = new ObjectMapper();        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);        //String序列化        StringRedisSerializer serializer = new StringRedisSerializer();        //设置key序列化        template.setKeySerializer(serializer);        template.setHashKeySerializer(serializer);        //设置value序列化        template.setValueSerializer(jackson2JsonRedisSerializer);        template.setHashValueSerializer(jackson2JsonRedisSerializer);        template.afterPropertiesSet();        return template;    }}

3、为方便Redis操作,可自行设置Redis操作的工具类

@Component
public class RedisUtils {

    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate redisTemplate;

    /** -------------------key相关操作--------------------- */

    /**
     * 删除key
     *
     * @param key
     */
    public void delete(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 批量删除key
     *
     * @param keys
     */
    public void delete(Collection<String> keys) {
        redisTemplate.delete(keys);
    }

    /**
     * 序列化key
     *
     * @param key
     * @return
     */
    public byte[] dump(String key) {
        return redisTemplate.dump(key);
    }

    /**
     * 是否存在key
     *
     * @param key
     * @return
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 设置过期时间
     *
     * @param key
     * @param timeout
     * @param unit
     * @return
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 设置过期时间
     *
     * @param key
     * @param date
     * @return
     */
    public Boolean expireAt(String key, Date date) {
        return redisTemplate.expireAt(key, date);
    }

    /**
     * 查找匹配的key
     *
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 将当前数据库的 key 移动到给定的数据库 db 当中
     *
     * @param key
     * @param dbIndex
     * @return
     */
    public Boolean move(String key, int dbIndex) {
        return redisTemplate.move(key, dbIndex);
    }

    /**
     * 移除 key 的过期时间,key 将持久保持
     *
     * @param key
     * @return
     */
    public Boolean persist(String key) {
        return redisTemplate.persist(key);
    }

    /**
     * 返回 key 的剩余的过期时间
     *
     * @param key
     * @param unit
     * @return
     */
    public Long getExpire(String key, TimeUnit unit) {
        return redisTemplate.getExpire(key, unit);
    }

    /**
     * 返回 key 的剩余的过期时间
     *
     * @param key
     * @return
     */
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key);
    }

    /**
     * 从当前数据库中随机返回一个 key
     *
     * @return
     */
    public Object randomKey() {
        return redisTemplate.randomKey();
    }

    /**
     * 修改 key 的名称
     *
     * @param oldKey
     * @param newKey
     */
    public void rename(String oldKey, String newKey) {
        redisTemplate.rename(oldKey, newKey);
    }

    /**
     * 仅当 newkey 不存在时,将 oldKey 改名为 newkey
     *
     * @param oldKey
     * @param newKey
     * @return
     */
    public Boolean renameIfAbsent(String oldKey, String newKey) {
        return redisTemplate.renameIfAbsent(oldKey, newKey);
    }

    /**
     * 返回 key 所储存的值的类型
     *
     * @param key
     * @return
     */
    public DataType type(String key) {
        return redisTemplate.type(key);
    }

    /** -------------------string相关操作--------------------- */

    /**
     * 设置指定 key 的值
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 获取指定 key 的值
     * @param key
     * @return
     */
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 返回 key 中字符串值的子字符
     * @param key
     * @param start
     * @param end
     * @return
     */
    public String getRange(String key, long start, long end) {
        return redisTemplate.opsForValue().get(key, start, end);
    }

    /**
     * 将给定 key 的值设为 value ,并返回 key 的旧值(old value)
     *
     * @param key
     * @param value
     * @return
     */
    public Object getAndSet(String key, String value) {
        return redisTemplate.opsForValue().getAndSet(key, value);
    }

    /**
     * 对 key 所储存的字符串值,获取指定偏移量上的位(bit)
     *
     * @param key
     * @param offset
     * @return
     */
    public Boolean getBit(String key, long offset) {
        return redisTemplate.opsForValue().getBit(key, offset);
    }

    /**
     * 批量获取
     *
     * @param keys
     * @return
     */
    public List<String> multiGet(Collection<String> keys) {
        return redisTemplate.opsForValue().multiGet(keys);
    }

    /**
     * 设置ASCII码, 字符串'a'的ASCII码是97, 转为二进制是'01100001', 此方法是将二进制第offset位值变为value
     *
     * @param key 位置
     * @param value
     *            值,true为1, false为0
     * @return
     */
    public boolean setBit(String key, long offset, boolean value) {
        return redisTemplate.opsForValue().setBit(key, offset, value);
    }

    /**
     * 将值 value 关联到 key ,并将 key 的过期时间设为 timeout
     *
     * @param key
     * @param value
     * @param timeout
     *            过期时间
     * @param unit
     *            时间单位, 天:TimeUnit.DAYS 小时:TimeUnit.HOURS 分钟:TimeUnit.MINUTES
     *            秒:TimeUnit.SECONDS 毫秒:TimeUnit.MILLISECONDS
     */
    public void setEx(String key, String value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    /**
     * 只有在 key 不存在时设置 key 的值
     *
     * @param key
     * @param value
     * @return 之前已经存在返回false,不存在返回true
     */
    public boolean setIfAbsent(String key, String value) {
        return redisTemplate.opsForValue().setIfAbsent(key, value);
    }

    /**
     * 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始
     *
     * @param key
     * @param value
     * @param offset
     *            从指定位置开始覆写
     */
    public void setRange(String key, String value, long offset) {
        redisTemplate.opsForValue().set(key, value, offset);
    }

    /**
     * 获取字符串的长度
     *
     * @param key
     * @return
     */
    public Long size(String key) {
        return redisTemplate.opsForValue().size(key);
    }

    /**
     * 批量添加
     *
     * @param maps
     */
    public void multiSet(Map<String, String> maps) {
        redisTemplate.opsForValue().multiSet(maps);
    }

    /**
     * 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在
     *
     * @param maps
     * @return 之前已经存在返回false,不存在返回true
     */
    public boolean multiSetIfAbsent(Map<String, String> maps) {
        return redisTemplate.opsForValue().multiSetIfAbsent(maps);
    }

    /**
     * 增加(自增长), 负数则为自减
     *
     * @param key
     * @return
     */
    public Long incrBy(String key, long increment) {
        return redisTemplate.opsForValue().increment(key, increment);
    }

    /**
     *
     * @param key
     * @return
     */
    public Double incrByFloat(String key, double increment) {
        return redisTemplate.opsForValue().increment(key, increment);
    }

    /**
     * 追加到末尾
     *
     * @param key
     * @param value
     * @return
     */
    public Integer append(String key, String value) {
        return redisTemplate.opsForValue().append(key, value);
    }

    /** -------------------hash相关操作------------------------- */

    /**
     * 获取存储在哈希表中指定字段的值
     *
     * @param key
     * @param field
     * @return
     */
    public Object hGet(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    /**
     * 获取所有给定字段的值
     *
     * @param key
     * @return
     */
    public Map<Object, Object> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 获取所有给定字段的值
     *
     * @param key
     * @param fields
     * @return
     */
    public List<Object> hMultiGet(String key, Collection<Object> fields) {
        return redisTemplate.opsForHash().multiGet(key, fields);
    }

    public void hPut(String key, String hashKey, String value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    public void hPutAll(String key, Map<String, String> maps) {
        redisTemplate.opsForHash().putAll(key, maps);
    }

    /**
     * 仅当hashKey不存在时才设置
     *
     * @param key
     * @param hashKey
     * @param value
     * @return
     */
    public Boolean hPutIfAbsent(String key, String hashKey, String value) {
        return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
    }

    /**
     * 删除一个或多个哈希表字段
     *
     * @param key
     * @param fields
     * @return
     */
    public Long hDelete(String key, Object... fields) {
        return redisTemplate.opsForHash().delete(key, fields);
    }

    /**
     * 查看哈希表 key 中,指定的字段是否存在
     *
     * @param key
     * @param field
     * @return
     */
    public boolean hExists(String key, String field) {
        return redisTemplate.opsForHash().hasKey(key, field);
    }

    /**
     * 为哈希表 key 中的指定字段的整数值加上增量 increment
     *
     * @param key
     * @param field
     * @param increment
     * @return
     */
    public Long hIncrBy(String key, Object field, long increment) {
        return redisTemplate.opsForHash().increment(key, field, increment);
    }

    /**
     * 为哈希表 key 中的指定字段的整数值加上增量 increment
     *
     * @param key
     * @param field
     * @param delta
     * @return
     */
    public Double hIncrByFloat(String key, Object field, double delta) {
        return redisTemplate.opsForHash().increment(key, field, delta);
    }

    /**
     * 获取所有哈希表中的字段
     *
     * @param key
     * @return
     */
    public Set<Object> hKeys(String key) {
        return redisTemplate.opsForHash().keys(key);
    }

    /**
     * 获取哈希表中字段的数量
     *
     * @param key
     * @return
     */
    public Long hSize(String key) {
        return redisTemplate.opsForHash().size(key);
    }

    /**
     * 获取哈希表中所有值
     *
     * @param key
     * @return
     */
    public List<Object> hValues(String key) {
        return redisTemplate.opsForHash().values(key);
    }

    /**
     * 迭代哈希表中的键值对
     *
     * @param key
     * @param options
     * @return
     */
    public Cursor<Entry<Object, Object>> hScan(String key, ScanOptions options) {
        return redisTemplate.opsForHash().scan(key, options);
    }

    /** ------------------------list相关操作---------------------------- */

    /**
     * 通过索引获取列表中的元素
     *
     * @param key
     * @param index
     * @return
     */
    public Object lIndex(String key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }

    /**
     * 获取列表指定范围内的元素
     *
     * @param key
     * @param start
     *            开始位置, 0是开始位置
     * @param end
     *            结束位置, -1返回所有
     * @return
     */
    public List<String> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 存储在list头部
     *
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPush(String key, String value) {
        return redisTemplate.opsForList().leftPush(key, value);
    }

    /**
     *
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPushAll(String key, String... value) {
        return redisTemplate.opsForList().leftPushAll(key, value);
    }

    /**
     *
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPushAll(String key, Collection<String> value) {
        return redisTemplate.opsForList().leftPushAll(key, value);
    }

    /**
     * 当list存在的时候才加入
     *
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPushIfPresent(String key, String value) {
        return redisTemplate.opsForList().leftPushIfPresent(key, value);
    }

    /**
     * 如果pivot存在,再pivot前面添加
     *
     * @param key
     * @param pivot
     * @param value
     * @return
     */
    public Long lLeftPush(String key, String pivot, String value) {
        return redisTemplate.opsForList().leftPush(key, pivot, value);
    }

    /**
     *
     * @param key
     * @param value
     * @return
     */
    public Long lRightPush(String key, String value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

    /**
     *
     * @param key
     * @param value
     * @return
     */
    public Long lRightPushAll(String key, String... value) {
        return redisTemplate.opsForList().rightPushAll(key, value);
    }

    /**
     *
     * @param key
     * @param value
     * @return
     */
    public Long lRightPushAll(String key, Collection<String> value) {
        return redisTemplate.opsForList().rightPushAll(key, value);
    }

    /**
     * 为已存在的列表添加值
     *
     * @param key
     * @param value
     * @return
     */
    public Long lRightPushIfPresent(String key, String value) {
        return redisTemplate.opsForList().rightPushIfPresent(key, value);
    }

    /**
     * 在pivot元素的右边添加值
     *
     * @param key
     * @param pivot
     * @param value
     * @return
     */
    public Long lRightPush(String key, String pivot, String value) {
        return redisTemplate.opsForList().rightPush(key, pivot, value);
    }

    /**
     * 通过索引设置列表元素的值
     *
     * @param key
     * @param index
     *            位置
     * @param value
     */
    public void lSet(String key, long index, String value) {
        redisTemplate.opsForList().set(key, index, value);
    }

    /**
     * 移出并获取列表的第一个元素
     *
     * @param key
     * @return 删除的元素
     */
    public Object lLeftPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
     *
     * @param key
     * @param timeout
     *            等待时间
     * @param unit
     *            时间单位
     * @return
     */
    public Object lBLeftPop(String key, long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().leftPop(key, timeout, unit);
    }

    /**
     * 移除并获取列表最后一个元素
     *
     * @param key
     * @return 删除的元素
     */
    public Object lRightPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    /**
     * 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
     *
     * @param key
     * @param timeout
     *            等待时间
     * @param unit
     *            时间单位
     * @return
     */
    public Object lBRightPop(String key, long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().rightPop(key, timeout, unit);
    }

    /**
     * 移除列表的最后一个元素,并将该元素添加到另一个列表并返回
     *
     * @param sourceKey
     * @param destinationKey
     * @return
     */
    public Object lRightPopAndLeftPush(String sourceKey, String destinationKey) {
        return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
                destinationKey);
    }

    /**
     * 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
     *
     * @param sourceKey
     * @param destinationKey
     * @param timeout
     * @param unit
     * @return
     */
    public Object lBRightPopAndLeftPush(String sourceKey, String destinationKey,
                                        long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
                destinationKey, timeout, unit);
    }

    /**
     * 删除集合中值等于value得元素
     *
     * @param key
     * @param index
     *            index=0, 删除所有值等于value的元素; index>0, 从头部开始删除第一个值等于value的元素;
     *            index<0, 从尾部开始删除第一个值等于value的元素;
     * @param value
     * @return
     */
    public Long lRemove(String key, long index, String value) {
        return redisTemplate.opsForList().remove(key, index, value);
    }

    /**
     * 裁剪list
     *
     * @param key
     * @param start
     * @param end
     */
    public void lTrim(String key, long start, long end) {
        redisTemplate.opsForList().trim(key, start, end);
    }

    /**
     * 获取列表长度
     *
     * @param key
     * @return
     */
    public Long lLen(String key) {
        return redisTemplate.opsForList().size(key);
    }

    /** --------------------set相关操作-------------------------- */

    /**
     * set添加元素
     *
     * @param key
     * @param values
     * @return
     */
    public Long sAdd(String key, String... values) {
        return redisTemplate.opsForSet().add(key, values);
    }

    /**
     * set移除元素
     *
     * @param key
     * @param values
     * @return
     */
    public Long sRemove(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }

    /**
     * 移除并返回集合的一个随机元素
     *
     * @param key
     * @return
     */
    public Object sPop(String key) {
        return redisTemplate.opsForSet().pop(key);
    }

    /**
     * 将元素value从一个集合移到另一个集合
     *
     * @param key
     * @param value
     * @param destKey
     * @return
     */
    public Boolean sMove(String key, String value, String destKey) {
        return redisTemplate.opsForSet().move(key, value, destKey);
    }

    /**
     * 获取集合的大小
     *
     * @param key
     * @return
     */
    public Long sSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    /**
     * 判断集合是否包含value
     *
     * @param key
     * @param value
     * @return
     */
    public Boolean sIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    /**
     * 获取两个集合的交集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set<String> sIntersect(String key, String otherKey) {
        return redisTemplate.opsForSet().intersect(key, otherKey);
    }

    /**
     * 获取key集合与多个集合的交集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sIntersect(String key, Collection<String> otherKeys) {
        return redisTemplate.opsForSet().intersect(key, otherKeys);
    }

    /**
     * key集合与otherKey集合的交集存储到destKey集合中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long sIntersectAndStore(String key, String otherKey, String destKey) {
        return redisTemplate.opsForSet().intersectAndStore(key, otherKey,
                destKey);
    }

    /**
     * key集合与多个集合的交集存储到destKey集合中
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long sIntersectAndStore(String key, Collection<String> otherKeys,
                                   String destKey) {
        return redisTemplate.opsForSet().intersectAndStore(key, otherKeys,
                destKey);
    }

    /**
     * 获取两个集合的并集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sUnion(String key, String otherKeys) {
        return redisTemplate.opsForSet().union(key, otherKeys);
    }

    /**
     * 获取key集合与多个集合的并集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sUnion(String key, Collection<String> otherKeys) {
        return redisTemplate.opsForSet().union(key, otherKeys);
    }

    /**
     * key集合与otherKey集合的并集存储到destKey中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long sUnionAndStore(String key, String otherKey, String destKey) {
        return redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey);
    }

    /**
     * key集合与多个集合的并集存储到destKey中
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long sUnionAndStore(String key, Collection<String> otherKeys,
                               String destKey) {
        return redisTemplate.opsForSet().unionAndStore(key, otherKeys, destKey);
    }

    /**
     * 获取两个集合的差集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set<String> sDifference(String key, String otherKey) {
        return redisTemplate.opsForSet().difference(key, otherKey);
    }

    /**
     * 获取key集合与多个集合的差集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sDifference(String key, Collection<String> otherKeys) {
        return redisTemplate.opsForSet().difference(key, otherKeys);
    }

    /**
     * key集合与otherKey集合的差集存储到destKey中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long sDifference(String key, String otherKey, String destKey) {
        return redisTemplate.opsForSet().differenceAndStore(key, otherKey,
                destKey);
    }

    /**
     * key集合与多个集合的差集存储到destKey中
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long sDifference(String key, Collection<String> otherKeys,
                            String destKey) {
        return redisTemplate.opsForSet().differenceAndStore(key, otherKeys,
                destKey);
    }

    /**
     * 获取集合所有元素
     *
     * @param key
     * @return
     */
    public Set<String> setMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 随机获取集合中的一个元素
     *
     * @param key
     * @return
     */
    public Object sRandomMember(String key) {
        return redisTemplate.opsForSet().randomMember(key);
    }

    /**
     * 随机获取集合中count个元素
     *
     * @param key
     * @param count
     * @return
     */
    public List<String> sRandomMembers(String key, long count) {
        return redisTemplate.opsForSet().randomMembers(key, count);
    }

    /**
     * 随机获取集合中count个元素并且去除重复的
     *
     * @param key
     * @param count
     * @return
     */
    public Set<String> sDistinctRandomMembers(String key, long count) {
        return redisTemplate.opsForSet().distinctRandomMembers(key, count);
    }

    /**
     *
     * @param key
     * @param options
     * @return
     */
    public Cursor<String> sScan(String key, ScanOptions options) {
        return redisTemplate.opsForSet().scan(key, options);
    }

    /**------------------zSet相关操作--------------------------------*/

    /**
     * 添加元素,有序集合是按照元素的score值由小到大排列
     *
     * @param key
     * @param value
     * @param score
     * @return
     */
    public Boolean zAdd(String key, String value, double score) {
        return redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     *
     * @param key
     * @param values
     * @return
     */
    public Long zAdd(String key, Set<TypedTuple<String>> values) {
        return redisTemplate.opsForZSet().add(key, values);
    }

    /**
     *
     * @param key
     * @param values
     * @return
     */
    public Long zRemove(String key, Object... values) {
        return redisTemplate.opsForZSet().remove(key, values);
    }

    /**
     * 增加元素的score值,并返回增加后的值
     *
     * @param key
     * @param value
     * @param delta
     * @return
     */
    public Double zIncrementScore(String key, String value, double delta) {
        return redisTemplate.opsForZSet().incrementScore(key, value, delta);
    }

    /**
     * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列
     *
     * @param key
     * @param value
     * @return 0表示第一位
     */
    public Long zRank(String key, Object value) {
        return redisTemplate.opsForZSet().rank(key, value);
    }

    /**
     * 返回元素在集合的排名,按元素的score值由大到小排列
     *
     * @param key
     * @param value
     * @return
     */
    public Long zReverseRank(String key, Object value) {
        return redisTemplate.opsForZSet().reverseRank(key, value);
    }

    /**
     * 获取集合的元素, 从小到大排序
     *
     * @param key
     * @param start
     *            开始位置
     * @param end
     *            结束位置, -1查询所有
     * @return
     */
    public Set<String> zRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    /**
     * 获取集合元素, 并且把score值也获取
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<TypedTuple<String>> zRangeWithScores(String key, long start,
                                                    long end) {
        return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
    }

    /**
     * 根据Score值查询集合元素
     *
     * @param key
     * @param min
     *            最小值
     * @param max
     *            最大值
     * @return
     */
    public Set<String> zRangeByScore(String key, double min, double max) {
        return redisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

    /**
     * 根据Score值查询集合元素, 从小到大排序
     *
     * @param key
     * @param min
     *            最小值
     * @param max
     *            最大值
     * @return
     */
    public Set<TypedTuple<String>> zRangeByScoreWithScores(String key,
                                                           double min, double max) {
        return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max);
    }

    /**
     *
     * @param key
     * @param min
     * @param max
     * @param start
     * @param end
     * @return
     */
    public Set<TypedTuple<String>> zRangeByScoreWithScores(String key,
                                                           double min, double max, long start, long end) {
        return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max,
                start, end);
    }

    /**
     * 获取集合的元素, 从大到小排序
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<String> zReverseRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRange(key, start, end);
    }

    /**
     * 获取集合的元素, 从大到小排序, 并返回score值
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<TypedTuple<String>> zReverseRangeWithScores(String key,
                                                           long start, long end) {
        return redisTemplate.opsForZSet().reverseRangeWithScores(key, start,
                end);
    }

    /**
     * 根据Score值查询集合元素, 从大到小排序
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<String> zReverseRangeByScore(String key, double min,
                                            double max) {
        return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
    }

    /**
     * 根据Score值查询集合元素, 从大到小排序
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<TypedTuple<String>> zReverseRangeByScoreWithScores(
            String key, double min, double max) {
        return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key,
                min, max);
    }

    /**
     *
     * @param key
     * @param min
     * @param max
     * @param start
     * @param end
     * @return
     */
    public Set<String> zReverseRangeByScore(String key, double min,
                                            double max, long start, long end) {
        return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max,
                start, end);
    }

    /**
     * 根据score值获取集合元素数量
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Long zCount(String key, double min, double max) {
        return redisTemplate.opsForZSet().count(key, min, max);
    }

    /**
     * 获取集合大小
     *
     * @param key
     * @return
     */
    public Long zSize(String key) {
        return redisTemplate.opsForZSet().size(key);
    }

    /**
     * 获取集合大小
     *
     * @param key
     * @return
     */
    public Long zZCard(String key) {
        return redisTemplate.opsForZSet().zCard(key);
    }

    /**
     * 获取集合中value元素的score值
     *
     * @param key
     * @param value
     * @return
     */
    public Double zScore(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 移除指定索引位置的成员
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Long zRemoveRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().removeRange(key, start, end);
    }

    /**
     * 根据指定的score值的范围来移除成员
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Long zRemoveRangeByScore(String key, double min, double max) {
        return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
    }

    /**
     * 获取key和otherKey的并集并存储在destKey中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long zUnionAndStore(String key, String otherKey, String destKey) {
        return redisTemplate.opsForZSet().unionAndStore(key, otherKey, destKey);
    }

    /**
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long zUnionAndStore(String key, Collection<String> otherKeys,
                               String destKey) {
        return redisTemplate.opsForZSet()
                .unionAndStore(key, otherKeys, destKey);
    }

    /**
     * 交集
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long zIntersectAndStore(String key, String otherKey,
                                   String destKey) {
        return redisTemplate.opsForZSet().intersectAndStore(key, otherKey,
                destKey);
    }

    /**
     * 交集
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long zIntersectAndStore(String key, Collection<String> otherKeys,
                                   String destKey) {
        return redisTemplate.opsForZSet().intersectAndStore(key, otherKeys,
                destKey);
    }

    /**
     *
     * @param key
     * @param options
     * @return
     */
    public Cursor<TypedTuple<String>> zScan(String key, ScanOptions options) {
        return redisTemplate.opsForZSet().scan(key, options);
    }
}

七、redis配置

redis配置文件对单位的大小写不敏感

1、INCLUDE

可以同时引用多个配置文件,将多个合并为一个

2、网络配置(NETWORK)

bind 127.0.0.1 -::1		# 默认是本地访问,可以绑定自定义的远程ipprotected-mode yes		# 保护模式,默认是开启的,如果开启远程连接,需要改为noport 6379				# 端口,默认是6379

3、通用设置(GENERAL)

daemonize yes	#是否以守护进程的方式运行,默认是nopidfile /var/run/redis_6379.pid		#如果是后台方式运行需要指定一个pid文件# 日志# Specify the server verbosity level.# This can be one of:# debug (a lot of information, useful for development/testing)# verbose (many rarely useful info, but not a mess like the debug level)# notice (moderately verbose, what you want in production probably)# warning (only very important / critical messages are logged)loglevel noticedatabases 16	#默认的数据库数量 16 个always-show-logo no		#是否总是显示logo,默认为no

4、快照(SNAPSHOTTING)

因为Redis是内存数据库,如果没有持久化,在断电的时候就会丢失数据,所以设置在规定的时间内,执行了多少次操作,则会持久化到文件 .rdb或.aof

save 3600 1		#在3600秒之内,如果至少有1个key改变了,就进行持久化操作save 300 100	#在300秒之内,如果至少有100个key改变了,就进行持久化操作save 60 10000	#在60秒之内,如果至少有10000个key改变了,就进行持久化操作stop-writes-on-bgsave-error yes	#持久化如果出错,是否还继续工作rdbcompression yes		# 是否压缩rdb文件(压缩会消耗一定的cpu资源)rdbchecksum yes		#保存rdb文件的时候,是否进行错误检查dbfilename dump.rdb		#rdb文件保存的名称dir ./				# rdb文件保存的目录

5、安全(SECURITY)

requirepass 123456		#密码,默认为空# 除了在配置文件中设置密码之外,还可以在redis-cli中使用命令设置set config requirepass "password"get config requirepass	#获取当前密码

6、客户端配置(CLIENT)

maxclients 10000		# 限制能连接上redis的最大客户端的数量是10000

7、内存管理(MEMORY MANAGEMENT)

maxmemory <bytes>	# 配置最大的内存容量,一般不用配置,会有一个默认值maxmemory-policy noeviction 	# 内存达到上限的处理策略,有以下6种策略    1、volatile-lru:只对设置了过期时间的key进行LRU(默认值)     2、allkeys-lru : 删除lru算法的key       3、volatile-random:随机删除即将过期key       4、allkeys-random:随机删除       5、volatile-ttl : 删除即将过期的       6、noeviction : 永不过期,返回错误

8、AOF(APPEND ONLY MODE)

appendonly no	# 默认不开启aof模式,大部分情况下,rdb完全够用appendfilename "appendonly.aof"		# aof持久化文件名# appendfsync always	# 每次修改都会sync,消耗性能appendfsync everysec	# 每秒执行一次sync,可能会丢失这1秒的数据# appendfsync no		# 不执行sync,这个时候操作系统自己同步数据,速度最快

八、Redis持久化

1、RDB(Redis DataBase)

在指定时间间隔内将内存种的数据集快照写入磁盘(快照),它恢复的时候就是将快照文件直接读到内存里。

Redis会单独创建一个子进程来进行持久化,会将数据写入到一个临时文件(默认是dump.rdb),待持久化的过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。

如果需要进行大规模的数据恢复,且对于数据的完整性不是非常敏感,那rdb方式比aof更加高效。rdb的缺点就是最后一次持久化的数据可能会丢失。

优点

  • 适合大规模的数据恢复

  • 对数据的完整性要求不高

缺点

  • 需要一定的时间间隔进行操作,如果意外宕机,最后的一次修改的数据就没有了
  • 创建子进程的时候会占用一定的内存空间
2、AOF(Append Only File)

以日志的形式来记录每个写操作,将Redis执行过程中的所有写操作都记录下来,只许追加文件,不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件将写指令全部执行一次,以恢复之前的数据。

优点

  • 每次修改都同步,文件的完整性会更好!
  • 每秒同步一次,可能会丢失1秒的数据
  • 从不同步效率最高

缺点

  • 相对于数据文件来说,aof远大于rdb,修复的速度也比rdb慢
  • aof运行效率比rdb慢

综上,一般默认使用rdb即可

九、Redis发布订阅

# 订阅一个或多个符合给定模式的频道
PSUBSCRIBE pattern [pattern ...]

# 查看订阅与发布系统状态
pubsub subcommand [argument [argument...]]

# 将信息发送到指定的频道
PUBLISH channel message

# 退订所有给定模式的频道
PUNSUBSCRIBE [pattern [pattern ...]]

# 订阅给定的一个或多个频道的信息
SUBSCRIBE channel [channel ...]
	
#指退订给定的频道
UNSUBSCRIBE [channel [channel ...]]
  • 测试
# 客户端
PUBSUB channels		# 查看订阅的系统信息
(empty array)
127.0.0.1:6379> PSUBSCRIBE mychannel	# 订阅频道
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "mychannel"
3) (integer) 1
1) "pmessage"
2) "mychannel"
3) "mychannel"
4) "hello"

# 发布端
127.0.0.1:6379> PUBLISH mychannel hello
(integer) 1

使用场景:实时聊天(消息)系统、公众号

十、Redis集群

目的:读写分离,提高性能

1、环境搭建

至少需要3台服务器,实现一主二从

搭建集群,只需要配置从库即可,因为redis默认自己就是主节点

  • 查看当前库的信息
127.0.0.1:6379> info replication
# Replication
role:master		# 角色(默认是matser)
connected_slaves:0	# 连接的从库数量
master_failover_state:no-failover
master_replid:83c5e0005e4381fc1928f19b30ac8fd418dffe0d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
  • 设置主从关系
# 这只主库信息,找老大
SLAVEOF 127.0.0.1 6379
OK
127.0.0.1:6380> info replication

role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:14
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:69bf0d2fa327c6bcde284d66fe0d1d394da0f9ca
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:14
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:14
  • 配置完毕可以查看主库状态
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=154,lag=0
slave1:ip=127.0.0.1,port=6381,state=online,offset=154,lag=0
master_failover_state:no-failover
master_replid:69bf0d2fa327c6bcde284d66fe0d1d394da0f9ca
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:168
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:168

注意:以上是临时的设置主从关系,需要永久的有效,需要到配置文件中配置

# 配置文件中配置 REPLICATION 中的信息
replicaof <masterip> <masterport> # replicaof 127.0.0.1 6379

# 如果主库有密码则需要配置虾米那一项
masterauth <master-password>  	# masterauth 123456

主机可以写,从机只能读!

复制原理

slave启动成功后会发送一个sync同步命令

Master 接到命令后会在后台启动存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕后,master将传送整个数据文件到slave,并完成一次完全同步。

复制分为 全量复制和增量复制:

  • 全量复制:slave服务在接收到数据库文件数据后,将其存盘并加载到内存中
  • 增量复制:master继续将新的所有收集到的修改的命令一次传到slave中,完成同步

但是只要是重新连接上的master,一次完全同步(全量复制)将被自动执行!

2、主从切换

  • 当主机宕机后,需要手动配置新节点为master,其他的节点重新设置主库为新的master
# 在将要被设为master的节点中设置
slaveof no one

哨兵模式

改进手动配置的繁琐,哨兵模式会自动选取新的master。

哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,他会独立运行。其原理是哨兵通过发动命令,等待Redis服务器相应,从而监控多个Redis实例

作用:

  • 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器
  • 当哨兵监测到master宕机,会自动将slave切换为master,然后通用发布订阅模式通知其他的从服务器没修改配置文件,让它们切换主机

但是一个哨兵进行对redis服务器进行监控,可能会出现问题,所以,我们可以使用多个哨兵进行监控。各个哨兵之间还会形成监控,于是形成了多哨兵模式。

实现

配置哨兵配置文件 sentinel.conf

# sentinel monitor 被监控对象 host port 权重
sentinel monitor myredis host port 1

启动哨兵进程

redis-sentinel sentinel.conf

优点

  • 哨兵集群,基于主从复制模式,所有主从配置的优点,它都有
  • 主从可以自动切换,故障可以转移,系统可用性会更好

缺点

  • 不容易在线扩容,集群容量一旦到达上限,在线扩容就会十分麻烦
  • 实现哨兵模式的配置繁琐

3、哨兵模式配置

# Example sentinel.conf
 
# 哨兵sentinel实例运行的端口 默认26379
port 26379
 
# 哨兵sentinel的工作目录
dir /tmp
 
# 哨兵sentinel监控的redis主节点的 ip port 
# master-name  可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
  sentinel monitor mymaster 127.0.0.1 6379 2
 
# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
 
 
# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000
 
# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,
这个数字越小,完成failover所需的时间就越长,
但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1
 
 
 
# 故障转移的超时时间 failover-timeout 可以用在以下这些方面: 
#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
#3.当想要取消一个正在进行的failover所需要的时间。  
#4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
# 默认三分钟
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000
 
# SCRIPTS EXECUTION
 
#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
#对于脚本的运行结果有以下规则:
#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
#若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
#一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
 
#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,
这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,
一个是事件的类型,
一个是事件的描述。
如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
#通知脚本
# sentinel notification-script <master-name> <script-path>
  sentinel notification-script mymaster /var/redis/notify.sh
 
# 客户端重新配置主节点参数脚本
# 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
# 以下参数将会在调用脚本时传给脚本:
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
# 目前<state>总是“failover”,
# <role>是“leader”或者“observer”中的一个。 
# 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
 sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

十一、Redis的缓存穿透与雪崩

1、缓存穿透

用户想要查询一个数据,发现redis的内存中没有(即缓存没有命中),于是向持久层数据库查询,发现也没有,于是本次查询失败。当用户跟多的时候,缓存都没有命中,于是都去请求了持久层的数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了 缓存穿透

解决方案

  • 布隆过滤器

    ​ 对所有可能查询的数据的参数以hash形式存储,在控制层先进性校验,不符合则丢弃,从而避免对底层存储系统的查询压力

  • 缓存空对象

    ​ 当存储层未命中,即使返回空对象也将其缓存起来,同时也会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护后端数据源

    但是这个方法存在两个问题:

    • 如果空值能够被缓存起来,这就意味着需要更多的空间存储更多的键值对,因为这当中可能会有很多的空值的键
    • 即使对空值设置了过期时间,还是会存在缓存层和存储层的数据有一段时间不一致,这对保持一致性的业务会有影响

2、缓存击穿

一个key非常热点,在不停的扛着大并发,大并发集中对这一点进行访问,当这个key失效的瞬间。持续的大并发就会穿破缓存,直接请求数据库。此时,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存。会导致数据库瞬间压力过大。

解决方案

  • 设置热点永不过期

    从缓存层面来看,没有设置过期时间,是不会出现key失效瞬间产生的问题

  • 加互斥锁

    分布式锁:使用分布式锁,保证每个key同时只有一个线程去查询后台服务,其他线程没有获得分布式锁的权限只能等待。

3、缓存雪崩

缓存雪崩是指,在某一个时间段,缓存集中过期。或者Redis宕机

解决方案

  • redis高可用

    搭建redis集群,实现高可用

  • 限流降级

    在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。

  • 数据预热

    在正式部署服务之前,把可能的数据先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间尽量均匀。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
尚硅谷是一个教育机构,他们提供了一份关于Redis学习笔记。根据提供的引用内容,我们可以了解到他们提到了一些关于Redis配置和使用的内容。 首先,在引用中提到了通过执行命令"vi /redis-6.2.6/redis.conf"来编辑Redis配置文件。这个命令可以让你进入只读模式来查询"daemonize"配置项的位置。 在引用中提到了Redis会根据键值计算出应该送往的插槽,并且如果不是该客户端对应服务器的插槽,Redis会报错并告知应该前往的Redis实例的地址和端口。 在引用中提到了通过修改Redis的配置文件来指定Redis的日志文件位置。可以使用命令"sudo vim /etc/redis.conf"来编辑Redis的配置文件,并且在文件中指定日志文件的位置。 通过这些引用内容,我们可以得出结论,尚硅谷的Redis学习笔记涵盖了关于Redis的配置和使用的内容,并提供了一些相关的命令和操作示例。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Redis学习笔记--尚硅谷](https://blog.csdn.net/HHCS231/article/details/123637379)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Redis学习笔记——尚硅谷](https://blog.csdn.net/qq_48092631/article/details/129662119)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值