REDIS FAQ

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

安装

https://github.com/ServiceStack/redis-windows

RedisManager连接指定ip的redis server

修改6379.conf

bind 127.0.0.1 

改为 

bind 127.0.0.1 物理IP

生产环境启动停止

REDIS FAQ_第1张图片

REDIS FAQ_第2张图片

测试连接是否正常

REDIS FAQ_第3张图片

多数据库

redis默认包含16个数据库 以数字命名且不可以更改 不同应用程序的数据应该存储在不同的redis实例中

redis数据类型

字符串类型

  • keys exists

REDIS FAQ_第4张图片 删除多个key 可以使用redis-cli keys "*" | xargs redis-cli del

  • type

REDIS FAQ_第5张图片

  • incr decr incrby decrby strlen

REDIS FAQ_第6张图片

  • 位操作 SETBIT GETBIT可以用来设置用户的性别 如SETBIT 用户id 1

散列类型

  • hsetnx hkeys hvals

REDIS FAQ_第7张图片

列表类型

集合类型相当于JAVA语言的List 元素有序 可重复 使用链表实现

LPUSH LPOP RPUSH RPOP 底层使用链表实现 首位添加删除快 按索引查找慢

LPUSH是向左边增加元素 如LPUSH name 1 2 3 在列表显示顺序是 3 2 1 搭配使用可以实现先进后出的栈 和 先进先出的队列

REDIS FAQ_第8张图片

rpoplpush

REDIS FAQ_第9张图片

集合类型

集合类型相当于JAVA语言的HashTable,元素无序 不重复 底层是散列表 值是空值 查询元素的复杂度是o(1)

srandmember scard sdiff spop

REDIS FAQ_第10张图片

有序集合

有序集合使用散列表和跳跃表实现 常用命令有zadd,zrange,zreverange,zscore,zrangebyscore,zincrby,zcard,zrank,zcount,zrem

REDIS FAQ_第11张图片

REDIS FAQ_第12张图片

事务

多个命令可以用一下方式实现事务

multi ... ... exec

排序

sort命令可以对数字进行排序,如果想要对字母排序 需要加上ALPHA sort和mysql数据库的order by很相似,可以加上desc实现倒序 limit实现分页 sort还可以和by结合 使用参考键的值进行排序

REDIS FAQ_第13张图片

sort后面还可以使用GET获得其他属性 sort末尾使用store表示存储排序的结果到一个指定key的列表中

任务队列

列表可以实现任务队列

如果要订阅多个任务 使用bpop queue:1 queue:2 0 可以实现优先级队列,不论queue:2中有多少任务 都会优先消费queue:1中任务

脚本

redis 2.6以后可以在redis中执行Lua脚本 使用脚本可以减少网络开销,实现原子操作以及复用

EVAL

REDIS FAQ_第14张图片

沙盒和随机数

redis禁止在脚本中执行文件和系统调用相关的命令 且禁止使用全局变量 保证脚本多次执行结果都相同 且保证服务器的安全

持久化

redis支持rdb以快照持久化数据 以及aof方式进行持久化 一般两者结合使用

RDB方式

REDIS FAQ_第15张图片

以下配置表示15分钟内有1次key值变更 则进行快照

save 900 1 save 300 10 save 60 10000

在服务重启或手动备份时 需要手动执行快照 推荐使用BGSAVE命令进行异步快照(同时redis还能接受并处理客户端指令 不会导致线程堵塞) redis本身的定时快照也是使用异步方式

flushall除了清空数据库外,如果配置了自动快照,也会执行快照操作

此外redis在主从复制开始时候,无论是否配置了自动快照,都会执行快照操作

快照原理

默认快照文件存在dump.rdb文件中 可以修改dir文件夹以及dbfilename来修改文件路径

REDIS FAQ_第16张图片

rdb方式快照在redis异常退出时 会丢失最后一次快照后的数据

AOF方式

AOF方式可以防止进程终止导致的数据丢失 AOF方式把redis的每一条写命令存储到硬盘文件中 AOF通过appendonly打开 默认存储位置和rdb文件一样 都是通过dir指定 需要手动修改以下命令打开AOF持久化

REDIS FAQ_第17张图片

redis允许同时开启rdb和aof方式 以保证数据安全

集群

redis主数据库可以有多个从数据库 通过配置文件在slaveof属性来配置 也可以用redis-server --slaveof 127.0.0.1 6379 启动命令来设置 主数据库不需要配置

REDIS FAQ_第18张图片

用info replication查看主从信息

哨兵

一般对每个主从数据库单独配置哨兵进程 这样避免哨兵单点故障 且进行领头哨兵选举时 更具有代表性。当哨兵监控到主数据库下线时 会进行领头哨兵选举 等选举出领头哨兵后 使用该领头哨兵进行故障恢复 quorum参数代表至少需要几个哨兵节点同意 一般是n/2 +1 n表示哨兵节点数

