Redis的简单使用-完善ing
一、安装
1、liunx安装
- 获取redis资源
wget http://download.redis.io/releases/redis-4.0.8.tar.gz
- 解压到自己指定目录
tar -zxvf redis-4.0.8.tar.gz
- 安装之前需要可能需要安装的插件
#安装gcc
yum install gcc-c++
- 解压完出现redis-4.0.8的目录,进入目录执行安装命令make
make
#如果出现 Jemalloc/jemalloc.h:没有那个文件或目录
make distclean之后再make
- 执行make install
make install
- 查看默认安装目录:usr/local/bin
Redis-benchmark:性能测试工具,可以在自己本子运行,看看自己本子性能如何
Redis-check-aof:修复有问题的AOF文件
Redis-check-dump:修复有问题的dump.rdb文件
Redis-cli:客户端,操作入口
Redis-sentinel:redis集群使用
Redis-server:Redis服务器启动命令
- 拷贝默认的redis配置文件
redis.conf
并修改
#拷贝一份到自己指定的目录
mkdir /usr/local/redis/etc
mv redis.conf /usr/local/redis/etc
#修改配置文件
vim redis.conf 将daemonize no 改成 yes,让服务在后台启动
- 根据修改的
redis.conf
启动
/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf
- 设置开机启动Redis(可配可不配)
vim /etc/rc.local //在里面添加内容:/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf (意思就是开机调用这段开启redis的命令)
- 关闭
redis-cli -p 6379 shutdown
- 卸载
rm -rf /usr/local/redis //删除安装目录
rm -rf /usr/bin/redis-* //删除所有redis相关命令脚本
rm -rf /root/download/redis-4.0.4 //删除redis解压文件夹
2、docker安装
- 查找Redis
docker search redis
- 下载镜像
docker pull redis
- 查看镜像
docker images
- 下载redis.conf配置文件,修改默认值
- bind 127.0.0.1 #注释掉这部分,这是限制redis只能本地访问
- protected-mode no #默认yes,开启保护模式,限制为本地访问
- requirepass #打开注释设置密码
- daemonize yes#默认no,改为yes意为以守护进程方式启动,可后台运行,除非kill进程(可选),改>- 为yes会使配置文件方式启动redis失败
- dir ./ #输入本地redis数据库存放文件夹(可选)
- appendonly yes #redis持久化(可选)
- 启动
docker run -p 6379:6379 --name myredis -v /usr/local/docker/redis.conf:/etc/redis/redis.conf -v /usr/local/docker/data:/data -d redis redis-server /etc/redis/redis.conf --appendonly yes
解释说明:
- -p 6379:6379 端口映射:前表示主机部分,:后表示容器部分。
- –name myredis 指定该容器名称,查看和进行操作都比较方便。
- -v 挂载目录,规则与端口映射相同。
- -d redis 表示后台启动redis
- redis-server /etc/redis/redis.conf 以配置文件启动redis,加载容器内的conf文件,最终找到的是挂载的目录/usr/local/docker/redis.conf
- appendonly yes 开启redis 持久化
- 进入容器查看启动
#查看redis容器id
docker ps
#进入redis
docker exec -it redis /bin/bash
#进入redis客户端
redis-cli
二、五种数据类型
redis命令介绍中文地址: http://redisdoc.com
1、String
string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
string类型是Redis最基本的数据类型,一个redis中字符串value最多可以是512M。
基本命令
命令 | 解释 | 示例 |
---|---|---|
SET key value | 给key设置值为value,如果key已存在,则默认覆盖 | set name aa |
SETNX key value | 只在键 key 不存在的情况下, 将键 key 的值设置为 value | setnx name aa |
SETEX key seconds value | 给key设置value值(覆盖)并设置过期时间为seconds秒(注意单位是秒) | setex name 60 aa |
GET key | 获取key的值如果key不存在则返回nil,如果存在但不为string则返回error | get name |
GETSET key value | 取出key的旧值,并设置新值为value;如果没有旧值就返回nil,保存新值不影响 | getset name aa |
STRLEN key | 返回key值得长度,如果key不存在则返回0 | strlen name |
APPEND key value | 把value追加到key中,如果key不存在则同set | append name bb |
GETRANGE key start end | 截取key从start到end的值 | getrange name 0 3 返回 aab |
INCR key | 给key值加一,key值必须是数值,否则报错;key不存在则赋值0 | incr age |
INCRBY key increment | 同INCR key,区别是加的值为 increment | incrby age 5 |
DECR key | 同INCR key,区别是减一 | decr age |
DECRBY key decrement | 同DECR key,区别是减去指定的值 decrement | decrby age 3 |
MSET key value [key value …] | 一次设置多个键值对(覆盖) | mset name aa age 23 id 10086 |
MSETNX key value [key value …] | 同MSET,区别在于如果有一个key有旧值的情况,则全部撤回 | msetnx name aa age 23 id 10086 |
MGET key [key …] | 一次取出多个值 | mget name age id |
2、哈希hash
Redis hash 是一个键值对集合。
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
类似Java里面的Map<String,Object>
每一个Hash可以存储4294967295个键值对。
基本命令
命令 | 解释 | 示例 |
---|---|---|
HSET hash field value | 将哈希表 hash 中域 field 的值设置为 value(覆盖) | hset hash1 k1 v1 |
HSETNX hash field value | 当且仅当域 field 尚未存在于哈希表的情况下, 将它的值设置为 value | hset hash1 k2 v2 |
HGET hash field | 返回哈希表中给定域的值 | hget hash1 k1 |
HEXISTS hash field | 检查给定域 field 是否存在于哈希表 hash 当中 | hexists hash1 k1 |
HDEL key field [field …] | 删除哈希表 key 中的一个或多个指定域 | hdel hash1 k1 k2 |
HLEN key | 返回哈希表 key 中域的数量 | hlen hash1 |
HSTRLEN key field | 返回哈希表 key 中, 与给定域 field 相关联的值的字符串长度(string length) | hstrlen hash1 k1 |
HINCRBY key field increment | 为哈希表 key 中的域 field 的值加上增量 increment | hincrby hash1 k1 10 |
HMSET key field value [field value …] | 同时将多个 field-value (域-值)对设置到哈希表 key 中 | hmset hash2 k1 v1 k2 v2 |
HMGET key field [field …] | 返回哈希表 key 中,一个或多个给定域的值 | hmget hash2 k1 k2 |
HKEYS key | 返回哈希表 key 中的所有域 | hkeys hash2 返回 k1 k2 |
HVALS key | 返回哈希表 key 中所有域的值 | hvals hash2 返回 v1 v2 |
HGETALL key | 返回哈希表 key 中,所有的域和值(以列表的形式) | hgetall hash2 返回 k1 v1 k2 v2 |
3、列表list
有序,可重复。按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)。它的底层实际是个链表,所以向列表两端添加元素的时间复杂度为0(1),获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的。List中可以包含的最大元素数量是4294967295。
应用场景:1.最新消息排行榜。2.消息队列,以完成多程序之间的消息交换。可以用push操作将任务存在list中(生产者),然后线程在用pop操作将任务取出进行执行。(消费者)
基本命令
命令 | 解释 | 示例 |
---|---|---|
LPUSH key value [value …] | 将一个或多个值 value 插入到列表 key 的表头(从左往右) | lpush list1 v1 v2 |
LPUSHX key value | 将值 value 插入到列表 key 的表头,当且仅当 key 存在并且是一个列表 | lpushx list2 v1 v2 |
RPUSH key value [value …] | 将一个或多个值 value 插入到列表 key 的表尾(从右往左) | rpush list3 v1 v2 |
RPUSHX key value | 将值 value 插入到列表 key 的表尾,当且仅当 key 存在并且是一个列表 | rpushx list4 v1 v2 |
LRANGE key start stop | 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定:0表示第一个值;-1表示倒数第一个值 | lrange list1 0 -1 |
LPOP key | 从左往右(表头)依次取出并销毁列表 key 中的值 | lpop list1 执行两次结果分别为:v2 v1 |
RPOP key | 从右往左(表尾)依次取出并销毁列表 key 中的值 | rpop list2 执行两次结果分别为:v1 v2 |
RPOPLPUSH source destination | 取出 source 表尾的值放到 destination 的表头 | rpoplpush list1 list2 |
LREM key count value | 移除 key 列表中 count 个值等于 value 的元素。count >0从左往右;count >0从右往左;count=0全部 | lrem list1 2 v1 |
LLEN key | 返回列表 key 的长度 | llen list1 |
LINDEX key index | 返回列表 key 中,下标为 index 的元素。0:第一个;-1:最后一个 | lindex list1 0 |
LSET key index value | 将列表 key 下标为 index 的元素的值替换为 value | lset list1 1 v3 |
LTRIM key start stop | 保留 key 列表 start 到 stop 直接的值,其余全部删除 | ltrim 0 2 |
4、集合set
无须,不可重复。它是通过HashTable实现实现的,和列表一样,在执行插入和删除以及判断是否存在某元素时,效率是很高的。集合最大的优势在于可以进行交集并集差集操作。Set可包含的最大元素数量是4294967295。
应用场景:1.利用交集求共同好友。2.利用唯一性,可以统计访问网站的所有独立IP。3.好友推荐的时候根据tag求交集,大于某个threshold(临界值的)就可以推荐
基本命令
命令 | 解释 | 示例 |
---|---|---|
SADD key member [member …] | 将一个或多个 member 元素加入到集合 key 当中 | sadd set1 v1 v2 |
SMEMBERS key | 返回集合 key 中的所有成员 | smembers set1 |
SCARD key | 返回集合 key 的元素个数 | scard set1 |
SISMEMBER key member | 判断 member 元素是否集合 key 的成员 | sismember set1 v1 |
SPOP key | 随机取出一个元素(销毁) | spop set1 |
SRANDMEMBER key [count] | 随机取出 count 个元素(不销毁),count默认为1 | srandmember set1 |
SREM key member [member …] | 移除集合 key 中的一个或多个 member 元素 | srem set1 v1 v2 |
SMOVE source destination member | 将 member 元素从 source 集合移动到 destination 集合 | smove set1 set2 v1 |
SINTER key [key …] | 返回多个 key 集合的交集 | sinter set1 set2 |
SINTERSTORE destination key [key …] | 同上,返回的交集放到 destination 中, destination也可以是某个 key | sinterstore set1 set1 set2 |
SUNION key [key …] | 返回多个 key 集合的并集 | sunion set1 seet2 |
SUNIONSTORE destination key [key …] | 同上,并集放到 destination 中 | sunionstore set3 set1 set2 |
SDIFF key [key …] | 返回第一个 key 集合中有的,后边多个 key 集合多没有的值(取差集) | sdiff set1 set2 |
SDIFFSTORE destination key [key …] | 同上,差集放到 destination 中 | sdiffstore set3 set1 set2 |
5、有序集合zset
有序,不可重复。zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。
redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。score 值可以是整数值或双精度浮点数。
和Set很像,都是字符串的集合,都不允许重复的成员出现在一个set中。他们之间差别在于有序集合中每一个成员都会有一个分数(score)与之关联,Redis正是通过分数来为集合中的成员进行从小到大的排序。尽管有序集合中的成员必须是唯一的,但是分数(score)却可以重复。
应用场景:可以用于一个大型在线游戏的积分排行榜,每当玩家的分数发生变化时,可以执行zadd更新玩家分数(score),此后在通过zrange获取几分top ten的用户信息。适合做排行榜,排序需要一个分数属性
基本命令
命令 | 解释 | 示例 |
---|---|---|
ZADD key score member [[score member] [score member] …] | 将一个或多个 member 元素及其 score 值加入到有序集 key 当中,如果 member 存在则更新他的 score 值 | zadd zset1 0 v1 1 v2 2 v3 |
ZRANGE key start stop [WITHSCORES] | 返回有序集 key 中,指定区间内的成员(score从小到大)。使用WITHSCORES让元素和他的score以列表的形式一并返回 | zrange zset1 0 -1 显示所有值 |
ZREVRANGE key start stop [WITHSCORES] | 同上,score从大到小。start 和 stop 表示第几个值 | zrevrange zset1 0 -1 |
ZSCORE key member | 返回有序集 key 中,成员 member 的 score 值 | zscore zset1 v1 |
ZINCRBY key increment member | 为有序集 key 的成员 member 的 score 值加上增量 increment | zincrby zset1 10 v1 |
ZCARD key | 返回有序集合 key 的元素个数 | zcard zset1 |
ZCOUNT key min max | 返回有序集 key 中, score 值在 min 和 max 之间的成员的个数 | zcount zset1 0 2 |
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] | 返回有序集 key 中,所有 score 值介于 min 和 max 之间元素(按 score 从小到大) | zrangebyscore zset1 -inf +inf 获取所有值 |
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count] | 同上,按 score 从大到小 | zrevrangebyscore zset1 1 10 |
ZRANK key member | 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。0表示第一 | zrank zset1 v1 |
ZREVRANK key member | 同上,按 score 从大到小排序 | zrevrank zset1 v1 |
ZREM key member [member …] | 移除有序集 key 中的一个或多个成员 | zrem zset1 v1 v2 |
ZREMRANGEBYRANK key start stop | 移除有序集 key 中,指定排名(rank)区间内的所有成员 | ZREMRANGEBYRANK zset1 0 2 |
ZREMRANGEBYSCORE key min max | 移除有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员 | ZREMRANGEBYSCORE zset1 |
Redis持久化
- AOF
AOF是一种通过记录操作命令的的方式来达到持久化的目的,正是因为记录操作命令而不是数据,所以在恢复数据的时候需要 redo 这些操作命令(当然 AOF 也有重写机制从而使命令减少),如果操作日志太多,恢复过程就会很慢,可能会影响 Redis 的正常使用 - RDB
RDB 是一种内存快照,把内存的数据在某一时刻的状态记录下来,所以通过 RDB 恢复数据只需要把 RDB 文件读入内存就可以恢复数据(具体实现细节还没去了解)
但这里有两个需要注意的问题
- 对哪些数据做快照,关系到快照的执行顺序
- 做快照时,还能对数据做增删改吗?这会关系到 Redis 是否阻塞,因为如果在做快照时,还能对数据做修改,说明 Redis 还能处理写请求,如果不能对数据做修改,就不能处理写请求,要等执行完快照才能处理写请求,这样会影响性能
来看第一个问题
- RDB 是对全量数据需要快照,全量数据会使 RDB 文件大,发文件写到磁盘就会耗时,因为 Redis 是单线程,会不会阻塞主线程?(这一点始终都要考虑的点)
Redis 实现 RDB 的方式有两种
①save:在主线程中执行,会导致阻塞
②bgsave:创建子线程来执行,不会阻塞,这是默认的
所以可以使用 bgsave 来对全量内存做快照,不影响主进程
来看第二个问题
- 在做快照时,我们是不希望数据被修改的,但是如果在做快照过程中,Redis 不能处理写操作,这对业务是会造成影响的,但上面刚说完 bgsave 进行快照是非阻塞的呀,这是一个常见的误区:避免阻塞和正常的处理写操作不是一回事。用 bgsave 时,主线程是没有被阻塞,可以正常处理请求,但为了保证快照的完整性,只能处理读请求,因为不能修改正在执行快照的数据。显然为了快照而暂停写是不能被接受的。Redis 采用操作系统提供的写时复制技术(Copy-On-Write 即 COW),在执行快照的同时,可以正常的处理写操作
bgsave 子线程是由主线程 fork 生成,可以共享主线程的所有内存数据,所以 bgsave 子线程是读取主线程的内存数据并把他们写入 RDB 文件的
如果主线程对这些数据只执行读操作,两个线程是互不影响的。如果主线程要执行写造作,那么这个数据就会被复制一份,生成一个副本,然后 bgsave 子线程会把这个副本数据写入 RDB 文件,这样主线程就可以修改原来的数据了。这样既保证了快照的完整性,也保证了正常的业务进行
那如何更好的使用 RDB 呢?
- RDB 的频率不好把握,如果频率太低,两次快照间一旦有宕机,就可能会丢失很多数据。如果频率太高,又会产生额外开销,主要体现在两个方面:
①频繁将全量数据写入磁盘,会给磁盘带来压力,多个快照竞争有效的磁盘带宽
②bgsave 子线程是通过 fork 主线程得来,前面也说了,bgsave 子线程是不会阻塞主线程的,但 fork 这个创建过程是会阻塞主线程的,而且主线程内存越大,阻塞时间越长
最好的方法是全量快照+增量快照,即进行一次 RDB 后,后面的增量修改用 AOF 记录两次全量快照之间的写操作,这样可以避免频繁 fork 对主线程的影响。同时避免 AOF 文件过大,重写的开销