Redis使用

String 基本用法

#查看所有的key
keys  * 
#切换数据库
select 3
#清空当前数据库
flushdb
#清空所有数据库
flushall
#检查key是否存在 返回1/0
exists key
#设置key过期时间 单位秒
expire key 10
#查看key过期时间
ttl key
#查看key类型
type key
#追加key字段 如果没找到key 新建key == set操作
append key "123"
# +1操作 例如阅读量等
incr key
# -1操作 
decr key
# +10 自定义加多少
incrby key 10
# -10 自定义减多少
decrby key 10
​
################################## 
#截取操作 [0,3]
getrange key 03
#截取全部 == get key
getrange key 0-1
#替换操作 从第二个开始替换字符串
setrange key 2 xx
#set的时候 设置过期时间30s
setex key 30"hello"
#分布式锁 尝使用 如果不存在创建key(1) 如果存在创建失败(0) 
setnx key "redis"
##################################
#同时set多个key
mset k1 v1 k2 v2 k3 v3
#同时get多个key
mget k1 k2 k3
# 如果不存在创建 原子性操作(一起成功/一起失败)
msetnx k1 v1 k4 v4
#对象 设置一个user:1 对象 值为json字符来保存一个对象
set user:1 {name:zhangsan,age:3}
#这里的key是一个巧妙的设计 user:{id}:{filed}
mset user:1:name zhangsan user:1:age 3
mget user:1:name user:1:age
####################
#getset 先get在set 如果不存在 返回null 如果存在 获取存在的值 并设置新的值
 getset key aaa  #输出null
get key   #输出aaa
 getset key bbb  #输出aaa
get key  #输出bbb

List

设置规则 可当队列 栈 阻塞队列使用等

所有list命令 “ L ”开头

#LPUSH 将一个值插入到列表头部(左)value可重复
 LPUSH list one
 LPUSH list two 
 LPUSH list three
#range操作 0,-1 == get
 LRANGE list 0-1#返回 three two one 栈顺序
#RPUSH 将一个值插入到列表尾部(右)
 RPUSH list one
#############################
#移除操作
 LPOP list #移除第一个 返回被移除的value
 RPOP list #移除最后一个 返回被移除的value
 
#############################
#根据index获取list值
 lindex list 1# 返回对应值
#############################
#list长度
 Llen list #返回列表长度
############
#移除指定值 移除1个 值为one的
 lrem list 1 one
#移除指定值 移除3个 值为one的
 lrem list 3 one
############################
#trim修剪 截取list 只保留[1,3]的value
 ltrim list 13
############################
# rpoplpush 移除list最后一个元素 添加到新list内
 rpoplpush list otherList
#查看原list
 range otherList 0-1
#查看新list
 lrange otherList 0-1
############################
 exists list
#将list 指定下标的值 替换为另外一个值 更新操作
#将第1个 替换为other 如不存在 报错 必须存在才可用!!!
 lset list 0"other"
############################
#插入 指定已有字段value前 插入other
 linsert list before "已有字段value""other"
#插入 指定已有字段value后 插入other
 linsert list after "已有字段value""other"
list实际是一个链表 可以做队列和栈

Set

所有Set命令 “ S ”开头

值不可重复 无序不重复


#添加
sadd myset "hello"
sadd myset "hello1"
sadd myset "hello2"
sadd myset "hello3"
...
#查看 
smembers myset
#判断 myset里存在hello? 0/1
sismember myset hello
#set集合中的个数
scard myset
#移除set中的指定元素
srem myset hello
#随机抽选一个元素
srandmember myset
#随机抽选指定个数元素
srandmember myset 2
#移除随机的key
spop myset
#移除指定的key 
#移动hello 从myset移动到myset2
smove myset myset2 "hello"
###
#并集 共同关注
#数字集合类
# - 差集 sdiff
 sdiff key1 key2
# - 交集 sinter
 sinter key1 key2
# - 并集 sunion
 sunion key1 key2 

使用场景:

  • 微博 A用户所有关注的人放进一个set内 所有粉丝放进一个set内

  • 共同关注 共同好友 二度好友 推荐好友等

