Redis

Redis简介

  • REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。

  • 它是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

  • Redis通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。

  • Redis 官网:https://redis.io/

  • Redis中文网站:http://www.redis.cn

  • Redis 在线测试工具:http://try.redis.io/

Redis 特点

  • Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。

  • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。

  • Redis支持数据的备份,即master-slave模式的数据备份。

Redis的应用场景

  • 缓存

  • 网站访问统计

  • 任务队列

  • 数据过期处理

  • 应用排行榜

  • 分布式集群架构中的session分离

安装

  • 准备linux环境;

  • 官网https://redis.io/ 下载redis版本;

  • 上传至linux;

  • 解压安装,按官网download页面给出的步骤安装;

下载:
[root@localhost ~]# cd /usr/local/chen/
[root@localhost src]# wget http://download.redis.io/releases/redis-版本.tar.gz

解压:
[root@localhost src]# tar -zxf redis-版本.tar.gz 

编译:
[root@localhost redis-版本]# make
# 如果编译失败请先下载编译依赖,编译成功不需要执行以下命令
yum install wget make gcc tcl

安装:make PREFIX=/usr/local/chen/redis install

# 进入redis安装目录
[root@localhost redis-4.0.6]# cd /usr/local/redis/bin/
[root@localhost bin]# ll

运行:
[root@localhost bin]# ./redis-server 

使用内置的客户端命令redis-cli进行使用
root@localhost bin]# ./redis-cli
127.0.0.1:6379> 

修改redis配置文件

# 复制redis.conf文件到/etc目录
[root@localhost redis-4.0.6]# cp /usr/local/src/redis-4.0.6/redis.conf /etc/redis.conf

# 编辑etc下的redis.conf文件
[root@localhost ~]# vim /etc/redis.conf 

# 设置redis为后台启动进程,将daemonize no 改为 daemonize yes
daemonize yes 

# 修改绑定的主机地址,将#bind 127.0.0.1改成自己的ip地址,去掉"#"号
bind 0.0.0.0

关闭redis服务,重新运行
[root@localhost bin]# ./redis-cli shutdown
# 重新启动redis加载指定的配置文件
[root@localhost bin]# ./redis-server /etc/redis.conf 

将redis-server和redis-cli命令加入环境变量
vim /etc/profile
# 在最后一行加入
export PATH=/usr/local/redis/bin:$PATH 

使其立即生效
source /etc/proflie

Jedis-java访问

maven依赖下载:

<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>2.9.0</version>
</dependency>

创建项目并测试

public static void main(String[] args) {
    Jedis jedis = null;
    try {
        jedis = new Jedis("localhost", 6379);
        // 连接测试
        System.out.println(jedis.ping());
        //设置 redis 字符串数据
        jedis.set("name", "张三");
        // 获取存储的数据并输出
        System.out.println("redis 存储的字符串为: "+ jedis.get("name")); //结果为  redis 存储的字符串为:张三
    } finally {
        if (jedis != null) {
            jedis.close();//关闭连接
        }
    }
}

注:若出现连接失败异常请关闭linux防火墙   

#关闭防火墙
systemctl stop firewalld
#开启防火墙
systemctl start firewalld
#查看防火墙
systemctl status firewalld

redis的基本命令

redis所有命令可参考http://redisdoc.com网站

set key value 赋值

get key 取值

select index 切换库(下标从0开始)

dbsize 查看当前数据库key的数据

flushdb  清空当前库

flushall 清空所有库

keys * 查看所有key (keys后面参数可以用表达式代替)
 	?    匹配一个字符
    *    匹配任意个(包括0个)字符
    []   匹配括号间的任一个字符,可以使用 "-" 符号表示一个范围,如 a[b-d] 可以匹配 
     "ab","ac","ad"
    \x   匹配字符x,用于转义符号,如果要匹配 "?" 就需要使用 \?

exists key  判断一个键值是否存在(如果存在,返回整数类型 1 ,否则返回 0)

del key [key.....] 可以删除一个或多个键,返回值是删除的键的个数

type key 返回值可能是 string(字符串类型) hash(散列类型) list(列表类型) set(集合类型) zset(有序集合类型)

move key db 当前库就没有了,被移除了

expire key time(秒) 为给定的key设置过期时间

ttl key 查看还有多少秒过期,-1表示永不过期,-2表示已过期

Redis的五大数据类型:

  1. string(字符串)

  2. hash(哈希,类似java里的Map)

  3. list(列表)

  4. set(集合)

  5. zset(sorted set:有序集合)

一、String

String类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。string类型是Redis最基本的数据类型,一个redis中字符串value最多可以是512M。Strings 数据结构是简单的key-value类型,value其实不仅是String,也可以是数字.

应用场景:

String是最常用的一种数据类型,普通的key/ value 存储都可以归为此类.即可以完全实现目前 Memcached 的功能,并且效率更高。还可以享受Redis的定时持久化,操作日志及 Replication等功能。

常用命令:

 

set key value # 赋值
get key # 取值
del key # 删除(可以多个)
append key # 追加
exists key # 判断某个key是否存在,1表示存在,0表示不存在
strlen key # 返回长度
move key db # 移动索引库,例如:move key1 5   将当前库中的key1移5号库
expire key 秒钟 # 为指定的key设置过期时间
ttl key # 查看还有多少秒过期,-1表示永不过期,-2表示已过期
type key # 查看你的key是什么类型

数字值命令:

incr key # 将key中储存的数字值增一
decr key # 将key中储存的数字值减一
incrby key # 将key所储存的值加上给定的增量值(increment) 
decrby key # 将key所储存的值减去给定的减量值(decrement) 
其它命令:

getrange key startindex endindex # 获取指定区间值(0到-1获取全部)
setrange key startindex endindex # 设置指定区间值
setex key time(秒) # value 将值value关联到key,并将key的过期时间设为seconds(以秒为单位)
setnx key value # 只有在key不存在时设置 key 的值,如果已经存在则不设置值
mset key value [key2 value2 ...] # 同时设置一个或多个键值对
mget key [key2 key3 ...] # 获取所有(一个或多个)给定key的值
msetnx key value [key2 value2 ...]  # 同时设置一个或多个键值对(key不存在时)在设置的过程中只要有一个失败,其他的都不会set成功
getset key value # 获取旧值,设置新值

