目录
redis介绍
redis是一个键值形式的数据库,其内部的数据以key-value的形式存储
其value可以包含多种形式,比如list,hashmap等
redis是非关系型数据库,mysql是关系型数据库,区别在于
1、mysql里数据的存储是表,每一个字段可以添加约束,比如unique、not null等。而非关系型数据库没有这种严格的约束
2、mysql的表可以存在主外键约束,实现两个表的关联。非关系型数据库没有实现这种主外键约束。比如记录一个用户购买了什么东西,就要在购买表中记录购买商品的信息
可以在item中只记录id,然后通过另外一个表,记录商品信息,但是这种表的关联需要程序员在业务逻辑中实现,redis是不存在主外键约束的。
3、mysql等关系型数据库的SQL语句是统一的,而非关系型数据库是每一种有不同的查询语言
4、mysql的事务满足ACID四种特性(原子性,隔离性,一致性,持久性),而非关系型数据库
要么没有事务,要么只能满足部分特性
5、关系型数据库数据一般存在于磁盘上,而非关系型数据库数据存在于内存中
6、在对性能要求高时,使用非关系型数据库
linux上redis安装
1、redis使用c语言编写,先安装所需依赖
yum install -y gcc tcl
2、上传安装包并解压
tar -zxvf redis-7.0.11.tar.gz
3、进入redis目录
cd redis-7.0.11
4、运行编译命令
make && make install
5、检查是否安装成功(进入默认安装路径)
redis运行
运行redis(前台启动)
redis-server
后台启动
如果想让redis支持后台启动,就要修改配置文件
进入redis的安装目录
找到redis-conf文件
以配置文件的方式运行
进入安装路径 cd redis-7.0.11
启动运行 redis-server redis.conf
查看是否启动
开机自启动
1、建立系统服务文件
vi /etc/systemd/system/redis.service
2、文件内容
[Unit]
Description=redis-server
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/bin/redis-server /usr/local/src/redis-7.0.11/redis.conf
PrivateTmp=true
[Install]
WantedBy=multi-user.target
其中 ExecStart 前部分是redis的安装路径下的redis-server文件 后一部分是redis.conf的目录
3、重新加载服务
systemctl daemon-reload
4、以下命令可以执行操作
systemctl start redis 启动redis(启动之前杀死已经启动的redis进行)
systemctl status redis 查看redis启动
systemctl stop redis 停止redis
systemctl enable redis redis开机自启
redis客户端
命令行客户端
1、命令行客户端 在安装redis时就已经被安装了
2、操作
可以使用-a输入密码 也可以使用 AUTH命令指定用户名和密码
图形化客户端
下载RedisDesktopManager 软件
https://github.com/lework/RedisDesktopManager-Windows/releases
连接时要设置linux防火墙,让6379端口可通过,或者去服务器后台添加6379端口
systemctl start firewalld.service 开启防火墙服务
firewall-cmd --add-port=6379/tcp --permanent 开启6379端口
firewall-cmd --reload 刷新
firewall-cmd --permanent --query-port=80/tcp ##查看80端口有没开放
比如在客户端操作
在命令行可以进行值的添加和读取
select index [选择第index库,index从0-15]
set 字段 值
get 字段 [获取值]
这里出现了中文乱码
Redis读取中文乱码问题的原因
Redis是一个基于内存的数据存储系统,其内部采用的是二进制数据存储方式。当向Redis中存储中文时,由于Redis只能识别二进制数据,所以需要将中文数据进行编码转换后才能正确存储。然而在读取数据时,如果没有进行正确的解码转换,就会出现中文乱码的情况。
在进入命令窗口时,加入--raw
Redis数据结构介绍
redis是一个key-value形式的数据库,key一般就是字符串类型,而value有8种类型
一些通用命令
1、keys:查询key
2、del +key:删除某一个key
3、EXISTS+key:判断某一个key是否存在
4、EXPIRE key seconds [NX | XX | GT | LT] :给key设置有效期,过期后删除
5、TTL key:查看key的有效期
string类型
当有多个表设计到了id这个字段,redis没有表,如何区分多个id,否则设置另外一个id就会替换之前的id值
在id字段设置层级:比如 user:id;product:id
key就实现了分级存储
hash类型
list类型
底层可以看作一个双向链表
-
LLEN:返回list的长度
set类型
sortedSet类型
1、加入:ZADD stu 85 jack 89 LUCY 82 ROSE 95 TOM 78 JERRY 82 AMY 86 MILES
2、删除tom:ZREM stu TOM
3、获取lucy的分数 ZSCORE stu LUCY
4、获取rose的排名 ZRANK stu rose
5、85分以下的人数: ZCOUNT stu 0 85
6、前三名的同学:
redis的java客户端
1、创建一个maven工程,引入redis依赖
<!-- Redis 依赖 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.1.0</version>
</dependency>
2、建立连接,调用API操作,关闭资源
public class Redis {
//建立连接
private static Jedis jedis = null;
public static Jedis setConection() {
if (jedis == null) {
jedis = new Jedis("162.14.117.216", 6379);//ip地址 和端口
jedis.auth("");//密码
jedis.select(0);//选择的数据库
}
return jedis;
}
//操作数据库
public static void test() {
Jedis jedis = setConection();
System.out.println(jedis.get("age"));//获取字符串类型的数据
System.out.println(jedis.get("user:1"));//获取以json格式存储的数据
System.out.println(jedis.hget("user:3", "name"));//以hash表存储的数据 user:3的name字段
Set<String> set = jedis.zrevrange("stu", 0, 2);//获取SortedSet中 成绩前三名的姓名
for (String s : set) {
System.out.println(s);
}
close();
}
//释放资源
public static void close() {
if (jedis != null)
jedis.close();
}
public static void main(String[] args) {
test();
}
Jedis连接池
jedis本身是线程不安全的,而且频繁创建和销毁线程会有巨大的损耗,使用了连接池来存储线程,实现线程复用
public class JediesPoolFactory {
private static final JedisPool jedisPool;
static {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();//创建连接池对象
jedisPoolConfig.setMaxTotal(5);//最大连接数量
jedisPoolConfig.setMaxIdle(2);//最大空闲连接数目
jedisPoolConfig.setMaxWaitMillis(200);//最大等待时间
jedisPool = new JedisPool(jedisPoolConfig, "162.14.117.216", 6379, 1000, "");
//jedisPoolConfig对象 IP 端口 超时等待时间 密码
}
public static Jedis getResource() {
//返回Jedis对象
return jedisPool.getResource();
}
}
SpringDataRedis
SpringData是Spring中进行数据整合的模块,包含对各种数据库的数据操作,其中对redis的集成模块是SpringDataRedis
1、 创建SpringBoot项目
2、配置文件 application.properties文件
#---------------------------------
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
#连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=1000
#---------------------------------
3、使用
@SpringBootTest
public class Redis {
//1、注入RedisTemplate 实现操作
@Autowired
private RedisTemplate redisTemplate;
@Test
public void test() {
redisTemplate.opsForValue().set("age", 9);
System.out.println(redisTemplate.opsForValue().get("age"));//对字符串类型数据操作
}
}
序列化问题
执行以下代码
redisTemplate.opsForValue().set("name", "七七");
在服务器上获取并没有这个记录
但是使用keys *查看所有的key,这里存在乱码,但是仍然发现是加入了一段信息+name的key
在客户端查看是这样的
这是因为发生了序列化
redisTemplate.opsForValue().set()方法的两个参数类型都是object,因此传入的name和七七,都被当做了java的对象,而redisTemplate底层的处理方式就是利用jdk的序列化工具ObjectOutputStream进行序列化
在set方法中将内容转换为字节,最终使用的就是ObjectOutputStream转换的
因此,最终name和key都被序列化了,最终存放的是
同理,在调用get时,参数key也被序列化,因此获取到的都是null
解决办法
1、StringRedisTemplate继承RedisTemplate,StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据。可以使用StringRedisTemplate进行字符串处理
//1、注入RedisTemplate 实现操作
@Autowired
private StringRedisTemplate redisTemplate;
@Test
public void test() {
redisTemplate.opsForValue().set("name", "七七");
System.out.println(redisTemplate.opsForValue().get("name"));//对字符串类型数据操作
}
2、自定义RedisTemplate的序列化方式
@Configuration
public class RedisConfig {
//创建获取 redisTemplate对象的方法
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
//1、创建redisTemplate对象
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
//2、设置连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
//3、设置json序列化工具
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//引入jaskson库,实现json格式和java对象的转换
ObjectMapper objectMapper = new ObjectMapper();
//setVisibility 设置对象映射器的可见性规则
//PropertyAccessor 是属性访问器 所有Spring创建的Bean对象都使用该接口存取Bean属性值
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//指定指定序列化输入类型
//不指定时 那存储到redis里的数据将是没有类型的纯json,我们调用redis API获取到数据后,java解析将是一个LinkHashMap类型的key-value的数据结构,我们需要使用的话就要自行解析,这样增加了编程的复杂度。
//[{"id":72,"uuid":"c4d7fc52-4096-4c79-81ef-32cb1b87fd28","type":2}]
//指定之后 存储到redis里的数据将是有类型的json数据,例如:
//["java.util.ArrayList",[{"@class":"com.model.app","id":72,"uuid":"c4d7fc52-4096-4c79-81ef-32cb1b87fd28","type":2}]]
// 这样java获取到数据后,将会将数据自动转化为java.util.ArrayList和com.model.app,方便直接使用。
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//4、设置key序列化
redisTemplate.setKeySerializer(RedisSerializer.string()); //RedisSerializer.string()返回的是 StringRedisSerializer.UTF_8 这个常量
redisTemplate.setHashKeySerializer(RedisSerializer.string()); //RedisSerializer.string()返回的是 StringRedisSerializer.UTF_8 这个常量
//5、设置value序列化 (json格式 实际根据业务场景决定·)
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
存入对象,会转为json格式
@Data
@AllArgsConstructor//有参构造
@NoArgsConstructor//无参数构造
public class User {
private String name;
private Integer age;
}
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Test
public void saveUsertest(){
User user=new User("张三",19);
redisTemplate.opsForValue().set("user:6",user);
System.out.println(redisTemplate.opsForValue().get("user:6"));
}
为了在反序列化时知道对象的类型,会将类的class类型存入数据库,造成额外的内存开销
但是对象的类型在反序列化时必须要用到,不将类的class类型存入数据库,就要求在业务处理时手动转换
@Autowired
private RedisTemplate redisTemplate;
@Test
public void saveUsertest() throws JsonProcessingException {
User user = new User("七七", 300);
ObjectMapper objectMapper = new ObjectMapper();
String value = objectMapper.writeValueAsString(user);//将对象转为字符串
redisTemplate.opsForValue().set("user:7", value);
value = (String) redisTemplate.opsForValue().get("user:7");
user = objectMapper.readValue(value, User.class);
System.out.println(user);
}
hash和sortedset结构
@Test
public void test() {
// 获取hash表类型的value
redisTemplate.opsForHash().put("user:3","age",18);
redisTemplate.opsForHash().put("user:3","id",3);
redisTemplate.opsForHash().put("user:3","name","七七");
Map<Object, Object> map = redisTemplate.opsForHash().entries("user:3");
for (Map.Entry<Object, Object> entry : map.entrySet()) {
System.out.print(entry.getKey() + " " + entry.getValue());
System.out.println();
}
System.out.println("-----------");
// "jack",85, "LUCY",89,"ROSE",82, "TOM",95 , "JERRY",78 , "AMY",82 , "MILES",86
redisTemplate.opsForZSet().add("stu1",new User("jack",20),85 );
redisTemplate.opsForZSet().add("stu1",new User("lucy",20),89 );
redisTemplate.opsForZSet().add("stu1",new User("rose",20),82) ;
redisTemplate.opsForZSet().add("stu1",new User("tom",20),95 );
redisTemplate.opsForZSet().add("stu1",new User("jerry",20),78 );
redisTemplate.opsForZSet().add("stu1",new User("amy",20),82 );
redisTemplate.opsForZSet().add( "stu1",new User("miles",20),86);
//获取SortedSet 成绩前三名 "TOM",95 "LUCY",89 "MILES",86
Set<Object> set = redisTemplate.opsForZSet().reverseRange("stu1", 0, 2);
for (Object s : set) {
System.out.println(s);
}
}