Hash

Map集合 key-map! 本质和string没有太大区别 基于K-V

set myhasj field hello

#添加
hset myhash field1 hello
#获取一个字段值
hget myhash field1
#同时添加多个
hmset myhash field1 hello1 field2 world
#同时获取多个值
hmget myhash field1 field2
#获取全部值
hgetall myhash
#删除hash指定的key字段
hdel myhash field1
#####################
#获取长度 
hlen myhash
#判断hash中的key存在
hexists myhash field1
#####################
#只获得key
hkeys myhash
#只获得value
hvalue myhash
#####################
# +1
incr  myhash field3 1
# -1
decr  myhash field3 1
#如果不存在 则添加成功 0/1
hsetnx myhash field4 "aa"​

Zset

在set基础上 增加一个值 set k1 v1 zset k1 score1 v1

#添加一个值 one薪资1块钱 
zadd myzset 1 one 
#添加多个值two薪资2块钱 three薪资3块钱 
zadd myzset 2 two 3 three
#获取 range用法 0,-1 等于get
zrange myzset 0-1
#排序获取 -inf +inf 负无穷-正无穷 从小到大
zrangebyscore myzset -inf+inf
#查看范围 从大到小
zrevrange myzset 0-1
#排序获取 -inf +inf 负无穷-正无穷
zrangebyscore myzset -inf+inf withscores #返回带排序字段附带score(薪水)
#排序获取 -inf +inf 负无穷 到 score(薪水)为2 的
zrangebyscore myzset -inf2 withscores  
#移除 myzset里值为one的 
zrem myzset one
#查看myzset的元素个数
zcard myzset
#获取指定区间的成员数量
zcount myzset 13​

Geospatial

#geoadd 添加经纬度 key 值(纬度 经度 城市名)
geoadd china:city 116.40 39.90 beijing
geoadd china:city 121.47 31.23 shanghai
geoadd china:city 106.50 29.53 chongqing
...
#获取指定城市的信息
geopos china:city beijing 
#获取多个城市的信息
geopos china:city beijing chongqing
#两人之间的距离 单位km 默认m 可选m km mi(英里) ft(英尺)
GEODIST china:city beijing shanghai km
#半径范围 以给定的经纬度为中心 查询 方圆1000 km的城市
georadius china:city 110301000 km
#widthdist 显示到中心距离的位置
georadius china:city 110301000 km widthdist
#widthdist 显示他人的经纬度
georadius china:city 110301000 km widthcoord
#count n 查询n个 筛选出结果
georadius china:city 110301000 km count 2  
################################
# 查询指定位置元素周围的其他元素
georadiusbymember china:city beijing 1000 km
​
# 了解即可 返回11位的Geohash字符串
geohash china:city beijing chongqing​

底层实现原理是zset 可以用zset操作geo
#查看地图中全部元素
zrange china:city 0-1
#移除地图中元素
zrem china:city beijing
zrange china:city 0-1

hyperloglog

基数 不重复的元素

优点 占用的内存是固定的,2^64 不同元素的技术,只需要提供12kb内存 从内存角度 他是首选

网页的uv(一个人访问多次 还是算作一次)

传统方式是用set保存用户id 然后统计set元素个数作为标准判断

这个方式如果保存大量的用户id 比较麻烦!我们的目的是计数 不是保存用户id

0.81%错误率!统计uv任务,可以忽略不计的!

#添加元素 
pfadd mykey a b c d e f g h i j
#查看mykey 不重复的个数
pfcount mykey
pfadd mykey2 i j m n p o l
pfcount mykey2
#将两个合并去掉重复 mykey3 = mykey+mykey2 
pfmerge mykey3 mykey mykey2
pfcount mykey3

如果允许容错 hyperloglog最优选择

不允许容错 使用set

bitmap

位存储

统计疫情感染人数:0 0 0 0 1 1 0 1 0....

统计用户信息,活跃 不活跃 /登录 不登录 / 打卡 不打卡等 两个状态的 都可以使用bitmaps

bitmaps 位图 ,数据结构!都是操作2进制 只有0 1

365天 = 365bit 1字节 = 8 bit 46字节存一年信息