Java 实例

import redis.clients.jedis.Jedis;
 
public class RedisStringJava {
    public static void main(String[] args) {
        //连接本地的 Redis 服务
        Jedis jedis = new Jedis("localhost", 6379);
        System.out.println("连接成功");
        //设置 redis 字符串数据
        jedis.set("znsdkey", "www.znsd.com");
        // 获取存储的数据并输出
        System.out.println("redis 存储的字符串为: "+ jedis.get("znsdkey"));
    }
}

编译以上程序。

连接成功
redis 存储的字符串为: www.znsd.com

二、List

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)。它的底层实际是个链表。

应用场景:

Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现。Lists 就是链表,相信略有数据结构知识的人都应该能理解其结构。使用Lists结构,我们可以轻松地实现最新消息排行等功能。Lists的另一个应用就是消息队列,

可以利用Lists的PUSH操作,将任务存在Lists中,然后工作线程再用POP操作将任务取出进行执行。Redis还提供了操作Lists中某一段的api,你可以直接查询,删除Lists中某一段的元素。

基本命令:

lpush key value [value2 ...] # 将一个或多个值插入到列表头部(允许值重复)
rpush key value [value2 ...] # 将一个或多个值插入到列表尾部(允许值重复)
lrange key startindex endindex # 获取列表指定范围内的元素(0到-1获取全部)
lindex key index # 通过索引获取列表中的元素
llen key # 获取指定key值的个数
lpop key # 移出并获取列表的第一个元素
rpop key # 移除并获取列表最后一个元素
其他命令:

lrem key count value # 删除count个value
ltrim key start end # 截取指定范围的值后再赋值给key(让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除)
lset key index value # 通过索引设置列表元素的值
linsert key BEFORE|AFTER pivot value # 在列表指定的元素前或者后插入元素

Java 实例

import java.util.List;
import redis.clients.jedis.Jedis;
 
public class RedisListJava {
    public static void main(String[] args) {
        //连接本地的 Redis 服务
        Jedis jedis = new Jedis("localhost");
        System.out.println("连接成功");
        //存储数据到列表中
        jedis.lpush("site-list", "znsd");
        jedis.lpush("site-list", "Google");
        jedis.lpush("site-list", "Taobao");
        // 获取存储的数据并输出
        List<String> list = jedis.lrange("site-list", 0 ,2);
        for(int i=0; i<list.size(); i++) {
            System.out.println("列表项为: "+list.get(i));
        }
    }
}

编译以上程序。

连接成功
列表项为: Taobao
列表项为: Google
列表项为: znsd

性能总结

  • 它是一个字符串链表,left、right都可以插入和添加

  • 如果键不存在,创建新的链表,如果键已经存在,新增内容,如果值全部移除,对应的键也消失了

  • 链表的操作无论是对头和尾效率都极高,但假如是对中间元素进行操作,效率就很惨淡了

四、Set

Set是string类型的无序集合。它是通过HashTable实现实现的。

应用场景:

Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。Sets 集合的概念就是一堆不重复值的组合。利用Redis提供的Sets数据结构,可以存储一些集合性的数据,比如在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。

基本命令:

sadd key value [value2 ...] # 向集合添加一个或多个成员
smembers key # 返回集合中的所有成员
sismember key member # 判断 member 元素是否是集合 key 的成员
scard key # 获取集合里面的元素个数
srem key value # 删除集合中指定元素
spop key [count] # 移除集合中一个随机元素并返这个元素(可选择删除数量)
srandmember key [coutn] # 随机返回集合中一个或多个值
smove key1 key2 # 将key1里的某个值赋给key2

其他命令:

sdiff key1 [key2] # 返回给定所有集合的差集
sinter key1 [key2] # 返回给定所有集合的交集
sunion key1 [key2] 返回所有给定集合的并集

三、Hash

Redis hash 是一个键值对集合。hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。类似Java里面的Map<String,Object>。

应用场景:

在Memcached中,我们经常将一些结构化的信息打包成HashMap,在客户端序列化后存储为一个字符串的值,比如用户的昵称、年龄、性别、积分等,这时候在需要修改其中某一项时,通常需要将所有值取出反序列化后,修改某一项的值,再序列化存储回去。这样不仅增大了开销,也不适用于一些可能并发操作的场合(比如两个并发的操作都需要修改积分)。而Redis的Hash结构可以使你像在数据库中Update一个属性一样只修改某一项属性值。

基本命令:

hset key field value # 以hash方式存储
hget key field # 以hash方式获取
hmset key field value # [field value ...] 以hash方式存储一个或者多个
hmget key field # [field ...] 以hash方式获取一个或者多个
hgetall key # 获取在哈希表中指定key的所有字段和值
hdel key field [field ...] # 删除一个或多个哈希表字段
hkeys key # 获取所有哈希表中的字段
hvals key # 获取哈希表中所有值
其他命令:

hlen key # 获取哈希表中字段的数量
hexists key # 查看哈希表key中,指定的字段是否存在
hincrby key field value # 为哈希表key中的指定字段的整数值加上增量value(必须为数字)
hincrbyfloat key field value # 为哈希表key中的指定字段的浮点数值加上增量value 

Java 实例

import java.util.Iterator;
import java.util.Set;
import redis.clients.jedis.Jedis;
 
public class RedisKeyJava {
    public static void main(String[] args) {
        //连接本地的 Redis 服务
        Jedis jedis = new Jedis("localhost");
        System.out.println("连接成功");
 
        // 获取数据并输出
        Set<String> keys = jedis.keys("*"); 
        Iterator<String> it=keys.iterator() ;   
        while(it.hasNext()){   
            String key = it.next();   
            System.out.println(key);   
        }
    }
}

编译以上程序。

