仿牛客网社区开发——第4章 Redis,一站式高性能存储方案
作者:互联网
Redis 入门
• Redis 是一款基于键值对的 NoSQL 数据库,它的值支持多种数据结构:
字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等。
• Redis 将所有的数据都存放在内存中,所以它的读写性能十分惊人。同时,Redis 还可以将内存中的数据以快照或日志的形式保存到硬盘上,以保证数据的安全性。
快照:把内存中的所有数据存放到硬盘上,占用空间小,恢复速度快;但是存放过程比较耗时间,一般作为备份使用
日志:存取每一条执行的 Redis 命令,实时,速度快;但是日志占用空间大,且恢复起来需要逐条执行命令,时间较长
• Redis典型的应用场景包括:缓存、排行榜、计数器、社交网络、消息队列(不如专业的消息队列服务器,如 kafka)等。
下载 Redis 并配置环境变量
github https://github.com/microsoftarchive/redis
常用命令
存取字符串(strings)
redis-cli //连接Redis
127.0.0.1:6379[2]> select 1 //选择数据库 一个16个 0-15
127.0.0.1:6379[1]> flushdb //刷新(清空)数据库
//存取数据 以key-value形式 存取字符串
127.0.0.1:6379[1]> set test:count 1
OK
127.0.0.1:6379[1]> get test:count
"1"
//增加减少
127.0.0.1:6379[1]> incr test:count
(integer) 2
127.0.0.1:6379[1]> decr test:count
(integer) 1
存取哈希(hashes)
127.0.0.1:6379[1]> hset test:user id 1
(integer) 1
127.0.0.1:6379[1]> hset test:user username wangwu
(integer) 1
//获取相应值
127.0.0.1:6379[1]> hget test:user id
"1"
127.0.0.1:6379[1]> hget test:user username
"wangwu"
存取列表(lists)
127.0.0.1:6379[1]> lpush test:ids 101 102 103 //左端方式存入数据 最终结果为 103 102 101
(integer) 3
127.0.0.1:6379[1]> llen test:ids //取列表长度
(integer) 3
127.0.0.1:6379[1]> lindex test:ids 0
"103"
127.0.0.1:6379[1]> lrange test:ids 0 2
1) "103"
2) "102"
3) "101"
127.0.0.1:6379> rpop test:ids //从右端弹出一个数据 列表长度减一
"101"
127.0.0.1:6379> rpop test:ids
"102"
127.0.0.1:6379> rpop test:ids
"103"
存取集合(sets)
127.0.0.1:6379> sadd test:teachers aaa bbb ccc //存入数据 aaa bbb ccc
(integer) 3
127.0.0.1:6379> scard test:teachers //获取集长度
(integer) 3
127.0.0.1:6379> spop test:teachers //随机弹出一个数据
"aaa"
127.0.0.1:6379> smembers test:teachers //查询集合数据
1) "ccc"
2) "bbb"
存取有序集合(sorted sets)
127.0.0.1:6379> zadd test:students 10 aaa 20 bbb 30 ccc //分数和姓名
(integer) 3
127.0.0.1:6379> zcard test:students //统计有多少个数据
(integer) 3
127.0.0.1:6379> zscore test:students ccc
"30"
127.0.0.1:6379> zrank test:students ccc //返回某一个值的排名 由小到大
(integer) 2
127.0.0.1:6379> zrank test:students aaa
(integer) 0
127.0.0.1:6379> zrange test:students 0 2
1) "aaa"
2) "bbb"
3) "ccc"
常用全局命令
127.0.0.1:6379> keys * //查看所有的key
1) "test:ids"
2) "test:students"
3) "test:teachers"
127.0.0.1:6379> type test:ids //看某一个key的类型
list
127.0.0.1:6379> exists test:user //是否存在某一个key
(integer) 0
127.0.0.1:6379> expire test:ids 10 //给key设置过期时间 /秒
(integer) 1
127.0.0.1:6379> keys *
1) "test:ids"
2) "test:students"
3) "test:teachers"
127.0.0.1:6379> keys *
1) "test:students"
2) "test:teachers"
注意点:
- Redis 的一些基本概念,NoSQL,快照日志等
- Redis 的五种常见的数据类型及其常用命令,以及常用的全局命令
Spring 整合 Redis
引入依赖 spring-boot-starter-data-redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
(不需要写版本,pom 父文件中定义了互相兼容的版本)
配置 Redis - 配置数据库参数 - 编写配置类,构造 RedisTemplate
# RedisProperties
spring.redis.database=11 //选择的库 一共16个 从0-15
spring.redis.host=localhost //地址ip
spring.redis.port=6379 //端口
访问 Redis
编写 RedisConfig
- 方法名就是 Bean 的名字(后面自动装配的时候必须写相同的名字,否则报错)
- 因为 Redis 是数据库,RedisTemplate 要想具备访问数据库的能力,它得能够创建连接。连接是由连接工厂创建的,所以需要注入连接工厂,再注入给 redisTemplate。在方法参数里声明连接工厂(在定义一个 Bean 的时候,方法上声明了这样的参数,Spring 会自动把它注入进来,它已经被容器装配了),再调用 template.setConnectionFactory(factory) 设置连接工厂
- 因为我们写的是 Java 程序,得到的数据是 Java 类型的数据。最终需要把数据存到 Redis 数据库里,需要指定一种序列化的方式
- 做完了一些设置后,最后需要调用 template.afterPropertiesSet() 触发一下使其生效
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 设置key的序列化方式
template.setKeySerializer(RedisSerializer.string());
// 设置value的序列化方式
template.setValueSerializer(RedisSerializer.json());
// 设置hash的key的序列化方式
template.setHashKeySerializer(RedisSerializer.string());
// 设置hash的value的序列化方式
template.setHashValueSerializer(RedisSerializer.json());
template.afterPropertiesSet();
return template;
}
}
通过 redisTemplate 访问 Redis
· redisTemplate.opsForValue()
@Test
public void testStrings() {
String redisKey = "test:count";
redisTemplate.opsForValue().set(redisKey, 1);
System.out.println(redisTemplate.opsForValue().get(redisKey));
System.out.println(redisTemplate.opsForValue().increment(redisKey));
System.out.println(redisTemplate.opsForValue().decrement(redisKey));
}
· redisTemplate.opsForHash()
@Test
public void testHashes() {
String redisKey = "test:user";
redisTemplate.opsForHash().put(redisKey, "id", 1);
redisTemplate.opsForHash().put(redisKey, "username", "zwc");
System.out.println(redisTemplate.opsForHash().get(redisKey, "id"));
System.out.println(redisTemplate.opsForHash().get(redisKey, "username"));
}
· redisTemplate.opsForList()
@Test
public void testLists() {
String redisKey = "test:ids";
redisTemplate.opsForList().leftPush(redisKey, 101);
redisTemplate.opsForList().leftPush(redisKey, 102);
redisTemplate.opsForList().leftPush(redisKey, 103);
System.out.println(redisTemplate.opsForList().size(redisKey));
System.out.println(redisTemplate.opsForList().index(redisKey, 0));
System.out.println(redisTemplate.opsForList().range(redisKey, 0, 2));
System.out.println(redisTemplate.opsForList().leftPop(redisKey));
System.out.println(redisTemplate.opsForList().leftPop(redisKey));
System.out.println(redisTemplate.opsForList().leftPop(redisKey));
}
· redisTemplate.opsForSet()
@Test
public void testSets() {
String redisKey = "test:teachers";
redisTemplate.opsForSet().add(redisKey, "刘备", "关羽", "张飞", "赵云", "诸葛亮");
System.out.println(redisTemplate.opsForSet().size(redisKey));
System.out.println(redisTemplate.opsForSet().pop(redisKey));
System.out.println(redisTemplate.opsForSet().members(redisKey));
}
· redisTemplate.opsForZSet()
@Test
public void testSortedSets() {
String redisKey = "test:students";
redisTemplate.opsForZSet().add(redisKey, "孙悟空", 90);
redisTemplate.opsForZSet().add(redisKey, "猪八戒", 60);
redisTemplate.opsForZSet().add(redisKey, "沙和尚", 50);
redisTemplate.opsForZSet().add(redisKey, "唐僧", 80);
redisTemplate.opsForZSet().add(redisKey, "白龙马", 70);
System.out.println(redisTemplate.opsForZSet().zCard(redisKey));
System.out.println(redisTemplate.opsForZSet().score(redisKey, "孙悟空"));
System.out.println(redisTemplate.opsForZSet().reverseRank(redisKey, "猪八戒"));
System.out.println(redisTemplate.opsForZSet().range(redisKey, 0, 4));
}
· 全局操作
@Test
public void testKeys() {
redisTemplate.delete("test:user");
System.out.println(redisTemplate.hasKey("test:user"));
redisTemplate.expire("test:students", 10, TimeUnit.SECONDS);
}
· 把 key 绑定到一个对象上,不用多次传入 key
// 多次访问同一个key
@Test
public void testBoundOperations() {
BoundValueOperations operations = redisTemplate.boundValueOps("test:count");
// 利用绑定的对象操作
operations.increment();
operations.increment();
operations.increment();
operations.increment();
operations.increment();
System.out.println(operations.get());
}
· 编程式事务
- 当一个客户端执行 MULTI 命令之后,它就进入了事务模式,这时用户输入的所有数据操作命令都不会立即执行,而是会按顺序放入一个事务队列中,等待事务执行时再统一执行。
- 当事务成功执行时,EXEC 命令将返回一个列表作为结果,这个列表会按照命令的入队顺序依次包含各个命令的执行结果。
@Test
public void testTx() {
Object obj = redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
String redisKey = "test:tx";
operations.multi();
operations.opsForSet().add(redisKey, "zhangsan");
operations.opsForSet().add(redisKey, "lisi");
operations.opsForSet().add(redisKey, "wangwu");
System.out.println(operations.opsForSet().members(redisKey));
return operations.exec();
}
});
System.out.println(obj); // [1, 1, 1, [zhangsan, lisi, wangwu]]
}
这里的 sout 输出为空,个人猜测是因为在 Java 代码的 multi 和 exec 之间的所有 Redis 命令被 Redis 加到事务执行命令队列,但是 Java 代码这个时候就已经被执行了,但是 members 那条命令不回立刻返回结果,所以这个时候 sout 拿不到结果就输出空。到最后 exec,Redis 把队列里的命令都执行了,返回一个结果,最后打印出来。感觉就是把 multi 和 exec 之间的 Redis 命令抽取出来了。(不一定对)
注意点:
- Autowired 的时候,参数名需要和 Bean 的名字完全一样,否则会报错
- RedisConfig 的代码编写,注入连接工厂、设置序列化方式以及最后调用 afterPropertiesSet()
- 各种常见操作方法,包括绑定 key 的方法
- Redis 的事务特性(见上),multi、exec。
标签:127.0,仿牛,0.1,Redis,redisKey,6379,客网,test,redisTemplate 来源: https://www.cnblogs.com/CWZhou/p/16356310.html