# redis-study **Repository Path**: wlyfree/redis-study ## Basic Information - **Project Name**: redis-study - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2019-11-14 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # `Redis(Remote Dictionary Server)` ## 特性特征 全双工的`TCP`连接、单线程处理命令 ## 环境搭建 ### 单机版 ```shell # 下载 [root@VM_0_9_centos package]# wget http://45.252.224.75/files/606300000DDEA4B6/download.redis.io/releases/redis-5.0.4.tar.gz ... # 解压 [root@VM_0_9_centos package]# tar -zxvf redis-5.0.4.tar.gz ... # 进入redis源码目录 [root@VM_0_9_centos package]# cd redis-5.0.4/src # 指定目录安装:/usr/local/soft/redis-5.0.4/ [root@VM_0_9_centos redis-5.0.4]# make PREFIX=/usr/local/soft/redis-5.0.5 install ... # 查看文件 [root@VM_0_9_centos bin]# ll total 32700 -rwxr-xr-x 1 root root 4366648 Nov 5 20:19 redis-benchmark -rwxr-xr-x 1 root root 8101328 Nov 5 20:19 redis-check-aof -rwxr-xr-x 1 root root 8101328 Nov 5 20:19 redis-check-rdb -rwxr-xr-x 1 root root 4806888 Nov 5 20:19 redis-cli lrwxrwxrwx 1 root root 12 Nov 5 20:19 redis-sentinel -> redis-server -rwxr-xr-x 1 root root 8101328 Nov 5 20:19 redis-server ``` ##### `redis bin`目录文件介绍 `redis-benchmark`:`redis`压测工具 `redis-check-aof`:`aof`文件恢复工具 `redis-check-rdb`:`rdb`文件恢复工具 `redis-cli`:`redis client`端 `redis-sentinel`:软链接,`redis server`端工具 `redis-server`:`redis server`端 ## `Redis`特性 ### 命令组 `Connection`:连接、授权 `Keys`:键 `Strings`:字符串 `Lists`:列表 `Sets`:集合 `Sorted Sets`:有序集合 `Hashes`:哈希表 `HyperLogLog`:基数统计算法 `Stream`:`redis 5.0+`新增的数据结构,用来处理日志文件 `Cluster`:集群命令 `Geo`:地理位置 `Scripting`:脚本 `Pub/Sub`:发布订阅 `Transactions`:事务 `Server`:服务管理 ### 管道(`pipeline`) #### `pipeline`使用 `redis`特性:全双工的`TCP`协议、单线程处理客户端命令。 往返时间`RTT(Round Trip Time)`:客户端请求到服务端时间 + 命令执行时间 + 服务端响应给客户端时间 ```shell # 普通命令处理: 请求 -> 命令处理 -> 响应 请求 -> 命令处理 -> 响应 请求 -> 命令处理 -> 响应 # 使用pipeline处理: 请求 -> 命令处理、命令处理、命令处理 -> 响应 ``` `pipeline`会严格保证命令的执行顺序,我们可以理解`pipeline`是一个队列,`pipeline`内的命令是按顺序执行的。 #### `pipeline`对比`scripting` - `pipeline`和`scripting`都耗费一个`RTT` - `pipeline`无法获取上一个命令的返回值,而`scripting`可以 思考:`redis`是长连接还是短连接,如果是长链接,使用`pipeline`是否具备优势呢? ### 脚本(`scripting`) #### 特性 原子性:整个脚本要么执行成功,要么执行失败 阻塞性:`redis`单线程执行命令(包含脚本),所以脚本运行期间,`redis`无法对其他命令或脚本进行读写操作 缓存性:`redis`内部对脚本具有缓存机制 #### 脚本和脚本缓存 `eval`:执行脚本,每次都必须将脚本以参数的形式发送过来,重新编译执行。 `evalsha`:执行脚本,无需传入脚本主体参数,传入脚本的`sha1`即可,从脚本缓存中查询脚本并执行。 `SCRIPT KILL`:杀死正在运行的脚本 `SCRIPT EXISTS`:校验是否存在脚本缓存 `SCRIPT LOAD`:载入脚本缓存,但不执行脚本 `SCRIPT FLUSH`:清除脚本缓存 #### 执行超时 超时时间:默认`5s`,可以在`redis.conf `中` lua-time-limit`参数调整这个配置 超时解决: 1. 记录脚本超时 2. 开始接收其他其他客户端的命令请求,`SCRIPT KILL`、`SHUTDOWN NOSAVE`命令会被处理,其它命令返回`BUSY`错误 3. 执行命令解决问题 - 执行`SCRIPT KILL`:针对只读脚本,直接杀死脚本即可,不会影响数据。 - 执行`SHUTDOWN NOSAVE`:针对有写操作的脚本,可以停止服务器,阻止数据写入磁盘。 #### `redis.call`和`redis.pcall` `redis.call`:抛出异常时,将错误返回给调用者 `redis.pcall`:抛出异常时,将捕获的错误以`lua`表的方式返回 示例: ```shell # key为foo,数据类型是list 127.0.0.1:6379> lpush foo a (integer) 1 # 使用脚本调用redis.call 127.0.0.1:6379> eval "return redis.call('get','foo')" 0 (error) ERR Error running script (call to f_6b1bf486c81ceb7edf3c093f4c48582e38c0e791): @user_script:1: WRONGTYPE Operation against a key holding the wrong kind of value # 使用脚本调用redis.pcall 127.0.0.1:6379> eval "return redis.pcall('get','foo')" 0 (error) WRONGTYPE Operation against a key holding the wrong kind of value ``` ### 发布订阅(`pub/sub`) #### 常用命令 `subscribe`:订阅某些`channel`,按名称匹配 `unsubscribe`:取消订阅某些`channel`,按名称匹配 `psubscribe`:订阅某些给定模式某些`channel`,按表达式匹配 `unpsubscribe`:取消订阅某些给定模式某些`channel`,按表达式匹配 `publish`:发布一条`message`到`channel` `pubsub channels`:列出当前活跃的频道(`subscribe`相关) `pubsub numpat`:返回订阅模式的channel的数量(`psubscribe`相关) `pubsub numsub`:返回给定频道订阅者数量(`subscribe`相关) #### 示例 ```shell # client1:订阅 test.01 test.02 127.0.0.1:6379> subscribe test.01 test.02 Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "test.01" 3) (integer) 1 1) "subscribe" 2) "test.02" 3) (integer) 2 # client2 订阅 test.02 test.03 127.0.0.1:6379> subscribe test.02 test.03 Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "test.02" 3) (integer) 1 1) "subscribe" 2) "test.03" 3) (integer) 2 # clien:3:模式订阅 test.* haha.* 127.0.0.1:6379> psubscribe test.* haha.* Reading messages... (press Ctrl-C to quit) 1) "psubscribe" 2) "test.*" 3) (integer) 1 1) "psubscribe" 2) "haha.*" 3) (integer) 2 # client4:模式订阅 haha.* hehe.* 127.0.0.1:6379> psubscribe haha.* hehe.* Reading messages... (press Ctrl-C to quit) 1) "psubscribe" 2) "haha.*" 3) (integer) 1 1) "psubscribe" 2) "hehe.*" 3) (integer) 2 # client5 # 查看pubsub命令的文档 127.0.0.1:6379> pubsub help 1) PUBSUB arg arg ... arg. Subcommands are: 2) CHANNELS [] -- Return the currently active channels matching a pattern (default: all). 3) NUMPAT -- Return number of subscriptions to patterns. 4) NUMSUB [channel-1 .. channel-N] -- Returns the number of subscribers for the specified channels (excluding patterns, default: none). # 查看当前活跃的channels 127.0.0.1:6379> pubsub channels 1) "test.02" 2) "test.01" 3) "test.03" # 统计订阅模式的数量(不去重),这里匹配到了client3的test.*、haha.*和client4的haha.*、hehe.* 127.0.0.1:6379> pubsub numpat (integer) 4 # 查看某些channel订阅的客户端数量 127.0.0.1:6379> pubsub numsub test.01 test.02 test.03 1) "test.01" 2) (integer) 1 3) "test.02" 4) (integer) 2 5) "test.03" 6) (integer) 1 ``` ### 内存优化 #### 数据压缩 `string`、`hash`、`list`、`set`、、`zset`、`stream` #### 内存配置和回收策略 >当`maxmemory`限制达到的时候,会触发`redis`的内存回收策略(`maxmemory-policy`) ```shell # 文件:redis.conf # 最大内存,32位默认3GB,64位默认为0,即不限制。 maxmemory 100mb # 内存回收策略 maxmemory-policy allkeys-lru # 配置每次回收采样检查的数量,选取访问时间最早的key,调整近似LRU算法的精度(redis lru并非完整LRU实现) maxmemory-samples 5 ``` - `noeviction`:不回收机制,需要申请更多内存时,直接报错。 - `allkeys-lru`:尝试回收最少使用的`key`,使新添加的数据有地方存放。 - `volatile-lru`:同`allkeys-lru`,不过只操作有过期时间的`key`。 - `allkeys-random`:回收随机的键,使新添加的数据有地方存放。 - `volatile-random`:同`allkeys-random`,不过只操作有过期时间的`key。` - `volatile-ttl`:回收有过期时间的`key`,并且优先回收存活时间较短的键,使得新添加的数据有地方存放。 #### 使用位图(`bitmap`) ##### 优势 按位存储必然比较节约内存,`1E`条数据,仅仅占用不到`12M`内存 100000000 ÷ 8 ÷ 1024 ÷ 1024 =‬ 11.920928955078125 ##### 位图操作命令 ```shell # 写入数据,value必须为0或1 setbit key offset value # 读 getbit key offset # 统计 bitcount key [start end] # 查询范围内首个值为0或1的数据,bit只能为0或1 bitpos key bit [start] [end] # 针对一部分key位操作(与、或、非、异或),并将结果存储到destkey中 # operate:AND与、OR或、XOR异或、NOT非 bitop operate destkey key [key...] ``` ##### 适用场景 数据量大、内存小、数据不重复,离散度越低越好 如:统计用户上线次数、在线状态、日活、月活等 ```shell # 如:统计2019年11月11、12号有哪些用户上过线 # 设置用户id为1、2、3、5的用户登录,以用户id作为偏移量,设置相关的bit # 记录11月11号上过线的用户 127.0.0.1:6379> setbit login_2019_11_11 1 1 (integer) 0 127.0.0.1:6379> setbit login_2019_11_11 5 1 (integer) 0 # 记录11月12号上过线的用户 127.0.0.1:6379> setbit login_2019_11_12 1 1 (integer) 0 127.0.0.1:6379> setbit login_2019_11_12 2 1 (integer) 0 127.0.0.1:6379> setbit login_2019_11_12 3 1 (integer) 0 127.0.0.1:6379> setbit login_2019_11_12 5 1 (integer) 0 # 使用OR操作,去重记录上线的用户数据 127.0.0.1:6379> bitop OR login_statistics login_2019_11_11 login_2019_11_12 (integer) 1 # 查看统计结果 127.0.0.1:6379> bitcount login_statistics (integer) 5 ``` #### 对象存储(`json vs hash`) | | 字符串 | 哈希 | | --------------- | ---------------------- | ------------------ | | 编码 | 简单 | 复杂 | | 序列化/反序列化 | 需要 | 无需 | | 内存占用 | 较大 | 较小 | | 对象嵌套 | 支持 | 不支持 | | 数据获取 | 获取整个对象后反序列化 | 只获取对应属性即可 | ### 数据过期 #### 命令 `expire key seconds`:为某个`key`设置过期时间 `persist key`:将某个`key`设置为持久存储 #### 过期策略 ##### 主动策略(概率算法) 1. 随机测试20个`key`是否过期 2. 删除过期的`key` 3. 若过期的`key`超过`25%`,则重复步骤1 ##### 被动策略(惰性删除) 1. 客户端访问某个`key`,发现`key`过期,就将`key`删除 #### 数据持久化 `AOF` - 写入`AOF`文件时`key`已过期,不会被写入`AOF`文件 - 写入`AOF`文件时未过期,数据过期触发删除会向`AOF`文件中追加`DEL`命令 - `AOF`重写不会写入过期的`key` `RDB` - 过期的`key`不会被存储到`RDB`文件中 - 恢复数据时,过期的`key`不会被恢复 ### 分区 #### 分区目的 - 单机内存瓶颈,横向扩展 - 单机网络瓶颈,分摊网络压力 #### 分区方案 - 客户端分区:由客户端决定从哪个节点读写数据 - 代理分区:客户端请求发给代理,由代理层决定从哪个节点读写数据 - 查询路由(服务端分区):客户端任意访问`任意redis实例`,`redis`根据`key`将客户端请求转发到正确的`redis`节点,在`redis cluster`中是采用混合路由的方式,结合客户端帮助,将客户端请求**重定向**到正确`redis`节点。 #### 分区缺点 - 不支持多个`key`的操作 - 不支持跨`key`的事务操作 ### 事务 #### 命令 `multi`:标记事务开始 `exec`:执行所有`multi`之后的命令,并操作`unwatch` `discard`:放弃执行事务,丢弃所有`multi`之后的命令,并操作`unwatch` ```shell # 标记事务开始 127.0.0.1:6379> multi OK # 执行命令会被放入队列,返回QUEUED 127.0.0.1:6379> set a 1 QUEUED 127.0.0.1:6379> set b 2 QUEUED # 执行事务,返回数组 127.0.0.1:6379> exec 1) OK 2) OK # 测试事务执行结果:执行事务 127.0.0.1:6379> get a "1" 127.0.0.1:6379> get b "2" # 标记事务开始 127.0.0.1:6379> multi OK # 执行命令会被放入队列,返回QUEUED 127.0.0.1:6379> set c 3 QUEUED 127.0.0.1:6379> set d 4 QUEUED # 放弃执行事务 127.0.0.1:6379> discard OK # 测试事务执行结果:放弃执行事务 127.0.0.1:6379> get c (nil) 127.0.0.1:6379> get d (nil) ``` `watch`:监视一个`key`,在事务中有条件的执行(`cas`),若`key`被其他事务执行,则事务不会被成功执行 `unwatch`:取消对`key`的监视 ```shell 127.0.0.1:6379> set a 1 OK # client1(线程1): # 1.监控一个key 127.0.0.1:6379> watch a OK # 2.开启事务 127.0.0.1:6379> multi OK # 3.放入命令 127.0.0.1:6379> set a 10 QUEUED 127.0.0.1:6379> set a 100 QUEUED # client2(线程2): # 4.给key为a的数据设置值 127.0.0.1:6379> set a 99 OK # client1(线程1): # 5.执行命令 127.0.0.1:6379> exec (nil) # 6.查看结果:事务未被成功执行 127.0.0.1:6379> get a "99" # 使用unwatch取消对命令监控,事务成功执行 127.0.0.1:6379> set a 1 OK 127.0.0.1:6379> watch a OK 127.0.0.1:6379> set a 2 OK 127.0.0.1:6379> unwatch OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> set a 3 QUEUED 127.0.0.1:6379> set a 4 QUEUED 127.0.0.1:6379> exec 1) OK 2) OK 127.0.0.1:6379> get a "4" ``` #### 执行流程 1. `multi`:标记事务开始 2. 接收客户端命令,并存储到队列中 3. `exec执行`/`discard撤销执行`命令 注意:事务在`exec`命令执行时,才会真正执行。事务本身是一个原子操作,在单线程的`redis`中,也不会被并发执行。 ### 大批量数据插入 #### 步骤 1. 将待插入的数据存储到文件中 2. 将文件转码(需要用到`unix2dos`命令,可使用`yum install unix2dos`安装) 3. 以`pipe mode`执行文件 4. 查询执行结果 #### 示例 ```shell # 准备命令 [root@VM_0_9_centos ~]# cat d1.txt set myk12 v1 zadd zset12 0 a 1 b 3 c sadd sset12 e f g hh set myk22 v2 hset myset12 k1 v1 hmset myset22 k2 v2 k3 v3 k4 v4 set myk32 v3 # 转码 [root@VM_0_9_centos ~]# unix2dos /usr/local/soft/redis-5.0.4/bin/redis-cli unix2dos: Binary symbol found at line 1 unix2dos: Skipping binary file /usr/local/soft/redis-5.0.4/bin/redis-cli # 以`pipe mode`执行命令 [root@VM_0_9_centos ~]# cat d1.txt | /usr/local/soft/redis-5.0.4/bin/redis-cli --pipe All data transferred. Waiting for the last reply... Last reply received from server. errors: 0, replies: 7 # 查看执行结果 [root@VM_0_9_centos ~]# /usr/local/soft/redis-5.0.4/bin/redis-cli # 字符串 127.0.0.1:6379> get myk12 "v1" 127.0.0.1:6379> get myk22 "v2" 127.0.0.1:6379> get myk32 "v3" # 哈希 127.0.0.1:6379> hget myset12 k1 "v1" 127.0.0.1:6379> hmget myset22 k2 k3 k4 1) "v2" 2) "v3" 3) "v4" # sortedSet 127.0.0.1:6379> zscan zset12 0 1) "0" 2) 1) "a" 2) "0" 3) "b" 4) "1" 5) "c" 6) "3" # set 127.0.0.1:6379> sscan sset12 0 1) "0" 2) 1) "hh" 2) "f" 3) "e" 4) "g" ``` ### 分布式锁 #### 实现思路 - 加锁时使用`set`命令,同时设置`nx`和`expire`参数,作为一个原子命令执行 - `nx`保证同一时间只有一个线程可以获取到锁 - `ex`保证锁有过期时间,防止死锁 - `value`包含特殊标识(如随机数)来标识这是哪个线程加的锁,锁只能被加锁的线程释放。 - 释放锁的时候使用`lua脚本`执行 - 不能简单执行`delete`操作,需要在`lua`脚本中判断,当前`delete`的线程是否是加锁的线程 #### 参考代码 ```java package com.wang.redisstudy.lock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisStringCommands; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.data.redis.core.script.RedisScript; import org.springframework.data.redis.core.types.Expiration; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; /** * Redis分布式缓存锁工具类 * 分布式锁的实现思路: * 1.多个线程并发加锁请求,同一时刻只有一个线程能获取到锁 * 2.线程使用完锁后释放锁,保证其他业务能正常获取到锁 * 3.为锁设置一个过期时间,防止锁释放失败导致死锁 *