连接成功
znsdkey
site-list

五、Zset

zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。

应用场景:

Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。还可以用Sorted Sets来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。

基本命令:

zadd key score1 member1 [score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数
zrange key startindex endindex [WITHSCORES] 通过索引区间返回有序集合成指定区间内的成员(WITHSCORES带出分数)
zscore key member 返回有序集中,成员的分数值
zrangebyscore key min max [WITHSCORES] [LIMIT] 通过分数返回有序集合指定区间内的成员
zrem key member [member ...] 删除一个或多个元素
zcard key 获取有序集合的成员数
其他命令:

zcount key min max 计算在有序集合中指定区间分数的成员数
zrank key member 返回有序集合中指定成员的索引
zrevrank key member 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序
zrevrange key startindex endindex [WITHSCORES] 返回有序集中指定区间内的成员,通过索引,分数从高到底
zrevrangebyscore key max min [WITHSCORES] 返回有序集中指定分数区间内的成员,分数从高到低排序

Redis常用配置

INCLUDES(包含)

include /path/to/other.conf 这在你有标准配置模板但是每个redis服务器又需要个性设置的时候很有用

GENERAL(一般常用)

daemonize yes  Redis 默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程(yes:后台运行;no:不是后台运行)
protected-mode yes 保护模式,默认开启。开启该参数后,redis只会本地进行访问,拒绝外部访问。
pidfile /var/run/redis/redis-server.pid redis的进程文件
port 6379 redis监听的端口号
tcp-backlog 511 TCP连接队列的长度
bind 127.0.0.1 指定redis只接收来自于该IP地址的请求,如果不进行设置,那么将处理所有请求
timeout 0 当客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
loglevel notice 
指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose;
debug(很多信息,方便开发、测试)
verbose(许多有用的信息,但是没有debug级别信息多)
notice(适当的日志级别,适合生产环境)
warn(只有非常重要的信息)
logfile /var/log/redis/redis-server.log 日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null。空字符串的话,日志会打印到标准输出设备。后台运行的redis标准输出是/dev/null
databases 16 设置数据库的数量,默认数据库为0,可以使用SELECT <dbid>命令在连接上指定数据库id

SNAPSHOTTING(快照)

save <seconds> <changes> <指定时间间隔> <执行指定次数更新操作>,满足条件就将内存中的数据同步到硬盘中。
save "" 
save 900 1
save 300 10
save 60 10000
注释掉“save”这一行配置项就可以让保存数据库功能失效,设置sedis进行数据库镜像的频率。
900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化) 
300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化) 
60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
stop-writes-on-bgsave-error yes 当RDB持久化出现错误后,是否依然进行继续进行工作,yes:不能进行工作,no:可以继续进行工作,可以通过info中的rdb_last_bgsave_status了解RDB持久化是否有错误
rdbcompression yes 使用压缩rdb文件,rdb文件压缩使用LZF压缩算法,yes:压缩,但是需要一些cpu的消耗。no:不压缩,需要更多的磁盘空间
rdbchecksum yes 是否校验rdb文件。从rdb格式的第五个版本开始,在rdb文件的末尾会带上CRC64的校验和。这跟有利于文件的容错性,但是在保存rdb文件的时候,会有大概10%的性能损耗,所以如果你追求高性能,可以关闭该配置。
dbfilename dump.rdb rdb文件的名称
dir /var/lib/redis 数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录

REPLICATION(复制)

slaveof <masterip> <masterport> 设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
masterauth <master-password> 当master服务设置了密码保护时,slav服务连接master的密码
slave-read-only yes 作为从服务器,默认情况下是只读的(yes),可以修改成NO,用于写(不建议)。
slave-priority 100 当master不可用,Sentinel会根据slave的优先级选举一个master。最低的优先级的slave,当选master。而配置成0,永远不会被选举

SECURITY(安全)

requirepass foobared requirepass配置可以让用户使用AUTH命令来认证密码,才能使用其他命令。这让redis可以使用在不受信任的网络中。为了保持向后的兼容性,可以注释该命令,因为大部分用户也不需要认证。使用requirepass的时候需要注意,因为redis太快了,每秒可以认证15w次密码,简单的密码很容易被攻破,所以最好使用一个更复杂的密码。

LIMITS(限制)

  • maxclients 10000 设置能连上redis的最大客户端连接数量。默认是10000个客户端连接。由于redis不区分连接是客户端连接还是内部打开文件或者和slave连接等,所以maxclients最小建议设置到32。如果超过了maxclients,redis会给新的连接发送’max number of clients reached’,并关闭连接。

  • maxmemory <bytes> redis配置的最大内存容量。当内存满了,需要配合maxmemory-policy策略进行处理。注意slave的输出缓冲区是不计算在maxmemory内的。所以为了防止主机内存使用完,建议设置的maxmemory需要更小一些。

  • maxmemory-policy noeviction 内存容量超过maxmemory后的处理策略

  1. volatile-lru(Least Recently Used 即最近最少使用):利用LRU算法移除key,只对设置了过期时间的key。

  2. allkeys-lru:利用LRU算法移除所有key,与是否设置过期时间没关系。

  3. volatile-random:随机移除设置了过期时间的key。

  4. allkeys-random:随机移除任何key。与是否设置过期时间没关系。

  5. volatile-ttl:移除即将过期的key。

  6. noeviction:不移除任何key,只是返回一个写错误。

APPEND ONLY MODE(追加)

appendonly no 默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了。但是redis如果中途宕机,会导致可能有几分钟的数据丢失,根据save来策略进行持久化,Append Only File是另一种持久化方式,可以提供更好的持久化特性。Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件,每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。
appendfilename "appendonly.aof" aof文件名
appendfsync everysec aof持久化策略的配置。
no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。
always表示每次写入都执行fsync,以保证数据同步到磁盘。
everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。
auto-aof-rewrite-percentage 100 aof自动重写配置。当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文件增长到一定大小的时候Redis能够调用bgrewriteaof对日志文件进行重写。当前AOF文件大小是上次日志重写得到AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程。
auto-aof-rewrite-min-size 64mb 设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写