记录周一到周日打卡

#周一:0 ;周二:1 ;周三:2 ;周四:3...
setbit sign 01
setbit sign 11
setbit sign 20
setbit sign 31
setbit sign 40
setbit sign 51
setbit sign 61
#查看某一天是否打卡 
#周四是否打卡
getbit sign 3#返回 0 1
#统计一周的打卡记录
bitcount sign #返回为1 的个数

事务

要么同时成功 要么同时失败 原子性

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

一次性、顺序性、排他性! 执行一系列的命令

-------队列 setsetset 执行----------

redis事务没有隔离级别的概念

所有的命令在事务中 并没有直接执行! 只有发起执行命令的时候才会执行!exec

redis 单挑命令保证原子性 但是事务不保证原子性!

redis的事务:

  • 开始事务(multi)

  • 命令入队(...)

  • 执行事务(exec)

正常执行事务!
# 开启事务
multi
#命令入队 
set k1 v1
set k2 v2
get k2
set k3 v3
# 执行事务
exec
放弃事务
# 开启事务
multi
#命令入队 
set k1 v1
set k2 v2
set k4 v4
# 取消事务 事务队列里所有命令都不会执行
discard 
编译型异常(代码有问题! 命令有错),事务所有命令都不会执行
# 开启事务
multi
#命令入队 
set k1 v1
set k2 v2
getset k4 #报错点
# 执行事务
exec 
运行时异常(1/0) ,如果事务队列存在语法型错误,那么执行命令的时候,其他命令可以正常执行 错误命令抛出异常
set k1 "v1"
# 开启事务
multi
#命令入队 
incr k1 #字符串不能执行+1操作 异常点
set k2 v2
set k3 v3
# 执行事务
exec 
监控! watch

悲观锁

  • 无论做什么都加锁

乐观锁

  • 更新数据的时候去判断一下 在此期间是否有人修改过这个数据,mysql中 用version字段判断

  • 获取version

  • 执行操作

  • 更新的时候比较version

测试watch

正常执行成功!

set money 100
set out 0
# 监视money
watch money 
# 事务正常结束 期间没有发生变动 这个时候正常执行成功
multi
decrby money 20
incrby out 20
exec
测试多线程 改值 使用watch 当redis乐观锁
# 监视money
watch   
# 事务
multi
decrby money 20
incrby out 20
exec # 执行之前 另外一个线程修改了我们的值 这时候就会导致事务执行失败 
# 事务执行失败 放弃监视  
unwatch 
# 重新监视 获取最新的值
watch money
#
...

Jedis

使用java操作redis

什么是Jedis 是官方推荐java连接开发工具 使用java操作redis中间件

Springboot整合

springboot操作数据:spring -data redis mongodb jpa jdbc

springboot2.0 之后原来的jedis 替换为lettuce

jedis :采用的直连 多线程操作的话 不安全 如果想避免不安全 使用jedis pool连接池 更像BIO模式

lettuce:采用netty 实例可以在多线程中进行共享 安全,减少线程数据,更像NIO模式

// 默认的 redistemplate 没有过多的设置 redis对象需要序列化

// 两个泛型都是object 需要强转

整合测试
spring:
    redis:
        host:127.0.0.1
        port:6379
        lettuce:
           pool:
              max-active: 20
              max-wait: -1
              #最大阻塞等待时间(负数表示没限制)
              max-idle: 5
              min-idle: 0
@Autowired
RedisTemplateredisTemplate
// opsForValue() 操作string
// opsForList() 操作list
// opsForSet() opsForGeo() ...
// redisTemplate.opsForValue().方法
// 事务和基本的操作 直接操作

config 配置文件 配自己的redisTemplate

