一. 概述
-
Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
-
Redis 与其他 key - value 缓存产品有以下三个特点:
- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
- Redis支持数据的备份,即master-slave模式的数据备份。
-
Redis 优势
- 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。 - 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
- Redis 特点
- 支持数据持久化,可将内存中的数据保存在磁盘,重启时再次加载
- 支持 KV 类型数据,也支持其他丰富的数据结构存储
- 支持数据备份,即 master-slave 模式的数据备份
- Redis 支持哪些数据结构
- STRING:字符串、整数或浮点数
- LIST:列表,可存储多个相同的字符串
- SET:集合,存储不同元素,无序排列
- HASH:散列表,存储键值对之间的映射,无序排列
- ZSET:有序集合,存储键值对,有序排列
二. Key的命名建议
redis单个key 存入512M大小
- key不要太长,尽量不要超过1024字节,这不仅消耗内存,而且会降低查找的效率;
- key也不要太短,太短的话,key的可读性会降低;
- 在一个项目中,key最好使用统一的命名模式,例如user:123:password;
- key名称区分大小写
三. Redis数据类型
3.1 String
- string是redis最基本的类型,一个key对应一个value。
- string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
- string类型是Redis最基本的数据类型,一个键最大能存储512MB。
3.1.1 语法
赋值语法:
SET KEY_NAME VALUE
Redis SET 命令用于设置给定 key 的值。如果 key 已经存储值, SET 就覆写旧值,且无视类型
SETNX key value //解决分布式锁 方案之一
只有在 key 不存在时设置 key 的值。Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值
MSET key value [key value …]
同时设置一个或多个 key-value 对
取值语法:
GET KEY_NAME
Redis GET命令用于获取指定 key 的值。如果 key 不存在,返回 nil 。如果key 储存的值不是字符串类型,返回一个错误。
GETRANGE key start end
用于获取存储在指定 key 中字符串的子字符串。字符串的截取范围由 start 和 end 两个偏移量决定(包括 start 和 end 在内)
GETBIT key offset
对 key 所储存的字符串值,获取指定偏移量上的位(bit)
MGET key1 [key2…]
获取所有(一个或多个)给定 key 的值
GETSET语法: GETSET KEY_NAME VALUE
Getset 命令用于设置指定 key 的值,并返回 key 的旧值,当 key 不存在时,返回 nil
STRLEN key
返回 key 所储存的字符串值的长度
删除语法:
DEL KEY_Name
删除指定的KEY,如果存在,返回值数字类型。
自增/自减:
INCR KEY_Name
Incr 命令将 key 中储存的数字值增1。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作
自增:INCRBY KEY_Name 增量值
Incrby 命令将 key 中储存的数字加上指定的增量值
自减:DECR KEY_NAME 或 DECYBY KEY_NAME 减值
decR 命令将 key 中储存的数字减1
字符串拼接:APPEND KEY_NAME VALUE
Append 命令用于为指定的 key 追加至未尾,如果不存在,为其赋值
3.1.2 应用场景:
1、String
通常用于保存单个字符串或JSON
字符串数据
2、因String
是二进制安全的,所以你完全可以把一个图片文件的内容作为字符串来存储
3、计数器(常规key-value
缓存应用。常规计数: 微博数, 粉丝数)
INCR
等指令本身就具有原子操作的特性,所以我们完全可以利用redis
的INCR
、INCRBY
、DECR
、DECRBY
等指令来实现原子计数的效果。假如,在某种场景下有3个客户端同时读取了mynum
的值(值为2),然后对其同时进行了加1的操作,那么,最后mynum
的值一定是5。
不少网站都利用redis
的这个特性来实现业务上的统计计数需求。
3.2. 哈希(Hash)
- Redis
hash
是一个string
类型的field
和value
的映射表,hash特别适合用于存储对象。Redis
中每个hash
可以存储2^32 - 1
键值对(40多亿) - 可以看成具有
KEY
和VALUE
的MAP
容器,该类型非常适合于存储值对象的信息, 如:uname,upass,age
等。该类型的数据仅占用很少的磁盘空间(相比于JSON
)
3.2.1 语法
赋值语法:
HSET KEY FIELD VALUE
//为指定的KEY,设定FILD/VALUE
HMSET KEY FIELD VALUE [FIELD1,VALUE1]……
同时将多个 field-value (域-值)对设置到哈希表 key 中。
取值语法:
HGET KEY FIELD
//获取存储在HASH中的值,根据FIELD得到VALUE
HMGET key field[field1]
//获取key所有给定字段的值
HGETALL key //返回HASH表中所有的字段和值
HKEYS key
//获取所有哈希表中的字段
HLEN key
//获取哈希表中字段的数量
删除语法:
HDEL KEY field1[field2]
//删除一个或多个HASH表字段
其它语法:
HSETNX key field value
只有在字段 field 不存在时,设置哈希表字段的值
HINCRBY key field increment
为哈希表 key 中的指定字段的整数值加上增量 increment 。
HINCRBYFLOAT key field increment
为哈希表 key 中的指定字段的浮点数值加上增量 increment 。
HEXISTS key field
//查看哈希表 key 中,指定的字段是否存在
3.2.2 应用场景
Hash的应用场景:(存储一个用户信息对象数据)
- 常用于存储一个对象
- 为什么不用string存储一个对象?
hash是最接近关系数据库结构的数据类型,可以将数据库一条记录或程序中一个对象转换成hashmap存放在redis中。
用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储,主要有以下2种存储方式:- 第一种方式将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。
- 第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的。
总结:
Redis提供的Hash很好的解决了这个问题,Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口
四. 使用Redis的各种方式
在官方网站列一些Java客户端访问,有:Jedis/Redisson/Jredis/JDBC-Redis
等,其中官方推荐使用Jedis
和Redisson
。常用Jedis
。
- 开始在 Java 中使用 Redis 前, 我们需要确保已经安装了 redis 服务及 Java redis 驱动,且你的机器上能正常使用 Java。 Java的安装配置可以参考我们的 Java开发环境配置 接下来让我们安装
Java redis
驱动
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.4.2</version>
</dependency>
- 测试是否连接成功
public class Redis {
public static void main(String[] args) {
String host = "127.0.0.1";
Jedis jedis = new Jedis(host);
System.out.println(jedis.ping());
}
}
-
开放端口(如下命令只针对Centos7以上)
查看已经开放的端口:firewall-cmd --list-ports
开启端口:
firewall-cmd --zone=public --add-port=6379/tcp --permanent
重启防火墙
firewall-cmd --reload
#重启 -
Java操作Redis 设置密码
这个问题是由于Redis没有配置密码的原因导致的,只需要为redis设置密码即可
config get requirepass
: 这是查询redis是否配置密码,如果返回为空,则表明未配置密码
config set requirepass “guoweixin”
这是将redis的密码设置为“guoweixin”
客户端登录:用redis-cli 密码登陆(redis-cli -a password
)
4.1 通过Jedis连接池
4.1.1 普通连接使用
public static void main(String[] args) {
String host = "127.0.0.1";
Jedis jedis = new Jedis(host);
System.out.println(jedis.ping());
}
4.1.2 通过连接池获得redis
public class JedisUtils {
private static JedisPool jedisPool;
private static String host = "localhost";
static{
//设置连接池的参数
JedisPoolConfig config = new JedisPoolConfig();
//设置空闲连接数
config.setMaxTotal(5);
//设置最大连接数
config.setMaxIdle(100);
//设置连接池
//。。。还有很多设置
jedisPool = new JedisPool(config, host);
}
public static Jedis getJedis(){
Jedis jedis = jedisPool.getResource();
return jedis;
}
public static void close(Jedis jedis){
jedis.close();
}
}
@Test
public void test_02(){
Jedis jedis = JedisUtils.getJedis();
System.out.println(jedis.ping());
}
4.1.3 redis 存储hash值
/**
* jedis 存储hash 值
*/
@Test
public void test_03(){
Jedis jedis = JedisUtils.getJedis();
//现在redis中查询
if(jedis.exists("user")){
//返回值类型为map
Map<String, String> map = jedis.hgetAll("user");
System.out.println("redis查询到的对象是:"+map);
}else {
//redis 没有的话在数据库中查找(假装)
User user = new User();
user.setId("1");
user.setAge(20);
user.setUname("zhangsan");
jedis.hset("user", "id", "1");
jedis.hset("user", "age", "20");
jedis.hset("user", "uname", "zhangsan");
System.out.println("mysql查询到的对象是:"+user);
}
//关闭连接
jedis.close();
}
4.2 RedisTemplate (springboot整合redis)
Spring data 提供了RedisTemplate模版
它封装了redis连接池管理的逻辑,业务代码无须关心获取,释放连接逻辑;spring redis同时支持了Jedis,Jredis,rjc 客户端操作;
在RedisTemplate中提供了几个常用的接口方法的使用,分别是
4.2.1 环境配置
- jar:Redis和SpringBoot整合
首先添加POM文件
<!-- redis 和SpringBoot整合 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
由于我们使用SpringBoot 进行测试所以还需要添加一个POM文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
- 编写SpringBoot 运行主类
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
- 编写测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes={App.class})// 指定启动类
public class Redis {
@Autowired
RedisTemplate redisTemplate;
@Test
public void test_04(){
ValueOperations<String, String> result = redisTemplate.opsForValue();
if(redisTemplate.hasKey("abc")){
String value = result.get("abc");
System.out.println("--redis中--"+value);
}else{
//到数据库中查
String b = "hahahaah";
result.set("abc", b);
System.out.println("--mysql中--"+ b);
}
}
}
4.2.2 redis-springboot-RedisTemplate JDK序列化方式更改
默认的是JdkSerializationRedisSerializer
我们需要修改一下
if (this.defaultSerializer == null) {
this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
}
- 加入maven依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.29</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
- 编写
RedisConfig
配置类
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//1, 解决key的序列化方式
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
//2.解决value 的序列化方式
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
ObjectMapper objectMapper = new ObjectMapper();
//针对时间类型的自定义序列化方式
// SimpleModule simpleModule = new SimpleModule();
// simpleModule.addSerializer(DateTime.class, new JodaDateTimeJsonSerializer());
// simpleModule.addDeserializer(DateTime.class, new JodaDateTimeJsonDeserializer());
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// objectMapper.registerModule(simpleModule);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
我们可以与上面的结果进行对比发现序列化方式的结果没有乱码了。
4.2.3 RedisTemplate 操作hash数据
redisTemplate 与用客户端插入hash数据的对比
阿斯顿
@Test
public void test_05(){
User user = new User();
user.setId("1");
user.setAge(12);
user.setUname("张三");
redisTemplate.opsForHash().put("user", user.getId(), user);
System.out.println("存储完毕");
}
在存储对象的时候记得要把要存储的对象实现序列化接口public class User implements Serializable
否则的话会报下面的异常。
org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.redis.User]
4.2.4 redis-springboot-RedisTemplate hash序列化方式更改
从源码中我们可以看到默认的hash序列化方式是jdk提供的,这里我们要更改为String
if (this.hashKeySerializer == null) {
this.hashKeySerializer = this.defaultSerializer;
defaultUsed = true;
}
我们在redisConfig 中设置hash的key value 的序列化方式
//设置hash存储的Ksy value 的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
更改序列化方式前后结果对比:
五. redis 多数据库
Redis
下,数据库是由一个整数索引标识,而不是由一个数据库名称。默认情况下,一个客户端连接到数据库0
。
redis
配置文件中下面的参数来控制数据库总数:
database 16
//(从0开始 1 2 3 …15)
select 数据库
//数据库的切换
移动数据(将当前key移动另个库)
move key名称 数据库
数据库清空:
flushdb
//清除当前数据库的所有key
flushall
//清除整个Redis
的数据库所有key