redis.conf常见配置

redis.conf 配置项说明如下:

# Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程
daemonize no
​
# 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
pidfile /var/run/redis.pid
​
# 指定Redis监听端口,默认端口为6379,作者在自己的一篇博文中解释了为什么选用6379作为默认端口,因为6379在手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字
port 6379
​
# 绑定的主机地址
bind 127.0.0.1
​
# 当 客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
timeout 300
​
# 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
loglevel verbose
​
# 日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null
logfile stdout
​
# 设置数据库的数量,默认数据库为0,可以使用SELECT <dbid>命令在连接上指定数据库id
databases 16
​
# 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
save <seconds> <changes>
​
# Redis默认配置文件中提供了三个条件:
    #save 900 1 # 900秒(15分钟)内有1个更改
    #save 300 10 # 300秒(5分钟)内有10个更改
    #save 60 10000 # 60秒内有10000个更改
# 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
rdbcompression yes
​
# 指定本地数据库文件名,默认值为dump.rdb
dbfilename dump.rdb
​
# 指定本地数据库存放目录
dir ./
​
# 设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
slaveof <masterip> <masterport>
​
# 当master服务设置了密码保护时,slav服务连接master的密码
masterauth <master-password>
​
# 设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH <password>命令提供密码,默认关闭
requirepass foobared
​
# 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
maxclients 128
​
# 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
maxmemory <bytes>
​
# 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no
appendonly no
​
# 指定更新日志文件名,默认为appendonly.aof
appendfilename appendonly.aof
​
# 指定更新日志条件,共有3个可选值: 
    #no:表示等操作系统进行数据缓存同步到磁盘(快) 
    #always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全) 
    #everysec:表示每秒同步一次(折衷,默认值)
    #appendfsync everysec
    
# 指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析Redis的VM机制)
vm-enabled no
​
# 虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享
vm-swap-file /tmp/redis.swap
​
# 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(Redis的索引数据 就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。默认值为0
vm-max-memory 0
​
# Redis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page大小最好设置为32或者64bytes;如果存储很大大对象,则可以使用更大的page,如果不 确定,就使用默认值
vm-page-size 32
​
# 设置swap文件中的page数量,由于页表(一种表示页面空闲或使用的bitmap)是在放在内存中的,,在磁盘上每8个pages将消耗1byte的内存。
vm-pages 134217728
​
# 设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为4
vm-max-threads 4
​
# 设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启
glueoutputbuf yes
​
# 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法
    #hash-max-zipmap-entries 64
    #hash-max-zipmap-value 512
# 指定是否激活重置哈希,默认为开启(后面在介绍Redis的哈希算法时具体介绍)
activerehashing yes
​
# 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件
include /path/to/local.conf

配置信息可以通过config get 获取

config get port # 获取redis端口号
config get dir # 获取启动路径
config get requirepass # 获取密码
config set requirepass "123456" # 设置redis密码

 

持久化(RDB/AOF)

RDB

RDB(Redis Database) 是 Redis 默认的持久化方案。它可以在指定的时间间隔内将内存中的数据集快照写入磁盘, 也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里,在RDB方式下,你有两种选择:

  • 一种是手动执行持久化数据命令来让redis进行一次数据快照,

  • 另一种则是根据你所配置的配置文件的策略,达到策略的某些条件时来自动持久化数据。

而手动执行持久化命令,你依然有两种选择:

  • save命令

    save命令执行时只管保存,其他不管,全部阻塞,save操作在Redis主线程中工作,因此会阻塞其他请求操作,应该避免使用。(默认下,持久化到dump.rdb文件,并且在redis重启后,自动读取其中文件,据悉,通常情况下一千万的字符串类型键,1GB的快照文件,同步到内存中的 时间是20-30秒

  • bgsave命令。

    bgsave后台备份,在备份的同时可以处理输入的数据,bgSave则是调用Fork,产生子进程,父进程继续处理请求。子进程将数据写入临时文件,并在写完后,替换原有的.rdb文件。Fork发生时,父子进程内存共享,所以为了不影响子进程做数据快照,在这期间修改的数据,将会被复制一份,而不进共享内存。所以说,RDB所持久化的数据,是Fork发生时的数据。在这样的条件下进行持久化数据,如果因为某些情况宕机,则会丢失一段时间的数据。如果你的实际情况对数据丢失没那么敏感,丢失的也可以从传统数据库中获取或者说丢失部分也无所谓,那么你可以选择RDB持久化方式。

### 生产环境推荐配置(系统默认的)
# 15分钟修改1次
save 900 1
# 5分钟修改10次
save 300 10
# 一分钟修改一万次
save 60 10000
​
### 如果想禁用
# save ""

这是配置文件默认的策略,他们之间的关系是或,每隔900秒,在这期间变化了至少一个键值,做快照。或者每三百秒,变化了十个键值做快照。或者每六十秒,变化了至少一万个键值,做快照。

RDB配置

触发RDB快照

  • 在指定的时间间隔内,执行指定次数的写操作。

  • 执行save(阻塞, 只管保存快照,其他的等待) 或者是bgsave (异步)命令。

  • 执行flushall 命令,清空数据库所有数据,意义不大。

  • 执行shutdown 命令,保证服务器正常关闭且不丢失任何数据,意义也不大。

数据恢复

将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可。

优势

  • 适合大规模的数据恢复

  • 对数据完整性要求不高

劣势

  • 在一定时间做一次备份,如果redis意外宕机的话就会丢失最后一次快照后的所有数据

  • Fork的时候,内存中的数据被克隆了一份,在fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求

AOF

  • AOF(Append Only File) ,Redis 默认不开启。它的出现是为了弥补RDB的不足(数据的不一致性),所以它采用日志的形式来记录每个写操作,并追加到文件中。Redis 重启的会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

  • 配置文件中的appendonly修改为yes。开启AOF持久化后,你所执行的每一条指令,都会被记录到appendonly.aof文件中。但事实上,并不会立即将命令写入到硬盘文件中,而是写入到硬盘缓存,在接下来的策略中,配置多久来从硬盘缓存写入到硬盘文件。所以在一定程度一定条件下,还是会有数据丢失,不过你可以大大减少数据损失。

  • 这里是配置AOF持久化的策略。redis默认使用everysec,就是说每秒持久化一次,而always则是每次操作都会立即写入aof文件中。而no则是不主动进行同步操作,是默认30s一次。当然always一定是效率最低的,个人认为everysec就够用了,数据安全性能又高

AOF配置

  • 开启AOF配置

# AOF and RDB persistence can be enabled at the same time without problems.
# AOF和RDB的持久化是可以同时开启的
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# 如果启动时启用了AOF, Redis将先加载AOF
# with the better durability guarantees.
​
# 开启aop配置
appendonly yes
​
# aop生成文件名称
appendfilename "appendonly.aof"

数据恢复

将备份文件 (appendonly.aof) 移动到 redis 安装目录并启动服务即可。

触发AOF快照

根据配置文件触发,可以是每次执行触发,可以是每秒触发,可以不同步。

  • always:每修改同步

  • everysec:每秒同步

  • no:不同步

# 每修改同步:同步持久化 每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好
appendfsync always
​
# 每秒同步:默认设置,异步操作,每秒记录   如果一秒内宕机,有数据丢失(推荐)
appendfsync everysec
​
# 不同步:从不同步
appendfsync no

AOF rewrite(重写)

  • AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof。

  • AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename), 遍历新进程的内存中数据,每条记录有一条的Set语句。重写aof文件的操作,并没有读取旧的aof文件, 而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。

  • Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发。