@EnableCaching
@Configuration
publicclassRedisConfigextendsCachingConfigurerSupport {
    /**
     * 固定模板
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    publicRedisTemplate<String, Object>myRedisTemplate(RedisConnectionFactoryredisConnectionFactory) {
        RedisTemplate<String,Object>redisTemplate=newRedisTemplate<>();
//        设置链接工厂
        redisTemplate.setConnectionFactory(redisConnectionFactory);
​
//        序列化配置
        Jackson2JsonRedisSerializerjackson2JsonRedisSerializer=newJackson2JsonRedisSerializer(Object.class);
​
        ObjectMapperom  =newObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
​
        jackson2JsonRedisSerializer.setObjectMapper(om);
// string 序列化
        StringRedisSerializerstringRedisSerializer=newStringRedisSerializer();
​
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
​
        returnredisTemplate;
    }
​
    @Bean
    publicCacheManagercacheManager(RedisConnectionFactoryfactory) {
        RedisSerializer<String>redisSerializer=newStringRedisSerializer();
        Jackson2JsonRedisSerializerjackson2JsonRedisSerializer=newJackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapperom=newObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfigurationconfig=RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManagercacheManager=RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        returncacheManager;
    }
}

redis工具类

/**
* Redis工具类,使用之前请确保RedisTemplate成功注入
*
* @author ye17186
* @version 2019/2/22 10:48
*/
publicclassRedisUtils {
 
    privateRedisUtils() {
    }
 
    @SuppressWarnings("unchecked")
    privatestaticRedisTemplate<String, Object>redisTemplate=SpringUtils
        .getBean("redisTemplate", RedisTemplate.class);
 
    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    publicstaticbooleanexpire(finalStringkey, finallongtimeout) {
 
        returnexpire(key, timeout, TimeUnit.SECONDS);
    }
 
    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功;false=设置失败
     */
    publicstaticbooleanexpire(finalStringkey, finallongtimeout, finalTimeUnitunit) {
 
        Booleanret=redisTemplate.expire(key, timeout, unit);
        returnret!=null&&ret;
    }
 
    /**
     * 删除单个key
     *
     * @param key 键
     * @return true=删除成功;false=删除失败
     */
    publicstaticbooleandel(finalStringkey) {
 
        Booleanret=redisTemplate.delete(key);
        returnret!=null&&ret;
    }
 
    /**
     * 删除多个key
     *
     * @param keys 键集合
     * @return 成功删除的个数
     */
    publicstaticlongdel(finalCollection<String>keys) {
 
        Longret=redisTemplate.delete(keys);
        returnret==null?0 : ret;
    }
 
    /**
     * 存入普通对象
     *
     * @param key Redis键
     * @param value 值
     */
    publicstaticvoidset(finalStringkey, finalObjectvalue) {
 
        redisTemplate.opsForValue().set(key, value, 1, TimeUnit.MINUTES);
    }
 
    // 存储普通对象操作
 
    /**
     * 存入普通对象
     *
     * @param key 键
     * @param value 值
     * @param timeout 有效期,单位秒
     */
    publicstaticvoidset(finalStringkey, finalObjectvalue, finallongtimeout) {
 
        redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
    }
 
    /**
     * 获取普通对象
     *
     * @param key 键
     * @return 对象
     */
    publicstaticObjectget(finalStringkey) {
 
        returnredisTemplate.opsForValue().get(key);
    }
 
    // 存储Hash操作
 
    /**
     * 往Hash中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    publicstaticvoidhPut(finalStringkey, finalStringhKey, finalObjectvalue) {
 
        redisTemplate.opsForHash().put(key, hKey, value);
    }
 
    /**
     * 往Hash中存入多个数据
     *
     * @param key Redis键
     * @param values Hash键值对
     */
    publicstaticvoidhPutAll(finalStringkey, finalMap<String, Object>values) {
 
        redisTemplate.opsForHash().putAll(key, values);
    }
 
    /**
     * 获取Hash中的数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    publicstaticObjecthGet(finalStringkey, finalStringhKey) {
 
        returnredisTemplate.opsForHash().get(key, hKey);
    }
 
    /**
     * 获取多个Hash中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    publicstaticList<Object>hMultiGet(finalStringkey, finalCollection<Object>hKeys) {
 
        returnredisTemplate.opsForHash().multiGet(key, hKeys);
    }
 
    // 存储Set相关操作
 
    /**
     * 往Set中存入数据
     *
     * @param key Redis键
     * @param values 值
     * @return 存入的个数
     */
    publicstaticlongsSet(finalStringkey, finalObject... values) {
        Longcount=redisTemplate.opsForSet().add(key, values);
        returncount==null?0 : count;
    }
 
    /**
     * 删除Set中的数据
     *
     * @param key Redis键
     * @param values 值
     * @return 移除的个数
     */
    publicstaticlongsDel(finalStringkey, finalObject... values) {
        Longcount=redisTemplate.opsForSet().remove(key, values);
        returncount==null?0 : count;
    }
 
    // 存储List相关操作
 
    /**
     * 往List中存入数据
     *
     * @param key Redis键
     * @param value 数据
     * @return 存入的个数
     */
    publicstaticlonglPush(finalStringkey, finalObjectvalue) {
        Longcount=redisTemplate.opsForList().rightPush(key, value);
        returncount==null?0 : count;
    }
 
    /**
     * 往List中存入多个数据
     *
     * @param key Redis键
     * @param values 多个数据
     * @return 存入的个数
     */
    publicstaticlonglPushAll(finalStringkey, finalCollection<Object>values) {
        Longcount=redisTemplate.opsForList().rightPushAll(key, values);
        returncount==null?0 : count;
    }
 
    /**
     * 往List中存入多个数据
     *
     * @param key Redis键
     * @param values 多个数据
     * @return 存入的个数
     */
    publicstaticlonglPushAll(finalStringkey, finalObject... values) {
        Longcount=redisTemplate.opsForList().rightPushAll(key, values);
        returncount==null?0 : count;
    }
 
    /**
     * 从List中获取begin到end之间的元素
     *
     * @param key Redis键
     * @param start 开始位置
     * @param end 结束位置(start=0,end=-1表示获取全部元素)
     * @return List对象
     */
    publicstaticList<Object>lGet(finalStringkey, finalintstart, finalintend) {
        returnredisTemplate.opsForList().range(key, start, end);
    }
}

redis.conf

config get requirepass
#设置密码
config set requirepass "123456"
append only 模式 aof配置
appendonly no #默认不开启aof模式 使用rdb方式持久化
appendfilename "appendonly.aop"#持久化文件名字
#leverysec always(每次写入都同步,消耗性能) no(不执行sync 系统自己同步)
appendfsync leverysec #每秒执行一次同步 可能会丢失这一秒的数据

rdb

rdb保存的文件是dump.rdb

主从复制中 rdb就是备用的! 从机上面!

#conf文件
#60s内 修改5次key -> 生成快照
save 60 5
# 文件名
dbfilename dump.rdb

#删除rdb文件
rm-rf dump.rdb
触发机制

1 save的规则满足 触发rdb规则

2 执行flushall 也会触发rdb规则

3 退出redis 也会产生rdb文件!

备份就会生成一个dump.rdb

如何恢复rdb文件

只需要将rdb文件放在redis启动目录就可以,redis启动的时候回自动检查dump.rdb 恢复数据

2 查看需要存放的位置

config get dir #返回目录 
".../bin" 如果这个目录下存在rdb文件 启动自动恢复
几乎 默认配置就够用了 但是还要做了解

优点:

  • 适合大规模的数据回复

  • 对数据完整性不高

缺点:

  • 需要一定的时间间隔,如果意外宕机 最后一次修改的数据就没了

  • fork进程的时候 会占用内存空间!!

aof(append only file)

将所有命令记录下来 history,恢复的时候执行一遍

appendonly yes#开启 aof模式

appendfilename "appenfonly.aof"

手动配置aof开启

重启redis就可以生效

如果aof配置文件有错误 这时候redis不能启动 需要修复aof配置文件

redis提供了一个修复工具 redis-check-aof --fix appendonly.aof

如果文件正常 重启直接恢复

重写规则 如果aof大于64m ,太大了 ,fork一个新的进程 来讲我们的文件重写
aof默认无限追加
优缺点

优点:

1 每一次修改都同步 文件的完整更好

2 每秒同步一次 可能回丢失一秒的数据

3 从不同步 效率最高

缺点:

1 相对于数据文件来说 aof远远大于rdb 修复速度比rdb慢

2 aof 运行效率 也要比rdb慢 所以我们redis默认是rdb!

redis发布订阅

redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。微信 微博 关注系统

redis 客户端可以订阅任意数量的频道

订阅发布消息图:

发送者

频道

订阅者

展示了频道channel1 以及订阅这个频道的三个客户端client2 client5 client1之间的关系

当有消息通过publish 命令发送给频道 channel1 时 这个消息就会呗发送给订阅他的三个客户端

测试

订阅端

#订阅频道test 实时监听
subscribe test
...
#等待推送
1) "message"#消息
2) "test"#频道名
3) "hello"#内容 

