目录
redis简介
redis功能
redis学习步骤
windows系统下的redis安装以及卸载
安装
卸载
启动
关闭
使用redis
需要了解的常识问题
redis5大数据类型介绍
类型简介
5大数据类型分类介绍
配置文件详解
redis持久化
RDB
AOF
redis主从复制
redis主从复制配置
redis哨兵模式
java操作redis
获取redis数据库连接的工具类
序列化方式存储对象到redis
使用JSON格式字符串存储对象到redis
spring和redis整合
导入依赖
编写配置文件
简介:Redis:REmote DIctionary Server(远程字典服务器),是完全开源免费的,用C语言编写的,遵守BSD协议,是一个高性能的(key/value)分布式内存数据库,基于内存运行并支持持久化的NoSQL(非关系型数据库)数据库,是当前最热门的NoSql数据库之一,也被人们称为数据结构服务器
1.内存存储和持久化:redis支持异步将内存中的数据写到硬盘上,同时不影响继续服务
2.取最新N个数据的操作,如:可以将最新的10条评论的ID放在Redis的List集合里面
3.模拟类似于HttpSession这种需要设定过期时间的功能
4.发布、订阅消息系统
5.定时器、计数器......
1.数据类型、基本操作和配置
2.持久化:RDB/AOF
3.事务的控制
4.主从复制
进入需要安装的redis目录下面执行:
redis-server --service-install redis.windows.conf --service-name redis6379
redis-server --service-uninstall --service-name redis6379
或者sc delete redis6379
net start redis6379
net stop redis6379
1.启动redis服务
2.redis-cli -p 6379连接端口号为6379的redis
3.输入set k1 v1,就是往redis数据库中存放了一个键为k1值为v1的数据
4.输入 get k1即可获得数据库中保存的k1对应的值
5.退出当前客户端:shutdown(退出Redis),quit(返回)
6;关闭redis服务:net stop redis
7:客户端默认不显示中文redis-cli --raw -p 6379
1.redis默认16个数据库,类似数组下表从零开始,初始默认使用零号库
2.select命令切换数据库:select 3 切换到下标为3的库
3.dbsize命令:查看当前数据库的key的数量
4.flushdb命令:清空当前库
5.flushall命令 : 清空全部库
6.redis默认端口号为6379
7.redis默认不设置密码
类似于关系型数据库的数据库类型,redis的五大数据类型是程序往redis数据库存数据的5种形式
string:string是redis最基本的类型,一个key对应一个 value(重要),value值的最大值为512M
hash:哈希,类似java里的Map集合,常用来存储java里面的Object(非常重要)
list:Redis 列表是简单的字符串列表,按照插入顺序排序, 你可以添加一个元素导列表的头部(左边)或者尾 部(右边),类似于LinkList
set:Redis的Set是string类型的无序不重复集合。它是通过HashTable实现的
zset:(sorted set):有序集合,Redis zset 和 set 一样也 是string类型元素的集合,且不允许重复的成员,不同的是 每个元素都会关联一个double类型的分数 redis正是通过分数来为集合中的成员进行从小到大的排序.zset的成员是唯一的,但分数(score)却可以重复.
对key的操作
keys *:查询该库中所有key
scan:循环查询键
exists key:判断某个key是否存在
expire key 秒钟:为给定的key设置过期时间
ttl key:查看还有多少秒过期,-1表示永不过期,-2表示已过期
type key:查看你的key是什么类型
get key:返回 key 所关 联的字符串值,如果 key 不存在那么返回特殊值 nil
set key value:设置值
del key:删除
对string数据类型的操作
append key value:如果 key 已经存在并且是一个字符串,APPEND 命令将 value 追加到 key 原来的值的末尾。如果 key 不存在, APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样。
strlen key :返回 key 所储存的字符串值的长度。
incr/decr/incrby/decrby:自增/自减/加指定值/减指定值
getrange key start end/setrange key index value:返回 key 中字符串值的子字符串/用 value 参数覆写给定 key 所储存的字符串值,从偏移量 index开始。
setex key seconds value/setnx key value:将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位).如果 key 已经存在,SETEX 命令将覆写旧值/将 key 的值设为 value ,当且仅当 key 不存在。
mset/mget/msetnx:设置多个/获得多个/设置多个当key不存在时
getset:将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
对list数据类型的操作
数据可重复:(l可以理解为头,r理解为尾),有下标,它是一个字符串链表,left、right都可以插入添加;如果键不存在,创建新的链表;如果键已存在,新增内容;如果值全移除,对应的键也就消失了。链表的操作无论是头和尾效率都极高,但假如是对中间元素进行操作,效率就很惨淡了。
lpush/rpush/lrange key start stop:左加入/右加入/获取区间数据0 -1代表取全部
lpop key/rpop key:移除并返回列表头部/尾部
llen key:长度
lrem key count value:移除列表中与参数 value 相等指定count个数的元素。
ltrim key start stop:获取指定区间的数据在赋值给key
rpoplpush k1 k2:将第一个列表尾部元素拿出放到第二个列表头部
lset key index value:将列表 key 下标为 index 的元素的值设置为 value 。
linsert key before/after pivot value:将值 value 插入到列表 key 当中,位于值 pivot 之前或之后,有多个值默认第一个
对hash数据类型的操作
类似于Map集合,KV模式不变,但V是一个键值对
hset/hget/hmset/hmget/hgetall/hdel:插入/获得/多个插入/获得键值/删除
hlen key:返回长度
hexists key field:查看哈希表 key 中,给定域 field 是否存在。
hkeys/hvals:取出所有的域/取出所有的域的值
hincrby key field increment/hincrbyfloat:为哈希表 key 中 的域 field 的值加上增量 increment /为哈希表 key 中的域 field 加上浮点数增量 increment
hsetnx:和set类似,key不存在赋值,key存在操作无效
对set数据类型的操作
sadd/smembers key/sismember key member:将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略/返回集合 key 中的所有成员/判断 member 元素是否集合 key 的成员
scard key:获取集合里面的元素个数
srem key value:删除集合中元素
srandmember key num:某个整数(随机出num个数):那么返回指定集合中的一个随机元素
spop key :移除并返回集合中的一个随机元素。
smove k1 k2 member:将 member 元素从 k1集合移动到 k2集合。
sdiff k1 k2.....:差集,在第一个里面而不在后面任何一个set里面的项
sinter:交集
sunion:并集
对zset数据类型的操作
在set基础上,加一个score值。之前set是k1 v1 v2 v3,现在zset是k1 score1 v1 score2 v2
zadd key score member:将一个或多个 member 元素及其 score 值加入到有序集 key 当中
zrem key value:移除key集合中的value值
zcard:返回元素个数
zcount key min max:返回元素个数,scores分数在min和max之间
zrank key value:返回有序集 key 中成员 value的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。zrevrank key value:从大到小
zrange key index [withscores]:返回区间中的数据(分数从小到大)
zrevrange key index [withscores];返回区间中的数据(分数从大到小)
看详解文档
redis是内存数据库,内存里面的数据在关机或者断电时会清空,那么怎么把内存中的时候永久保存下来就是持久化(把数据保存到本地硬盘),通常用两种方式:
RDB(Redis DataBase)
AOF(Append Only File)
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里,Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
RDB配置
RDB注意事项
1.通过配置满足条件时快照可以自动触发,并产生dump.rdb文件,当然也可以通过save命令手动触发快照,在save的时候阻塞其他操作,bgsave会在后台异步进行快照操作,快照同时还可以响应客户端请求。可以通过lastsave命令获取最后一次成功执行快照的时间
2.执行flushall命令,也会产生dump.rdb文件,但里面是空的,无意义
3.将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务可以自动的把硬盘中的数据导入到内存中
RBD优缺点
优点:适合大规模的数据恢复,对数据完整性和一致性要求不高
缺点:在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改,fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑
以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
AOF配置
AOF默认是关闭的,需要的话可以在配置文件中打开修改默认的appendonly no,改为yesAOF产生的文件的名字默认叫:appendonly.aof
AOF注意事项
AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof,AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename),遍历新进程的内存中数据,每条记录有一条的Set语句。重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似
AOF优缺点
如果rdb和aof文件共存的时候先加载aof文件,由于如果aof文件损坏导致redis无法启动:进入Redis安装目录:输入redis-check-aof --fix appendonly.aof修复
优点:同步持久化每次发生数据变更会被立即记录到磁盘性能较差但数据完整性比较好
缺点:aof运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同
在实际开发过程中一般一台主服务器会有多个从服务器,当主服务器发生故障的时候,从服务器能替代主服务器的功能,那么就要要求主从服务器之间的数据要同步,主服务器实时的吧数据复制到从服务器上,一般主服务器复制写,从服务器负责读,实现读写分离
当从机连接上主机以后会把主机中所有的数据都同步到从机中,并且从机只能读不能写
在从服务器上配置:
slaveof 主机ip 端口号从机每次断开都要执行该命令,可以写入配置文件
slaveof no one:解除从机状态
info replication :查看当前主从状态
当主机发生故障时从从机中自动选出一个替代主机
1.在安装目录下创建sentinel.conf文件(哨兵配置文件)添加内容格式:
sentinel monitor myMonitor 127.0.0.1 6379 1
2.启动哨兵进入哨兵配置文件所在目录:redis-server sentinel.conf --sentinel
3.启动主机和从机
4.设置主机故障
package util;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class RedisUtil {
private static RedisUtil ru = new RedisUtil();
// redis连接池对象
private JedisPool jp;
// 创建连接池配置对象
JedisPoolConfig jpc;
private RedisUtil() {
// 为连接池配置对象赋引用
jpc = new JedisPoolConfig();
// 配置
// 最大空闲连接
jpc.setMaxIdle(10);
// 最大连接数
jpc.setMaxTotal(100);
// 最大等待毫秒数
jpc.setMaxWaitMillis(100000);
// 保证获得的连接都可用
jpc.setTestOnBorrow(true);
// 把配置对象交给连接池对象
jp = new JedisPool(jpc, "127.0.0.1", 6379);
}
public static RedisUtil init() {
return ru;
}
public Jedis getJedis() {
return jp.getResource();
}
}
package util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerUtil {
// 序列化:把对象转换成字节数组
// 反序列化:把字节数组转换成对象
// 序列化——对象→对象流→内存流→字节数组
public static byte[] getByte(Object obj) {
byte[] bytes = null;
// 把对象转换成IO流
ObjectOutputStream oos = null;
// 把数据放在内存流中
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
// 把数据写在内存流中
oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
// 从内存流中取出数据放到直接数组中
bytes = bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (oos != null) {
oos.close();
}
} catch (Exception e2) {
}
}
return bytes;
}
// 反序列化——字节数组→内存流→对象流→对象
public static Object getObject(byte[] bytes) {
// 把字节数组中的对象数据取出来
Object obj = null;
ObjectInputStream ois = null;
// 字节数组→内存流
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
try {
// 内存流→对象流
ois = new ObjectInputStream(bis);
// 对象流→对象
obj = ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (ois != null) {
ois.close();
}
} catch (Exception e2) {
}
}
// 返回对象
return obj;
}
}
redis.clients
jedis
2.9.0
org.apache.commons
commons-pool2
2.4.2
org.springframework.data
spring-data-redis
1.6.0.RELEASE
pom.xml
4.0.0
zhang
redis-spring
0.0.1-SNAPSHOT
4.1.6.RELEASE
org.springframework
spring-core
${spring-version}
org.springframework
spring-beans
${spring-version}
org.springframework
spring-context
${spring-version}
org.springframework
spring-context-support
${spring-version}
org.springframework
spring-expression
${spring-version}
org.springframework
spring-aop
${spring-version}
org.springframework
spring-aspects
${spring-version}
org.springframework
spring-jdbc
${spring-version}
org.springframework
spring-orm
${spring-version}
redis.clients
jedis
2.9.0
org.apache.commons
commons-pool2
2.4.2
org.springframework.data
spring-data-redis
1.6.0.RELEASE
springRedis.xml
package zhang.pojo;
import java.io.Serializable;
public class User implements Serializable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package zhang.dao;
import org.springframework.stereotype.Component;
import zhang.pojo.User;
@Component
public class UserDao {
public User queryUserById() {
System.out.println("查询数据库......");
return new User();
}
}
此处仅为测试,service层没有写接口和实现类,方便而已,读者知道就行。
package zhang.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import zhang.dao.UserDao;
import zhang.pojo.User;
@Service
public class UserServiceImpl {
@Autowired
private UserDao ud;
@Autowired
private RedisTemplate rt;
public User queryUserById() {
// 通过RedisTemplate获取到操作string数据类型的工具类
ValueOperations vo = rt.opsForValue();
// 从Redis中获取该键的value值,如果没有,则返回null
User user = (User) vo.get("user_01");
if (user == null) {
// 说明缓存中没有改对象
// 那么就从数据库值继续查询
user = ud.queryUserById();
// 把查询结果放置在redis中
vo.set("user_01", user);
}
return user;
}
}
package zhang.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import zhang.pojo.User;
import zhang.service.UserServiceImpl;
public class Test {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("classpath:springRedis.xml");
UserServiceImpl usi = app.getBean(UserServiceImpl.class);
User user = usi.queryUserById();
System.out.println(user);
}
}