AOP文件修复

redis-check-aof --fix appendonly.aof

RDB & AOF比较

优势

RDB:适合大规模的数据恢复。如果业务对数据完整性和一致性要求不高,RDB是很好的选择。

AOF:数据的完整性和一致性更高。

劣势

RDB:数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。 备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍哦),最后再将临时文件替换之前的备份文件。所以Redis 的持久化和数据的恢复要选择在夜深人静的时候执行是比较合理的。

AOF:因为AOF记录的内容多,文件会越来越大,数据恢复也会越来越慢。

总结

  • Redis 默认开启RDB持久化方式,在指定的时间间隔内,执行指定次数的写操作,则将内存中的数据写入到磁盘中。

  • RDB 持久化适合大规模的数据恢复但它的数据一致性和完整性较差。

  • Redis 需要手动开启AOF持久化方式,默认是每秒将写操作日志追加到AOF文件中。

  • AOF 的数据完整性比RDB高,但记录内容多了,会影响数据恢复的效率。

  • Redis 针对 AOF文件大的问题,提供重写的瘦身机制。

  • 若只打算用Redis 做缓存,可以关闭持久化。

  • 若打算使用Redis 的持久化。建议RDB和AOF都开启。其实RDB更适合做数据的备份,留一后手。AOF出问题了,还有RDB

事物

redis的事务中,一次执行多条命令,本质是一组命令的集合,一个事务中所有的命令将被序列化,即按顺序执行而不会被其他命令插入在redis中,事务的作用就是在一个队列中一次性、顺序性、排他性的执行一系列的命令。事务可以一次执行多个命令, 并且带有以下两个重要的保证:

  1. 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

  2. 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

使用事物

用multi命令开启事物,用exec执行事物。

例子:

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) OK
3) "v1"
4) OK

执行结果:

① 正常执行完成;

② 有命令入队不成功,事物执行失败,全部命令都不执行;

③ 命令入队成功,事物照常执行,有命令执行失败,其余命令正常执行;

放弃事物

在使用multi开起事物之后,可以使用discard放弃当前事物。

例子:

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 vv
QUEUED
127.0.0.1:6379> set k2 vv2
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> get k1
"v1"

事物-监控

redis可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行。监控一直持续到EXEC命令(事务中的命令是在EXEC之后才执行的)。

监控

监控一个鞋子,当我加入购物车(事务)准备提交购买时,别人先一步买了,我购买失败

watch key [key ...] 

例子:

127.0.0.1:6379> watch k1
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> EXEC
(ni1)

放弃监控

unwatch 取消 WATCH 命令对所有 key 的监视。

消息发布订阅

消息发布订阅是什么

  • Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

  • Redis 客户端可以订阅任意数量的频道。

  • 下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:

     

  • 当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

     

 

发布订阅命令

序号命令及描述
1PSUBSCRIBE pattern [pattern ...] 订阅一个或多个符合给定模式的频道。
2PUBSUB subcommand [argument [argument ...]] 查看订阅与发布系统状态。
3PUBLISH channel message将信息发送到指定的频道。
4PUNSUBSCRIBE [pattern [pattern ...]] 退订所有给定模式的频道。
5SUBSCRIBE channel [channel ...]订阅给定的一个或多个频道的信息。
6UNSUBSCRIBE [channel [channel ...]]指退订给定的频道。

实例

  1. 可以一次性订阅多个

    SUBSCRIBE c1 c2 c3
  2. 消息发布

    PUBLISH c1 helloredis
  3. 订阅多个可以使用通配符*

    PSUBSCRIBE new*
  4. 接收消息

    PUBLISH new1 redis2018
    PUBLISH new2 redis2018

主从复制

在Redis中,用户可以通过执行SLAVEOF命令或者设置slaveof选项,让一个服务器去复制(replicate)另一个服务器,我们称呼被复制的服务器为主服务器(master),而对主服务器进行复制的服务器则被称为从服务器(slave)。通常用作读写分离,以及数据恢复。

配置主从

认主:

在从库里使用slaveof命令,从库认主后,从库只能做读操作;

当主机挂求后,从机原地待命;

当从机挂求重启后,从机变主机;

当使用redis.conf认主,不使用命令配置认主。

slaveof host port 认主
info [section] 查看redis信息(带replication参数可直接看主从信息)
SLAVEOF no one 将从库转成主库,并保已有数据