发送端


#发布者发送消息 "hello" 到test频道
publish test "hello"

Redis主从复制

主从复制 是将一台redis服务器的数据 复制到其他的redis服务器 前者称为主节点master 后者成为从节点slave 数据复制是单向的 只能由主节点到从节点 ,master以写为主 slave以读为主.

默认情况下 每台reids服务器都是主节点

主从复制的作用主要包括

  1. 数据冗余:主从复制实现了数据的热备份 是持久化之外的一种数据荣誉方式

  1. 数据故障:当主节点出现问题是 可以由从节点提供服务 实现快速的故障恢复 实际上是一种服务的冗余

  1. 负载均衡:在主从复制的基础上 配和读写分离 可以由主节点提供写服务 由从节点提供读服务,分担服务器负载 尤其是在写少读多的场景下 通过多个从节点分担读负载 可以大大提高redis服务器的并发量

  1. 高可用(集群)基石:除了上述作用以外 主从复制还是哨兵和集群能够实施的基础 因此说主从复制是redis高可用基础

一般来说 要将redis运用于工程项目中 只使用一台服务器是万万不能的(宕机,一主二从)原因如下

  1. 结构上 单个redis服务器会发生单点故障 并且一台服务器需要处理所有的请求负载 压力较大

  1. 容量上 单个redis服务器内存容量有限 就算一台redis内存容量为256G 也布恩那个将所有内存用作redis存储内存 一般来说 单台redis最大使用内存不超过20G

