Redis的安装分为windows版和linux版
windows直接下载安装包即可
linux安装如下:
node01服务器执行以下命令下载redis安装包
cd /export/softwares
wget http://download.redis.io/releases/redis-3.2.8.tar.gz
node01执行以下命令进行解压redis
cd /export/softwares
tar -zxvf redis-3.2.8.tar.gz -C ../servers/
node01执行以下命令安装C程序运行环境
yum -y install gcc-c++
下载安装较新版本的tcl
node01执行以下命令下载tcl安装包
cd /export/softwares
wget Download tcl8.6.1-src.tar.gz (Tcl)
解压tcl
tar -zxvf tcl8.6.1-src.tar.gz -C ../servers/
进入指定目录
cd ../servers/tcl8.6.1/unix/
./configure
make && make install
node01执行以下命令在线安装tcl
yum -y install tcl
node01执行以下命令进行编译:
cd /export/servers/redis-3.2.8/
make MALLOC=libc 或者使用命令 make 进行编译
make test && make install
node01执行以下命令修改redis配置文件
cd /export/servers/redis-3.2.8/
mkdir -p /export/servers/redis-3.2.8/logs
mkdir -p /export/servers/redis-3.2.8/redisdata
vim redis.conf
bind node01
daemonize yes
pidfile /var/run/redis_6379.pid
logfile "/export/servers/redis-3.2.8/logs/redis.log"
dir /export/servers/redis-3.2.8/redisdata
node01执行以下命令启动redis
cd /export/servers/redis-3.2.8/
src/redis-server
可以使用grep查看一下
ps -ef | grep redis
node01执行以下命令连接redis客户端
cd /export/servers/redis-3.2.8/src
redis-cli -h node01
redis当中一共支持五种数据类型,分别是string字符串类型,list列表类型,集合set类型,hash表类型以及有序集合zset类型,通过这五种不同的数据类型,我们可以实现各种不同的功能,也可以应用与各种不同的场景,
redis的各种数据类型结构如上图:
SETkey value | 设置指定key值 | set hello world |
get key | 获取指定key值 | get hello |
getrange key start end | 返回key中字符串的子字符 |
getrange hello 0 3 |
getset key value | 将给定的key的值设置为value 并返回key的旧值(old value) | getset hello world2 |
mget key1 key2 | 获取一个或多个key值 | mget hello hello2 |
setex key seconds value | 将值value关联到key,并将key的过期时间设定为seconds(以秒为单位) | setex hello 10 world3 |
setnx key value | 只有key不存在时设置key的值 | setnx itcast redisvalue |
setrange key offset value |
用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始 |
setrange itcast 0 hello redis |
strlen key | 返回key所储存的字符串值长度 | strlen itcast |
mset key value [key value] | 同时设置一个或多个 key-value 对。 |
mset hello world[hello1 world1] |
msetnx key value[]key value | 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。 |
msetnx hello world[hello1 world1] |
psetex key millisconds value | 这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。 |
pset itcast6 6000 itcast6value |
incr key | 将key中储存的数字值增一 | set itcast7 1 incr itcast7 |
incrby key increment | 将key 所存储的值加上给定的增量(increment) | increby itcast7 2 |
incrbyfloat key increment | 将 key 所储存的值加上给定的浮点增量值(increment) |
incrbyfloat itcast7 0.5 |
decr key | 将 key 中储存的数字值减一 |
set itcast8 2 decr itcast8 |
decrby key increment | 将key 所存储的值减去给定的增量(increment) | decreby itcast7 2 |
append key value | 如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾。 |
append itcast8 hello |
Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。
hset key field value | 将哈希表 key 中的字段 field 的值设为 value 。 |
hset key1 field1 value1 |
hsetnx key field value | 只有在字段 field 不存在时,设置哈希表字段的值。 |
hsetnx key1 field1 value1 |
hmset key field1 value1 [field2 value2] |
同时将多个 field-value (域-值)对设置到哈希表 key 中。 |
hmset key1 field1 value1 field2 value2 |
hexists key field | 查看哈希表key中,指定字段是否存在 | hexists key1 field1 |
hget key field | 获取存储在哈希表中指定字段的值。 |
hget key1 field1 |
hgetall key | 获取在哈希表中指定key的所有字段和值 | hgetall key1 |
hkeys key | 获取所有哈希表中的字段 | hkeys key1 |
hlen key | 获取哈希表中字段的数量 | hlen key1 |
hmet key field[field2] | 获取所有给定字段的值 | hmget key1 field1 field2 |
hincrby key field increment | 为哈希表 key 中的指定字段的整数值加上增量 increment 。 |
hset key2 field1 1 hincrby key2 field1 1 hget key2 field1 |
hincrbyfloat key field increment | 为哈希表 key 中的指定字段的浮点数值加上增量 increment |
hincrbyfloat key2 field1 0.5 |
hvals key | 获取哈希表中所有值 | hvals key1 |
hdel key field1[field2] | 删除一个或多个哈希表字段 | hdel key1 field1 hvals key1 |
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
lpush key value1[value2 ] | 将一个或多个值插入到列表头部 | lpush list1 value1 value2 |
lrange key start stop | 查看list当中所有的数据 |
lrange list 0 1 |
lpush key value | 将一个值插入到已存在的列表头部 |
lpush list1 value3 |
rpush key value1 [value2] | 在列表尾部添加一个值或多个值 | lpush list1 value3 |
rpushx key value | 为已存在的列表添加值 | rpushx list1 value6 |
linsert key before/after value | 在列表的元素前或者后插入元素 | linsert list1 before value3 beforevalue3 |
lindex key index | 通过索引获取列表的元素(索引从0开始) | lindex list1 2 |
lset key index value | 通过索引设置列表元素的值(覆盖) | lset list1 0 hello |
llen key | 获取列表长度 | llen list1 |
lpop key | 移除并获取列表的第一个元素 | lpop list1 |
rpop key | 移除并获取列表的第一个元素 | rpop list1 |
blpop key1[key2] timeout | 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
blpop list1 2000 |
rpoplpush key1 key2 | 移除列表的最后一个元素,并将该元素添加到另一个列表并返回 |
rpoplpush list1 list2 |
brpoplpush key1 key2 timeout | 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
brpoplpush list1 list2 2000 |
ltrim key start stop | 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除 |
ltrim list1 0 2 |
del key1 key2 |
删除指定key的列表 | del list1 |
redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
sadd keymember1 [member2] | 向集合中添加一个或多个成员 | sadd set1 setvalue1 setvalue2 |
smembers key | 返回集合中的所有元素 | smembers set1 |
scard key | 获取集合的成员数 | scard set1 |
sdiff key1 [key2] |
返回给定集合的差集 | sdiff set1 set2 |
sdiffstore destination key1[key2] | 返回给定所有集合的差集并存储在destination中 | sdiffstore set3 set1 set2 |
sinter key1 [key2] | 返回给定所有集合的交集 | sinter set1 set2 |
sinterstore destination key1[key2] | 返回给定所有集合的交集并存储在destination中 | sinterstore set4 set1 set2 |
sismember key member | 判断 member 元素是否是集合 key 的成员 |
sismember set1 setvalue1 |
smove source distination member | 将member元素从source集合移动到distination | smove set1 set2 setvalue1 |
spop key | 移除并返回集合中一个随机元素 | spop set2 |
srandmember key [count] | 返回集合中一个或多个随机数 | srandmember set2 2 |
srem key member1 [member2] | 移除集合中一个或多个成员 | srem set2 setvalue1 |
sunion key1[key2] | 返回所有给定集合的并集 | sunion set1 set2 |
sunionstore destination key1 [key2] | 所有给定结合的并集存储在destination集合中 | sunionstore set2 set1 set2 |
del key |
该命令用于在 key 存在时删除 key。 |
del itcast5 |
dump key | 序列化给定key,并返回序列化的值 | dump key1 |
exists key | 检查给定key是否存在 | exists key1 |
expire key second | 为给定key设定过期时间,以秒计 | expire itcast5 |
pexpire key milliseconds | 设置key的过期时间以毫秒计 | pexpire set2 300000 |
keys pattern | 查找所有符合给定模式( pattern)的 key |
keys * |
persist key | 移除 key 的过期时间,key 将持久保持。 |
persist set2 |
pttl key | 以毫秒为单位返回 key 的剩余的过期时间 |
pttl set2 |
ttl key | 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。 |
ttl set2 |
randomkey | 从当前数据库中随机返回一个 key 。 |
randomkey |
rename key newkey | 修改key的名字 | rename set5 set6 |
renamenx key newkey | 仅当 newkey 不存在时,将 key 改名为 newkey |
renamenx set8 set10 |
type key | 返回key所存储的值的类型 | type set10 |
由于redis是一个内存数据库,所有的数据都是保存在内存当中的,内存当中的数据极易丢失,所以redis的数据持久化就显得尤为重要,在redis当中 ,提供了两种数据持久化的方法,分别为RDB和AOF且redis默认开启的数据持久化方式为RDB方式,
Redis会定期保存数据快照至一个rdb文件中,并且启动时自动加载rdb文件,恢复之前保存的数据。可以在配置文件中配置redis进行快照保存的时机
修改redis的配置文件
vim redis.conf
save 900 1
save 300 10
save 60 10000
save 5 1
采用AOF持久化的时候,Redis会把每一个写请求都记录在一个日志文件里。在Redis重启时,会把AOF文件中记录 的所有写操作顺序执行一遍,确保数据恢复到最新。AOF默认是关闭的,如果要开启,要进行以下配置
appendonly yes
AOF提供了三种fsync配置,always/everysec/no,通过配置项[appendfsync]指定:
appendfsync no:不进行fsync,将flush文件的时机交给OS决定,速度最快appendfsync always:每写入一条日志就进行一次fsync操作,数据安全性最高,但速度最慢
appendfsync everysec:折中的做法,交由后台线程每秒fsync一次
配置redis的AOF持久化机制方式:
vim redis.conf
appendonly yes
# appendfsync always
appendfsync everysec
# appendfsync no
由于redis集群当中最少需要三个主节点,每个主节点,最少需要一个对应的从节点,所以搭建redis集群最少需要三主三从的配置,所以redis集群最少需要6台redis的实例,我们这里使用三台机器,每台服务器上面运行两个redis的实例。我们这里使用node01服务器,通过配置不同的端口,实现redis集群的环境搭建
node01执行以下命令重新解压redis压缩包到/export路径下
cd /export/softwares/
tar -zxf redis-5.0.8.tar.gz -C /export/
yum -y install gcc-c++ tcl
可以创建软连接方便使用,并配置环境变量、
# node01, 编译、安装、创建软连接 # 进入源码目录 cd /export/server/redis-5.0.8 # 编译 make # 安装至指定目录 make PREFIX=/export/server/redis-5.0.8-bin install # 创建安装目录软连接 cd /export/server ln -s redis-5.0.8-bin redis |
配置环境变量(如果以前安装过Redis,配置过环境变量,就不用配置)。
# 配置环境变量 vim /etc/profile # ======================== 添加如下内容 ======================== # REDIS HOME export REDIS_HOME=/export/server/redis export PATH=:$PATH:$REDIS_HOME/bin # 执行生效 source /etc/profile |
从Redis-5.0.8源码目录下拷贝配置文件:redis.conf至Redis 安装目录。
# ====================== node01 上操作 ====================== # 拷贝配置文件 cd /export/server/redis-5.0.8 cp redis.conf /export/server/redis |
每台机器上启动2个Redis服务,一个主节点服务:7001,一个从节点服务:7002,如下图所示:
在Redis安装目录下创建7001和7002目录,分别存储Redis服务配置文件、日志及数据文件。
# 创建目录:7001和7002 cd /export/server/redis mkdir -p 7001 7002 |
拷贝配置文件:redis.conf至7001目录,并重命名为redis_7001.conf。
cd /export/server/redis cp redis.conf 7001/redis_7001.conf |
编辑配置文件:redis_7001.conf,内容如下:
cd /export/server/redis/7001 vim redis_7001.conf ## =========================== 修改内容说明如下 =========================== ## 69行,配置redis服务器接受链接的网卡 bind 0.0.0.0 ## 88行,关闭保护模式 protected-mode no ## 92行,设置端口号 port 7001 ## 136行,redis后台运行 daemonize yes ## 158行,Redis服务进程PID存储文件名称 pidfile /var/run/redis_7001.pid ## 171行,设置redis服务日志存储路径 logfile "/export/server/redis-5.0.8-bin/7001/log/redis.log" ## 263行,设置redis持久化数据存储目录 dir /export/server/redis-5.0.8-bin/7001/data/ ## 699行,启动AOF方式持久化 appendonly yes ## 832行,启动Redis Cluster cluster-enabled yes ## 840行,Redis服务配置保存文件名称 cluster-config-file nodes-7001.conf ## 847行,超时时间 cluster-node-timeout 15000 |
创建日志目录和数据目录:
mkdir -p /export/server/redis/7001/log mkdir -p /export/server/redis/7001/data |
配置7002端口号启动Redis服务,操作命令如下:
## 拷贝配置文件 cd /export/server/redis cp 7001/redis_7001.conf 7002/redis_7002.conf ## 修改配置文件:redis_7002.conf cd /export/server/redis/7002 vim redis_7002.conf # 进入vim编辑之后,执行以下代码将7001全部替换成7002 :%s/7001/7002/g # 表示:%s/old/new/g g表示全部替换 # 创建目录 mkdir -p /export/server/redis/7002/log mkdir -p /export/server/redis/7002/data |
2.发送安装包
将node01上配置好的Redis安装包,发送至node02,node03,每台机器运行2个Redis服务,端口号分别为7001和7002,具体命令如下:
# 发送安装包 cd /export/server scp -r redis-5.0.8-bin [email protected]:$PWD scp -r redis-5.0.8-bin [email protected]:$PWD # 在node2和node3创建软连接 cd /export/server ln -s redis-5.0.8-bin redis # 配置环境变量 vim /etc/profile # ======================== 添加如下内容 ======================== # REDIS HOME export REDIS_HOME=/export/server/redis export PATH=:$PATH:$REDIS_HOME/bin # 执行生效 source /etc/profile |
在三台机器node01、node02和node03,分别启动6个Redis服务,命令如下:
# 启动7001端口Redis服务 /export/server/redis/bin/redis-server /export/server/redis/7001/redis_7001.conf # 启动7002端口Redis服务 /export/server/redis/bin/redis-server /export/server/redis/7002/redis_7002.conf |
启动完成后用ps -ef | grep redis
Redis5.x版本之后,通过redis-cli客户端命令来进行创建集群,注意:Redis对主机名解析不友好,使用IP地址。
# 任意选择一台机器执行如下命令,创建集群 redis-cli -a 123456 --cluster create 192.168.23.100:7001 192.168.23.100:7002 192.168.23.110:7001 192.168.23.120:7002 192.168.23.120:7001 192.168.23.120:7002 --cluster-replicas 1 |
成功如下:
在任意一台机器,使用redis-cli客户端命令连接Redis服务:
redis-cli -c -h node01 -p 7001 -a 123456
编写脚本,方便启动和关闭Redis集群:redis-cluster-start.sh和redis-cluster-stop.sh。
cd /export/server/redis-5.0.8-bin/bin/ touch redis-cluster-start.sh touch redis-cluster-stop.sh # 给以执行权限 chmod u+x redis-cluster-start.sh chmod u+x redis-cluster-stop.sh |
vim redis-cluster-start.sh #!/bin/bash REDIS_HOME=/export/server/redis # Start Server ## node1.itcast.cn ssh node1.itcast.cn "${REDIS_HOME}/bin/redis-server /export/server/redis/7001/redis_7001.conf" ssh node1.itcast.cn "${REDIS_HOME}/bin/redis-server /export/server/redis/7002/redis_7002.conf" ## node02 ssh node2.itcast.cn "${REDIS_HOME}/bin/redis-server /export/server/redis/7001/redis_7001.conf" ssh node2.itcast.cn "${REDIS_HOME}/bin/redis-server /export/server/redis/7002/redis_7002.conf" ## node03 ssh node3.itcast.cn "${REDIS_HOME}/bin/redis-server /export/server/redis/7001/redis_7001.conf" ssh node3.itcast.cn "${REDIS_HOME}/bin/redis-server /export/server/redis/7002/redis_7002.conf" |
vim redis-cluster-stop.sh #!/bin/bash REDIS_HOME=/export/server/redis # Stop Server ## node01 ${REDIS_HOME}/bin/redis-cli -h node1.itcast.cn -p 7001 -a 123456 SHUTDOWN ${REDIS_HOME}/bin/redis-cli -h node1.itcast.cn -p 7002 -a 123456 SHUTDOWN ## node02 ${REDIS_HOME}/bin/redis-cli -h node2.itcast.cn -p 7001 -a 123456 SHUTDOWN ${REDIS_HOME}/bin/redis-cli -h node2.itcast.cn -p 7002 -a 123456 SHUTDOWN ## node03 ${REDIS_HOME}/bin/redis-cli -h node3.itcast.cn -p 7001 -a 123456 SHUTDOWN ${REDIS_HOME}/bin/redis-cli -h node3.itcast.cn -p 7002 -a 123456 SHUTDOWN |
在实际项目中可能由于Redis Cluster中节点宕机或者增加新节点,需要操作命令管理,主要操作如下。
连接Redis集群,需要使用JedisCluster来获取Redis连接。
实现步骤:
从Redis 3.0发布提供Redis Cluster以后,经历Redis 4.x、Redis5.x和Redis 6.x一系列版本,Redis Cluster更加成熟、稳定,推荐企业使用此种架构,通常公司也是使用此种架构。如果使用Redis Cluster集群,面试中碰到的问题有一些坑,还望注意。
在应用程序和MySQL数据库中建立一个中间层:Redis缓存,通过Redis缓存可以有效减少查询数据库的时间消耗,但是引入redis又有可能出现缓存穿透、缓存击穿、缓存雪崩等问题。
缓存穿透
缓存穿透:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。
一言以蔽之:查询Key,缓存和数据源都没有,频繁查询数据源
比如用一个不存在的用户id获取用户信息,无论论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
解决缓存穿透的方案主要有两种:
缓存击穿:key对应的数据库存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
一言以蔽之:查询Key,缓存过期,大量并发,频繁查询数据源
业界比较常用的做法:使用互斥锁。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db(查询数据库),而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,就是只让一个线程构建缓存,其他线程等待构建缓存的线程执行完,重新从缓存获取数据。
String get(String key) { // 如果key不存在,则设置为1 // 从db中加载数据,但注意:只有一个线程能进入到这里,其他线程访问的时候已有课key_mutex // 从数据库中加载成功,则设置对应的数据 |
缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
一言以蔽之:缓存不可用(服务器重启或缓存失效),频繁查询数据源
与缓存击穿的区别在于这里针对很多key缓存,前者则是某一个key。缓存正常从Redis中获取,示意图如下:
缓存失效瞬间示意图如下:
缓存失效时的雪崩效应对底层系统的冲击非常可怕!大多数系统设计者考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时将缓存失效时间分散开,比如可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。