哨兵模式

Redis的主从架构,如果master发现故障了,还得手动将slave切换成master继续服务,手动的方式容易造成失误,导致数据丢失,那Redis有没有一种机制可以在master和slave进行监控,并在master发送故障的时候,能自动将slave切换成master呢?有的,那就是哨兵。

哨兵的作用:

  1. 监控redis进行状态,包括master和slave

  2. 当master down机,能自动将slave切换成master

配置哨兵:

新建配置文件,命名为sentinel.conf,输入下面内容

sentinel monitor mymaster 192.168.137.101 6379 1
#sentinel auth-pass myMaster master123 #配置密码

mymaster :master服务的名称,随便定义

启动哨兵:

redis-sentinel sentinel.conf 

JedisPool使用

Jedis实例不是线程安全的,所以不可以多个线程共用一个Jedis实例,但是创建太多的实现也不好因为这意味着会建立很多sokcet连接。JedisPool是一个线程安全的网络连接池。可以用JedisPool创建一些可靠Jedis实例,可以从池中获取Jedis实例,使用完后再把Jedis实例还回JedisPool。这种方式可以避免创建大量socket连接并且会实现高效的性能.

使用JedisPool

  1. JedisPool#getResource()方法从连接池中取得一个Jedis实例,

  2. 使用Jedis实例进行正常的数据操作

  3. Jedis实例使用完后要把它再放回连接池。

JedisPool工具类

public class JedisPoolUtil {
    
    private static JedisPool jedisPool = null;
​
    private JedisPoolUtil() {
​
    }
    
    public static JedisPool getJedisPoolInstance() {
        if (null == jedisPool) {
            
            synchronized (JedisPoolUtil.class) {
                
                if (null == jedisPool) {
                    JedisPoolConfig poolConfig = new JedisPoolConfig();
                    // 设置最大连接数
                    poolConfig.setMaxTotal(1000);
                    // 设置连接池最大空闲数
                    poolConfig.setMaxIdle(32);
                    // 最大等待时间
                    poolConfig.setMaxWaitMillis(100 * 1000);
                    // 获得一个redis连接实例是否检查可用性(ping())
                    poolConfig.setTestOnBorrow(true);
                    
                    jedisPool = new JedisPool(poolConfig , "192.168.0.25", 6380);
                }
            }
        }
        return jedisPool;
    }
    
    public static void release(Jedis jedis) {
        if (null != jedis) {
            jedis.close();
        }
    }
    
    public static void destory(JedisPool jedisPool) {
        if (null != jedisPool) {
            jedisPool.destroy();
        }
    }
}

JedisPool常用配置

JedisPool保证资源在一个可控范围内,并且提供了线程安全,但是一个合理的GenericObjectPoolConfig配置能为应用使用Redis保驾护航,下面将对它的一些重要参数进行说明和建议:

序号参数名含义默认值使用建议
1maxTotal资源池中最大连接数8 
2maxIdle资源池允许最大空闲的连接数8 
3minIdle资源池确保最少空闲的连接数0 
4blockWhenExhausted当资源池用尽后,调用者是否要等待。只有当为true时,下面的maxWaitMillis才会生效true建议使用默认值
5maxWaitMillis当资源池连接用尽后,调用者的最大等待时间(单位为毫秒)-1:表示永不超时不建议使用默认值
6testOnBorrow向资源池借用连接时是否做连接有效性检测(ping),无效连接会被移除false业务量很大时候建议设置为false(多一次ping的开销)。
7testOnReturn向资源池归还连接时是否做连接有效性检测(ping),无效连接会被移除false业务量很大时候建议设置为false(多一次ping的开销)。
8jmxEnabled是否开启jmx监控,可用于监控true建议开启,但应用本身也要开启