电商网站上的商品 一般都是一次上传

主从复制 读写分离! 80%的情况下都是进行读操作! 减缓服务器的压力 架构中经常使用! 一主二从!

只要在公司中 主从复制是必须要使用的 是为在真是的项目中不可能单机使用redis

环境配置

只配置从库 不用配置主库

info replication #查看当前库信息
#返回值
role:master #角色 master
connected_slaves:0 #没有从机

赋值三个conf配置文件 修改对应信息

  1. 端口

  1. pid名字

  1. log名字

  1. rdb备份文件名字

修改完毕之后 启动三个redis服务

一主二从

默认情况 每台都是主节点 一般情况 配置从机就可以了

a机器 主机

#连接客户端
redis-cli -p6379
#查看 
info replication

b机器 从机

#连接客户端
redis-cli -p6380
# 配置主机
slaveof 127.0.0.1 6379
#查看
info replication
role:slave #当前角色 master
master_host:127.0.0.1 #
master_host:6379 #

c机器 从机

#连接客户端
redis-cli -p6381
# 配置主机
slaveof 127.0.0.1 6379

真实的配置 是配置文件修改 配置文件是暂时的

#============================= replication=================

replicaof <masterip><masterport>

masterauth <master-password>

细节

主机可以写 从机只能读 主机中所有数据都会被从机保存

主机操作

set k1 v1

从机

get k1
set k2 v2 #报错 从机不可写

测试:

  • 主机断开链接 从机依然链接到主机 但是没有写操作 这个时候 主机回来了 从机依旧可以读取 主机写的操作

  • 如果使用命令行配置主从 ·如果重启 变回主机,只要配置为从机 立马就会从主机获取值

复制原理

全量复制:slave 服务在接受到数据库文件数据后 将其存盘并加载到内存中

增量复制:master 继续将新的所有手机的修改命令依次传给slave 完成同步

只要重新链接master 一次性完全同步(全量复制)将自动执行!数据在从机中一定可以看到

手动变成主机