选举出领头哨兵后 则需要选出新的主数据库 依次从优先级slave-priority 复制数据偏移量(最完整) 最小的运行id顺序进行选择 在选出主数据库后 会把发生故障的旧的数据库升级为从数据库

配置集群需要设置每个节点的cluster-enabled yes 哨兵可以看成是集群的子集 集群通过插槽把key均匀地分布到不同的主数据库节点上 而哨兵则是对主从数据库进行故障监控及恢复

插槽的分配

在集群中 所有的key被分配给16384个插槽 具体是根据key的进行CRC16算法计算出散列值 然后对16384取余 这样就能分配到16384个插槽中。 key值的有效部分如果包含{value} 则有效部分为value,如果是其他情况 则有效部分为全部key值 可以利用此规则 将有关联的key分配到同样插槽中 即同样节点中 可以通过cluster slots查看插槽的分布

来一张redis集群示意图

REDIS FAQ_第19张图片

集群故障恢复

集群某主数据库故障会导致存储在该节点上的插槽内的key写入失败 故障集群的故障恢复使用Raft算法(和哨兵选举一样)从数据库向其他节点发送请求,要求选举自己作为主数据库 如果超过半数以上节点同意 则此从数据库升级为主数据库 原来写入主数据库插槽内的key写入到该从数据库上

遇到问题

在线上三台物理机部署三主三从,使用cluster模式时,pinpoint监控显示JedisClustr经常有以下异常信息 JedisMovedDataException MOVED 3867 172.19.103.57:6380

对应源码如下

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package redis.clients.jedis;

import redis.clients.jedis.exceptions.JedisAskDataException;
import redis.clients.jedis.exceptions.JedisClusterException;
import redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException;
import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.exceptions.JedisMovedDataException;
import redis.clients.jedis.exceptions.JedisRedirectionException;
import redis.clients.util.JedisClusterCRC16;

public abstract class JedisClusterCommand {
    private JedisClusterConnectionHandler connectionHandler;
    private int redirections;
    private ThreadLocal askConnection = new ThreadLocal();

    public JedisClusterCommand(JedisClusterConnectionHandler connectionHandler, int maxRedirections) {
        this.connectionHandler = connectionHandler;
        this.redirections = maxRedirections;
    }

    public abstract T execute(Jedis var1);

    public T run(String key) {
        if (key == null) {
            throw new JedisClusterException("No way to dispatch this command to Redis Cluster.");
        } else {
            return this.runWithRetries(key, this.redirections, false, false);
        }
    }

    private T runWithRetries(String key, int redirections, boolean tryRandomNode, boolean asking) {
        if (redirections <= 0) {
            throw new JedisClusterMaxRedirectionsException("Too many Cluster redirections?");
        } else {
            Jedis connection = null;

            Object var7;
            try {
                if (asking) {
                    connection = (Jedis)this.askConnection.get();
                    connection.asking();
                    asking = false;
                } else if (tryRandomNode) {
                    connection = this.connectionHandler.getConnection();
                } else {
                    connection = this.connectionHandler.getConnectionFromSlot(JedisClusterCRC16.getSlot(key));
                }

                Object var6 = this.execute(connection);
                return var6;
            } catch (JedisConnectionException var12) {
                if (tryRandomNode) {
                    throw var12;
                }

                this.releaseConnection(connection);
                connection = null;
                var7 = this.runWithRetries(key, redirections - 1, true, asking);
            } catch (JedisRedirectionException var13) {
                this.releaseConnection(connection);
                connection = null;
                if (var13 instanceof JedisAskDataException) {
                    asking = true;
                    this.askConnection.set(this.connectionHandler.getConnectionFromNode(var13.getTargetNode()));
                } else {
					//如果不是JedisMovedDataException,则向上抛出JedisClusterException
                    if (!(var13 instanceof JedisMovedDataException)) {
                        throw new JedisClusterException(var13);
                    }
					//JedisMovedDataException抛出,则刷新slot缓存
                    this.connectionHandler.renewSlotCache();
                }
				//重试执行命令
                var7 = this.runWithRetries(key, redirections - 1, false, asking);
                return var7;
            } finally {
                this.releaseConnection(connection);
            }

            return var7;
        }
    }

    private void releaseConnection(Jedis connection) {
        if (connection != null) {
            connection.close();
        }

    }
}

JedisCluster在执行命令时,如果收到JedisMovedDataException会刷新slot缓存,在没达到最大重试次数前,会再次执行命令。 可能是线上网络偶尔异常导致,JedisCluster自带容错机制,最大重试5次。 暂时不做处理,仅做记录。

ISSUE

(error) MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.

此错误是磁盘空间满了 导致写操作失败 释放部分磁盘空间即可

转载于:https://my.oschina.net/odetteisgorgeous/blog/1519238

你可能感兴趣的:(REDIS FAQ)