redis整合spring

  • 导入maven依赖

    <!-- jedis api -->
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.9.0</version>
    </dependency>
    ​
    <!-- spring redis -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-redis</artifactId>
        <version>1.7.5.RELEASE</version>
    </dependency>
    ​
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.9</version>
        <scope>test</scope>
    </dependency>
    ​
    <!-- spring core -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version> 4.2.5.RELEASE</version>
    </dependency>
    ​
    <!-- spring 测试 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version> 4.2.5.RELEASE</version>
        <scope>provided</scope>
    </dependency>
  • 配置文件

    # ip地址
    redis.host.ip=192.168.0.25
    # 端口号
    redis.port=6379
    # 如果有密码 
    redis.password=
    # 客户端超时时间单位是毫秒 默认是2000  
    redis.timeout=3000
    ​
    ​
    # 最大空闲数  
    redis.maxIdle=6
    # 连接池的最大数据库连接数。设为0表示无限制,如果是jedis 2.4以后用redis.maxTotal  
    #redis.maxActive=600  
    # 控制一个pool可分配多少个jedis实例,用来替换上面的redis.maxActive,如果是jedis 2.4以后用该属性  
    redis.maxTotal=20
    # 最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。  
    redis.maxWaitMillis=3000
    # 连接的最小空闲时间 默认1800000毫秒(30分钟) 
    redis.minEvictableIdleTimeMillis=300000
    # 每次释放连接的最大数目,默认3 
    redis.numTestsPerEvictionRun=4
    # 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1  
    redis.timeBetweenEvictionRunsMillis=30000
  • 创建spring整合redis配置文件spring-redis.xml

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:c="http://www.springframework.org/schema/c"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:lang="http://www.springframework.org/schema/lang"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
            http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-4.2.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
    ​
        <!--1,如果你有多个数据源需要通过<context:property-placeholder管理,且不愿意放在一个配置文件里,那么一定要加上ignore-unresolvable=“true" -->
        <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true" />
    ​
        <!--2,注意新版本2.3以后,JedisPoolConfig的property name,不是maxActive而是maxTotal,而且没有maxWait属性-->
        <!-- redis连接池配置 -->
        <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
            <!--最大空闲数 -->
            <property name="maxIdle" value="${redis.maxIdle}" />
            <!--连接池的最大数据库连接数 -->
            <property name="maxTotal" value="${redis.maxTotal}" />
            <!--最大建立连接等待时间 -->
            <property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
            <!--逐出连接的最小空闲时间 默认1800000毫秒(30分钟) -->
            <property name="minEvictableIdleTimeMillis"
                value="${redis.minEvictableIdleTimeMillis}" />
            <!--每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3 -->
            <property name="numTestsPerEvictionRun"
                value="${redis.numTestsPerEvictionRun}" />
            <!--逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1 -->
            <property name="timeBetweenEvictionRunsMillis"
                value="${redis.timeBetweenEvictionRunsMillis}" />
        </bean>
    ​
        <!--redis连接工厂 -->
        <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">
            <property name="poolConfig" ref="jedisPoolConfig"></property>
            <!--IP地址 -->
            <property name="hostName" value="${redis.host.ip}"></property>
            <!--端口号 -->
            <property name="port" value="${redis.port}"></property>
            <!--如果Redis设置有密码 -->
            <!-- <property name="password" value="${redis.password}" /> -->
            <!--客户端超时时间单位是毫秒 -->
            <property name="timeout" value="${redis.timeout}"></property>
        </bean>
    ​
        <!-- redis操作模板,这里采用尽量面向对象的模板 -->
        <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
            <property name="connectionFactory" ref="jedisConnectionFactory" />
            <!-- 指定redis中key-value的序列化方式(此处省略) -->
        </bean>
        
        <bean id="redisUtil" class="com.znsd.jedis.util.RedisUtil">
            <property name="redisTemplate" ref="redisTemplate"></property>
        </bean>
    </beans>
  • RedisUtil工具类

    public class RedisUtil {
    ​
        private RedisTemplate<String, Object> redisTemplate;
    ​
        /*
         * 如果使用注解注入RedisTemplate对象,则不需要该setter方法
         */
        public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    ​
        /**
         * String类型缓存获取
         * 
         * @param key 键
         * @return 值
         */
        public Object get(String key) {
            return key == null ? null : redisTemplate.opsForValue().get(key);
        }
    ​
        /**
         * String类型缓存保存
         * 
         * @param key   键
         * @param value 值
         * @return true:成功;false:失败
         */
        public boolean set(String key, Object value) {
            try {
                if (!StringUtils.isEmpty(key) && null != value) {
                    redisTemplate.opsForValue().set(key, value);
                    return true;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
    ​
        // ... ...
    ​
    }
  • 整合junit测试

    //使用junit4进行测试
    @RunWith(SpringJUnit4ClassRunner.class)
    //加载配置文件
    @ContextConfiguration(locations = {"classpath:spring/spring-redis.xml"})
    public class RedisUtilTest {
    ​
        @Autowired
        private RedisUtil redisUtil;
        
        @Test
        public void testSet() {
            User user = new User(1, "张三", "123");
            System.out.println(redisUtil.set("k1", "v111111"));
            System.out.println(redisUtil.set("user", user));
        }
        
        @Test
        public void testGet() {
            System.out.println("k1:" + redisUtil.get("k1"));
            System.out.println("user:" + redisUtil.get("user"));
        }
    }

Redis集群

  • 创建集群服务器存放目录

    mkdir /usr/local/redis-cluster
  • 将redis安装目录复制至新建目录下

    cp -r redis/bin redis-cluster/redis1
  • 将redis编译后的src目录下的redis-trib.rb复到制redis-cluster目录里面

    cp /usr/local/src/redis-5.0.3/src/redis-trib.rb /usr/local/redis-cluster/ 
  • 为复制的redis服务设置配置文件,每个服务都有一个配置文件

    daemonize yes 
    bind 127.0.0.1
    Port 7001
    logfile "./redis-7001.log"
    databases 1
    protected-mode no
    pidfile /var/run/redis_7001.pid
    cluster-enabled yes
  • 安装 ruby :redis集群运行的环境

    # yum 安装需要联网
    yum -y install ruby rubygems
    #查看安装版本,centos6默认安装的ruby是1.8.7版本,执行以下命令更新ruby版本至2.2以上
    ruby -v
    #更新ruby版本至2.2以上
    yum install centos-release-scl-rh 
    #yum 安装ruby2.3
    yum install rh-ruby23  -y
    #
    scl  enable  rh-ruby23 bash
    # 安装redis
    gem install redis
  • 至每个redis服务目录下 逐个启动Redis

    ./redis-server redis_配置文件.conf
  • 将所有redis服务集群

    redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 --cluster-replicas 1
    #使用 redis-trib.rb 命令搭建集群
    ./redis-trib.rb create --replicas 1 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006

常用命令

# 查看集群的信息
cluster info 
# 列出集群当前已知的所有节点
cluster nodes 
# 添加主节点
redis-trib.rb add-node 127.0.0.1:7008(新节点) 127.0.0.1:7001(任一老节点)
# 添加从节点
redis-trib.rb add-node --slave --master-id 主节点id 127.0.0.1:7008(新节点) 127.0.0.1:7001(任一老节点)
# 查看集群的信息
redis-trib.rb check 127.0.0.1:7001
​

代码中 使用

# 在application.properties中配置redis参数
spring.redis.host=192.168.108.130
spring.redis.port=6379
spring.redis.database=0
# 连接池
spring.redis.pool.max-idle=8
spring.redis.pool.min-idle=0
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=1
# 集群的所有节点: 127.0.0.1:7001,127.0.0.1:7002
spring.redis.cluster.nodes=127.0.0.1:7001,127.0.0.1:7002
# 哨兵节点
spring.redis.sentinel.nodes=127.0.0.1:26379
# 哨兵名字
spring.redis.sentinel.master=mymaster
@Configuration
public class MongoDBConfig {
    @Value("${spring.redis.pool.max-idle}")
    private int poolMaxIdle;
    @Value("${spring.redis.pool.min-idle}")
    private int poolMinIdle;
    @Value("${spring.redis.pool.max-active}")
    private int poolMinActive;
    @Value("${spring.redis.pool.max-wait}")
    private int poolMaxWait;
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    
    @Value("${spring.redis.sentinel.nodes}")
    private String sentinelNodes;
    @Value("${spring.redis.sentinel.master}")
    private String sentinelName;
    @Value("${spring.redis.cluster.nodes}")
    private String clusterNodes;
​
    
    
    @Bean
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisFactory){
        RedisTemplate template = new StringRedisTemplate(redisFactory);
        // 反序列化设置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        // 序列化参数设置
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        // 设置连接工厂
        template.setConnectionFactory(redisFactory);
        // 开启事物
        template.setEnableTransactionSupport(true);
        return template;
    }
    
    @Bean
    public  JedisPool JedisPoolFactory() {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxIdle(poolMaxIdle);
        poolConfig.setMaxTotal(1000);
        poolConfig.setMaxWaitMillis(poolMaxWait * 1000);
        JedisPool jp = new JedisPool(poolConfig, host, port);
        return jp;
    }
    
    @Bean
    public RedisSentinelConfiguration redisSentinelConfiguration(){
        RedisSentinelConfiguration configuration = new RedisSentinelConfiguration();
        // 根据sentinel的配置生成sentinelConfiguration
        String sentinels[] = sentinelNodes.split(",");
        for(int i=0;i<sentinels.length;i++) {
            configuration.addSentinel(new RedisNode(sentinels[i].split(":")[0], Integer.parseInt(sentinels[i].split(":")[1])));
        }
        configuration.setMaster(sentinelName);
        return configuration;
    }
​
    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        // 创建Redis连接时,加上sentinel哨兵设置
        JedisConnectionFactory factory = new JedisConnectionFactory(redisSentinelConfiguration());
        factory.setHostName(host);
        factory.setPort(port);
        factory.setDatabase(0);
        return factory;
    }
    // 集群配置JedisCluster,在Dao类中注入JedisCluster对象便可操作redis服务
    @Bean
    public JedisCluster jedisCluster() {
        String nodes[] = clusterNodes.split(",");
        Set<HostAndPort> nodeSet = new HashSet<HostAndPort>();
        for(int i=0;i<nodes.length;i++) {
            nodeSet.add(new HostAndPort(nodes[i].split(":")[0], Integer.parseInt(nodes[i].split(":")[1])));
        }
        return new JedisCluster(nodeSet);
    }
}

tomcat-redis-session-manager

 

分析:

分布式web server集群部署后需要实现session共享,针对 tomcat 服务器的实现方案多种多样,
比如 tomcat cluster session 广播、nginx IP hash策略、nginx sticky module等方案,
本文主要介绍了使用 redis 服务器进行 session 统一存储管理的共享方案。
​

必要环境:

  • java1.7

  • tomcat7

  • redis2.8

nginx 负载均衡配置

  1. 修改nginx conf配置文件加入

    upstream student_server {
            server 127.0.0.1:8080 weight=1 max_fails=2 fail_timeout=30s;
            #server 127.0.0.1:8220 weight=2 max_fails=2 fail_timeout=30s;
            #ip_hash;
    }
  2. 配置相应的server或者location

    server  {
            listen       80;
            server_name  www.student.com;        
            location / {
                # 允许跨域请求配置
                #add_header Access-Control-Allow-Origin http://static.ccfp.com:5001;
                #add_header Access-Control-Allow-Credentials true;
                #add_header Access-Control-Allow-Methods GET;
                add_header Access-Control-Allow-Origin  *;
                
                proxy_pass http://student_server;
                index index.html index.php index.jsp;
            }
    }

tomcat session共享配置步骤

  1. 添加redis session集群依赖的jar包到 TOMCAT_BASE/lib 目录下

  2. 修改 TOMCAT_BASE/conf 目录下的 context.xml 文件

    <!-- tomcat-redis-session共享配置 -->  
    <Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />  
            <Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"  
             host="192.168.1.15"   
             port="6379"   
             database="0"   
             maxInactiveInterval="60" />

    属性解释:

    • host redis服务器地址

    • port redis服务器的端口号

    • database 要使用的redis数据库索引

    • maxInactiveInterval session最大空闲超时时间,如果不填则使用tomcat的超时时长,一般tomcat默认为1800 即半个小时

    • sessionPersistPolicies session保存策略,除了默认的策略还可以选择的策略有:

      [SAVE_ON_CHANGE]:每次 session.setAttribute() 、 session.removeAttribute() 触发都会保存. 
          注意:此功能无法检测已经存在redis的特定属性的变化,
          权衡:这种策略会略微降低会话的性能,任何改变都会保存到redis中.
      ​
      [ALWAYS_SAVE_AFTER_REQUEST]: 每一个request请求后都强制保存,无论是否检测到变化.
          注意:对于更改一个已经存储在redis中的会话属性,该选项特别有用. 
          权衡:如果不是所有的request请求都要求改变会话属性的话不推荐使用,因为会增加并发竞争的情况。
      ​
    • sentinelMaster redis集群主节点名称(Redis集群是以分片(Sharding)加主从的方式搭建,满足可扩展性的要求)

    • sentinels redis集群列表配置(类似zookeeper,通过多个Sentinel来提高系统的可用性)

    • connectionPoolMaxTotal

    • connectionPoolMaxIdle jedis最大能够保持idel状态的连接数

    • connectionPoolMinIdle 与connectionPoolMaxIdle相反

    • maxWaitMillis jedis池没有对象返回时,最大等待时间

    • minEvictableIdleTimeMillis

    • softMinEvictableIdleTimeMillis

    • numTestsPerEvictionRun

    • testOnCreate

    • testOnBorrow jedis调用borrowObject方法时,是否进行有效检查

    • testOnReturn jedis调用returnObject方法时,是否进行有效检查

    • testWhileIdle

    • timeBetweenEvictionRunsMillis

    • evictionPolicyClassName

    • blockWhenExhausted

    • jmxEnabled

    • jmxNameBase

    • jmxNamePrefix


  3. 重启tomcat,session存储即可生效

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值