如果主机断开 使用slave no one 手动变成主机

哨兵模式(自动选举)

(自动选取)

概述

主从切换技术的方法是 当主机服务器宕机 需要手动把一台服务器切换为主服务器 这就需要人工干预 费事费力 话会造成一段时间内服务不可用,这不是一种推荐的方式,更多时候 我们有限考虑哨兵模式 redis2.8开始 提供了哨兵架构

监控主机是否故障 如果故障了 根据投票数自动将从库切换为主库

哨兵通过发消息确定是否活着

假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover[故障转移]操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线

测试

我们目前一主二从

1 配置哨兵文件 sentinel.conf

vim sentinel
# sentinel monitor 被检控的名称 host port 主机挂了 slave投票 看让谁接替成为主机 票多者得
sentinel monitor myredis 127.0.0.1 63791

2 启动

redis-sentinel config/sentinel.conf

如果master节点断了 这个时候会从从机中随机选择一个服务器(这里面有一个投票算法)

如果主机回来了 成为从机

优点

  • 基于主从复制 主从配置优点他全有

  • 主从可以切换 故障可以转移 可用性高

  • 主从升级 从手动到自动

缺点

  • redis 不好在线扩容 集群容量一旦达到上线 在线扩容就十分麻烦

  • 实现哨兵的配置很麻烦 选择很多

全部配置
# Example sentinel.conf
# 哨兵sentinel实例运行的端口 默认26379
port 26379
​
# 哨兵sentinel的工作目录
dir /tmp
​
# 哨兵sentinel监控的redis主节点的 ip port
# master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 配置多少个sentinel哨兵统一认为master主节点失联 那么这时客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 63792
​
# 当在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无法正常启动成功。
​
#通知脚本
# shell编程
# 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 # 一般都是由运维来配置!
​

缓存穿透和雪崩

我是在这位大佬的文章里了解的 https://blog.csdn.net/qq_39781497/article/details/126268506

穿透

bloomFilter

击穿

setnx 分布式锁