* * @author wly * @date 2018-08-11 16:16 */ @Component public class RedisLockUtils { private static Logger logger = LoggerFactory.getLogger(RedisLockUtils.class); /** * 锁key的前缀 */ public static final String LOCK_KEY_PREFIX = "redisLock_"; /** * 线程获取锁默认的重试次数 */ public static final int DEFAULT_LOCK_DEFAULT_RETRY_TIMES = 3; /** * 获取锁失败的休眠时间 */ public static final long DEFAULT_LOCK_FAILED_SLEEP_TIME_MILLIS = 500L; /** * 持有锁的默认超时时间 */ public static final long DEFAULT_LOCK_TIME_OUT_SECOND = 10L; @Autowired private StringRedisTemplate stringRedisTemplate; private ThreadLocal lockFlag = new ThreadLocal<>(); /** * 释放锁的LUA脚本 */ public static final String UNLOCK_LUA; /** * 初始化LUA脚本 */ static { StringBuilder sb = new StringBuilder(); sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] "); sb.append("then "); sb.append(" return redis.call(\"del\",KEYS[1]) "); sb.append("else "); sb.append(" return 0 "); sb.append("end "); UNLOCK_LUA = sb.toString(); } /** * 获取锁 * 获取锁成功: * key:LOCK_KEY_PREFIX + businessTag + businessValue,value:UUID * * @param businessTag 业务标识,redis key的一部分,如:商品传goods,订单传order * @param businessValue 业务值,如传1,对businessTag业务标识为1的数据进行加锁 * @return 获取锁的结果,true:成功 false:失败 * @author wly * @date 2018-08-13 09:15:00 */ public boolean lock(String businessTag, String businessValue) { if (StringUtils.isEmpty(businessTag) || StringUtils.isEmpty(businessValue)) { return false; } // 获取锁的次数 int retryTimes = 1; return setRedis(businessTag, businessValue, retryTimes); } public boolean lock(String businessTag, String businessValue, int retryTimes){ if (StringUtils.isEmpty(businessTag) || StringUtils.isEmpty(businessValue)) { return false; } // 获取锁的次数 return setRedis(businessTag, businessValue, retryTimes); } /** * 获取锁 * 获取锁成功: * key:LOCK_KEY_PREFIX + businessTag + businessValue,value:UUID * * @param businessTag 业务标识,redis key的一部分,如:商品传goods,订单传order * @param businessValue 业务值,如传1,对businessTag业务标识为1的数据进行加锁 * @param retryTimes 线程尝试获取锁的次数 * @return 获取锁的结果,true:成功 false:失败 * @author wly * @date 2018-08-13 09:15:00 */ private boolean setRedis(String businessTag, String businessValue, int retryTimes) { boolean result = stringRedisTemplate.execute((RedisConnection redisConnection) -> { // 生成UUID记录当前哪个线程持有锁 String value = UUID.randomUUID().toString(); lockFlag.set(value); // redis key String key = LOCK_KEY_PREFIX + businessTag + businessValue; // 原子操作:setNx + expire return redisConnection.set(key.getBytes(), value.getBytes(), Expiration.seconds(DEFAULT_LOCK_TIME_OUT_SECOND), RedisStringCommands.SetOption.SET_IF_ABSENT); }); // 获取锁失败,在有效次数内进行重试 if (!result && retryTimes <= DEFAULT_LOCK_DEFAULT_RETRY_TIMES) { // 休眠一段时间 try { TimeUnit.MILLISECONDS.sleep(DEFAULT_LOCK_FAILED_SLEEP_TIME_MILLIS); } catch (InterruptedException e) { logger.error("获取锁休眠线程中断", e); Thread.currentThread().interrupt(); } // 递归获取锁 return setRedis(businessTag, businessValue, ++retryTimes); } return result; } /** * 释放锁 * 不能直接用del操作释放锁,因为如果业务执行时间过长(超过持有锁的时间)就会导致释放掉其他线程的锁 * 添加锁的时候,我们为每个线程生成了一个UUID作为标识,通过LUA脚本检索到当前线程持有的锁并释放 * * @param businessTag 业务标识,redis key的一部分,如:商品传goods,订单传order * @param businessValue 业务值,如传1,对businessTag业务标识为1的数据进行加锁 * @return 释放锁的结果,true:成功 false:失败 * @author wly * @date 2018-08-13 09:15:00 */ public boolean releaseLock(String businessTag, String businessValue) { if (StringUtils.isEmpty(businessTag) || StringUtils.isEmpty(businessValue)) { return false; } // key String key = LOCK_KEY_PREFIX + businessTag + businessValue; // value String value = lockFlag.get(); List args = new ArrayList<>(); args.add(lockFlag.get()); // 脚本对象 RedisScript script = new DefaultRedisScript<>(UNLOCK_LUA, Long.class); // 执行脚本 Long result = stringRedisTemplate.execute(script, Collections.singletonList(key), value); lockFlag.remove(); return result > 0; } } ``` ## `Redis`使用 ### `redis-cli` #### 连接命令 > `./redis-cli -h ${host} -p ${port} -a ${password}` ```shell # 使用客户端连接,使用默认host:localhost,默认端口:6379 [root@VM_0_9_centos config]# /usr/local/soft/redis-5.0.4/bin/redis-cli # 设置密码 127.0.0.1:6379> config set requirepass '123456' OK # CTRL+C断开客户端连接 # 重新连接,不指定密码 [root@VM_0_9_centos config]# /usr/local/soft/redis-5.0.4/bin/redis-cli # 查询数据失败 127.0.0.1:6379> get a (error) NOAUTH Authentication required. # CTRL+C断开客户端连接 # 指定-h:host、-p:port、-a:password [root@VM_0_9_centos config]# /usr/local/soft/redis-5.0.4/bin/redis-cli -h localhost -p 6379 -a 123456 Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. # 查询数据成功 localhost:6379> get a "1" ``` #### 帮助命令 > `help @category`:显示有关给定类别的所有命令。类别:`@generic`,`@list`,`@set`,`@sorted_set`,`@hash`, `@pubsub`,`@transactions`,`@connection`,`@server`,`@scripting`, `@hyperloglog` > > `help commandName`:显示具体命令的帮助。 ```shell # 查看事务相关命令文档 localhost:6379> help @transactions DISCARD - summary: Discard all commands issued after MULTI since: 2.0.0 EXEC - summary: Execute all commands issued after MULTI since: 1.2.0 MULTI - summary: Mark the start of a transaction block since: 1.2.0 UNWATCH - summary: Forget about all watched keys since: 2.2.0 WATCH key [key ...] summary: Watch the given keys to determine execution of the MULTI/EXEC block since: 2.2.0 # 查看set命令的帮助文档 localhost:6379> help set SET key value [expiration EX seconds|PX milliseconds] [NX|XX] summary: Set the string value of a key since: 1.0.0 group: string ``` #### 统计`redis`状态 > 持续统计`redis`的状态 ```shell [root@VM_0_9_centos config]# /usr/local/soft/redis-5.0.4/bin/redis-cli --stat ------- data ------ --------------------- load -------------------- - child - keys mem clients blocked requests connections 21 836.65K 1 0 1691 (+0) 55 21 836.65K 1 0 1692 (+1) 55 ``` 统计`redis big keys` ```shell [root@VM_0_9_centos config]# /usr/local/soft/redis-5.0.4/bin/redis-cli --bigkeys Scanning the entire keyspace to find biggest keys as well as average sizes per key type. You can use -i 0.1 to sleep 0.1 sec per 100 SCAN commands (not usually needed). [00.00%] Biggest string found so far 'myk12' with 2 bytes [00.00%] Biggest set found so far 'sset12' with 4 members [00.00%] Biggest hash found so far 'myset22' with 3 fields [00.00%] Biggest string found so far 'aa' with 4 bytes [52.38%] Biggest string found so far 'hehe' with 10 bytes [52.38%] Biggest zset found so far 'zset12' with 3 members -------- summary ------- Sampled 21 keys in the keyspace! Total key length in bytes is 132 (avg len 6.29) Biggest hash found 'myset22' has 3 fields Biggest string found 'hehe' has 10 bytes Biggest set found 'sset12' has 4 members Biggest zset found 'zset12' has 3 members 0 lists with 0 items (00.00% of keys, avg size 0.00) 2 hashs with 4 fields (09.52% of keys, avg size 2.00) 17 strings with 43 bytes (80.95% of keys, avg size 2.53) 0 streams with 0 entries (00.00% of keys, avg size 0.00) 1 sets with 4 members (04.76% of keys, avg size 4.00) 1 zsets with 3 members (04.76% of keys, avg size 3.00) ``` #### 监控`redis`延迟 ```shell # 持续统计`redis`延迟,原理是发送`ping`命令 [root@VM_0_9_centos config]# /usr/local/soft/redis-5.0.4/bin/redis-cli --latency min: 0, max: 1, avg: 0.18 (66 samples) # 持续统计`redis`延迟 # 与`latency`区别:每15s重新生成一份统计 [root@VM_0_9_centos config]# /usr/local/soft/redis-5.0.4/bin/redis-cli --latency-history min: 0, max: 1, avg: 0.16 (1459 samples) -- 15.01 seconds range min: 0, max: 1, avg: 0.16 (1459 samples) -- 15.01 seconds range min: 0, max: 1, avg: 0.13 (134 samples)^C ``` #### 远程备份`rdb`文件 ```shell # 远程备份`rdb`文件 [root@VM_0_9_centos config]# /usr/local/soft/redis-5.0.4/bin/redis-cli --rdb /tmp/dump.rdb SYNC sent to master, writing 669 bytes to '/tmp/dump.rdb' Transfer finished with success. # 查看备份的`rdb`文件 [root@VM_0_9_centos config]# ll /tmp/ | grep dump.rdb -rw-r--r-- 1 root root 669 Nov 19 20:18 dump.rdb ``` ### `redis`配置 > 注意:包含空格的参数可以用双引号括起来 #### 配置方式 - 命令行传参 - 配置文件`redis.conf` - 运行时更改`基于config set、config get命令` #### `redis.conf`详解 参考`redis-default-英文.conf`、`redis-default-中文.conf` ### 主从复制 #### 复制流程 - `slave`连接`master`,正常连接`master`发送一连串命令给`slave`做同步 - `master`和`slave`连接超时或断开,重连尝试增量同步数据。 - 增量同步失败,则尝试全量同步数据。