课程来源 狂神说https://space.bilibili.com/95256449

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis 用法 简单说明ppt 初学者试用。 string: =============================================================================== SET name "John Doe" GET name 批量的读写操作 MSET age 30 sex "male" MGET age sex 存储数字 INCR age INCRBY age 4 GET age DECR age DECRBY age 4 GET age 修改和获取操作 APPEND name " Mr." GET name STRLEN name SUBSTR name 0 3 bit操作 SETBIT bit 10086 1 SETBIT bit 200 1 SETBIT bit 300 1 GETBIT bit 10086 GETBIT bit 100 BITCOUNT bit 设置超时 SETEX key_with_time 10 "same values" ttl key_with_time ttl key_with_time get key_with_time 如果服务器返回 OK ,那么这个客户端获得锁。 如果服务器返回 NIL ,那么客户端获取锁失败,可以在稍后再重试。 第一次成功,第二次失败(nil) set unknown "some data" NX EX 100 set unknown "some data" NX EX 100 key ===================================================================================== keys * SET name huangz EXISTS name DEL name 数据过期设置 SET name "John Doe" TTL name 先用EXISTS命令查看key值是否存在,然后设置了5秒的过期时间 EXISTS name EXPIRE name 5 EXISTS name GET name List ===================================================================================== 基本list操作 LPUSH students "John Doe" LPUSH students "Captain Kirk" LPUSH students "Sheldon Cooper" LLEN students LRANGE students 0 2 LPOP students LLEN students LRANGE students 0 1 LREM students 1 "John Doe" LLEN students LRANGE students 0 -1 多种修改操作 LINSERT students BEFORE "Captain Kirk" "Dexter Morgan" LRANGE students 0 -1 LPUSH students "Peter Parker" LRANGE students 0 -1 LTRIM students 1 3 LLEN students LRANGE students 0 2 LREM students 1 "John Doe" LLEN students LRANGE students 0 1 阻塞行为 确保key都被删除, 为command列表增加一个值,job 列表为空,被跳过,紧接着 command 列表的第一个元素被 DEL job command request LPUSH command "update system..." LPUSH request "visit page" BLPOP job command request 300 BLPOP job command request 300 BLPOP job command request 300 从别的窗口 LPUSH job "aaa" 等待10秒回怎样呢? BLPOP job command request 10 在MULTI/EXEC事务中的BLPOP # 对非空列表进行操作 RPUSH job "programming" MULTI BLPOP job 30 EXEC # 不阻塞,立即返回 # 对空列表进行操作 LLEN job MULTI BLPOP job 30 EXEC #不阻塞,立即返回(nil) SET ===================================================================================== 基本操作 SADD birds crow SADD birds pigeon SADD birds bat SADD mammals dog SADD mammals cat SADD mammals bat SMEMBERS birds SMEMBERS mammals 修改操作 SREM mammals cat SMEMBERS mammals SADD mammals human SMEMBERS mammals SISMEMBER mammals human 集合的子交并补等操作 SINTER birds mammals SUNION birds mammals SDIFF birds mammals Ordered SET ===================================================================================== ZADD days 0 mon ZADD days 1 tue ZADD days 2 wed ZADD days 3 thu ZADD days 4 fri ZADD days 5 sat ZADD days 6 sun ZCARD days ZRANGE days 0 6 ZSCORE days sat ZCOUNT days 3 6 ZRANGE days 0 -1 ZRANGE days 0 -1 WITHSCORES ZREVRANGE days 0 -1 ZREVRANGE days 0 -1 WITHSCORES ZRANGEBYSCORE days 0 6 ZRANGEBYSCORE days 0 6 WITHSCORES ZRANK days sat HASH ===================================================================================== HSET student name "Ganesh" HSET student age 30 HSET student sex "Male" HKEYS student HVALS student HGETALL student HGET student sex HDEL student sex HGETALL student 多值设置 HMSET kid name Akshi age 2 sex Female HMGET kid name age sex 数据库操作 ===================================================================================== DBSIZE # 0 号数据库的 key 数量 SELECT 1 # 切换到 1 号数据库 DBSIZE # 1 号数据库的 key 数量 flushall # 清空所有数据库的所有 key DBSIZE # 不但 1 号数据库被清空了 SELECT 0 # 0 号数据库(以及其他所有数据库)也一样 DBSIZE -在Redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个 key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。 这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。 Publish/Subscribe ===================================================================================== 用一个客户端订阅管道 SUBSCRIBE channelone 另一个客户端往这个管道推送信息 PUBLISH channelone hello PUBLISH channelone world 用一个客户端订阅所有channel开头的信息通道 PSUBSCRIBE channel* 另一个客户端对两个推送信息 PUBLISH channelone hello PUBLISH channeltwo world 事务性 ===================================================================================== NX结尾命令都是判断在这个值没有时才进行某个命令。 SET name "John Doe" SETNX name "Dexter Morgan" GET name Redis还支持自定义的命令组合,通过MULTI和EXEC,将几个命令组合起来执行 SET counter 0 MULTI INCR counter INCR counter INCR counter EXEC GET counter DISCARD命令来中断执行中的命令序列 SET counter 0 MULTI INCR counter INCR counter INCR counter DISCARD GET counter 持久化 ===================================================================================== 数据快照的原理是将整个Redis中存的所有数据遍历一遍存到一个扩展名为rdb的数据文件中。通过SAVE命令可以调用这个过程。 SET name "John Doe" SAVE SET name "Sheldon Cooper" BGSAVE /home/dong1/redis-2.6.16/src/dump.rdb 其日志文件以aof结局,我们一般各为aof文件。要开启aof日志的记录 你需要在配置文件中进行如下设置: appendonly yes 管理命令 ===================================================================================== Redis支持多个DB,默认是16个,你可以设置将数据存在哪一个DB中,不同DB间的数据具有隔离性。也可以在多个DB间移动数据。 SELECT 0 SET name "John Doe" SELECT 1 GET name SELECT 0 MOVE name 1 SELECT 1 GET name DBSIZE INFO FLUSHDB SET name "John Doe" DBSIZE SELECT 1 DBSIZE SELECT 0 FLUSHDB DBSIZE FLUSHALL DBSIZE

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值