Redis入门
2019-12-03 本文已影响0人
远方的橄榄树
一、工具
- redis
Redis版本: 3.2.100
下载链接:https://github.com/microsoftarchive/redis/releases - AnotherRedisDesktopManager
免费的可视化管理工具
下载链接:https://github.com/qishibo/AnotherRedisDesktopManager/releases
二、简介
- Redis是完全开源免费,遵守BSD协议,一个高性能的key - value数据库。
- Redis 的优势:
1、支持数据持久化,可以将内存中的数据保存保存在磁盘中。
2、不仅支持key - value的数据类型,还提供了list、set、zset、hash等数据结构的存储。
3、极高的读写速率。
4、Redis的所有操作都是原子性的,意思是要么成功执行、要么失败完全不执行。多个操作也支持事务。 - Redis在web开发中主要用于存储缓存的数据。
三、Redis的基本操作
- 启动和关闭redis
net start redis
net stop redis
- 命令行访问和退出redis
D:\Redis>redis-cli
127.0.0.1:6379> exit
D:\Redis>
- 访问redis并能正常显示中文
D:\Redis>redis-cli --raw
我是用 IDEA的
terminal
打开命令行的,如果用windows的cmd工具好像无法正常显示中文。
- key 常用操作
127.0.0.1:6379> set name earth
OK
127.0.0.1:6379> type name # 判断value类型
string
127.0.0.1:6379> exists name # 判断key是否存在
(integer) 1
127.0.0.1:6379> del name # 删除key
(integer) 1
127.0.0.1:6379> exists name
(integer) 0
四、Redis的数据类型
Redis支持5种数据类型:string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(sorted set:有序集合)
- string
127.0.0.1:6379> set name jarry
OK
127.0.0.1:6379> get name
jarry

- list
list内的元素是可重复的。
127.0.0.1:6379> rpush jobs teacher # 右边添加元素
1
127.0.0.1:6379> rpush jobs police
2
127.0.0.1:6379> lpush jobs doctor # 左边添加元素
3
127.0.0.1:6379> lpush jobs worker
4
127.0.0.1:6379> lrange jobs 0 -1 # 遍历所有值
worker
doctor
teacher
police
127.0.0.1:6379> lrange jobs 0 2 # 遍历序号为0~2的值
worker
doctor
teacher
127.0.0.1:6379> lpop jobs # 左边删除一个值
worker
127.0.0.1:6379> rpop jobs # 右边删除一个值
police
127.0.0.1:6379> lrange jobs 0 -1
doctor
teacher

- set
redis的set是字符串类型的无序集合。集合是通过哈希表实现的。因此添加、删除查找时间复杂度都是O(1)。
127.0.0.1:6379> sadd names mike
1
127.0.0.1:6379> sadd names jack
1
127.0.0.1:6379> sadd names lily
1
127.0.0.1:6379> sadd names mike
0
127.0.0.1:6379> sadd names tom
1
127.0.0.1:6379> smembers names
lily
mike
jack
tom
127.0.0.1:6379> sismember names tom
1
127.0.0.1:6379> sismember names jarry
0

zset
zset与set类似,都是字符串类型元素的集合,并且集合内元素不能重复。
不同的是,zset每个元素都会关联一个double类型的分数。redis通过分数来为集合中的成员进行从小到大的排序。
zset的元素是惟一的,但分数可以重复。
127.0.0.1:6379> zadd countries 1 China # 添加元素
(integer) 1
127.0.0.1:6379> zadd countries 3 America
(integer) 1
127.0.0.1:6379> zadd countries 8 Japan
(integer) 1
127.0.0.1:6379> zadd countries 5 England
(integer) 1
127.0.0.1:6379> zrange countries 0 -1 # 遍历
1) "China"
2) "America"
3) "England"
4) "Japan"
127.0.0.1:6379> zrange countries 0 2 withscores # 遍历member和score
1) "China"
2) "1"
3) "America"
4) "3"
5) "England"
6) "5"
127.0.0.1:6379> zadd countries 11 India
(integer) 1
127.0.0.1:6379> zadd countries 11 Canada
(integer) 1
127.0.0.1:6379> zadd countries 12 Canada # 添加失败
(integer) 0
127.0.0.1:6379> zrange countries 0 -1
1) "China"
2) "America"
3) "England"
4) "Japan"
5) "India"
6) "Canada"
127.0.0.1:6379> zrem countries America # 删除
(integer) 1
127.0.0.1:6379> zrem countries Brazil # 删除失败
(integer) 0
127.0.0.1:6379> zrange countries 0 -1
1) "China"
2) "England"
3) "Japan"
4) "India"
5) "Canada"

hash
redis hash是一个键值对集合。
hash是一个string类型的key和value的映射表,hash特别适合存储对象。
127.0.0.1:6379> hset person name smallbear
(integer) 1
127.0.0.1:6379> hset person age 21
(integer) 1
127.0.0.1:6379> hset person gender man
(integer) 1
127.0.0.1:6379> hset person hobby acg
127.0.0.1:6379> hgetall person # 获取所有键值对
1) "name"
2) "smallbear"
3) "age"
4) "21"
5) "gender"
6) "man"
7) "hobby"
8) "acg"
127.0.0.1:6379> hget person gender
"man"
127.0.0.1:6379> hdel person gender # 删除一个属性
(integer) 1
127.0.0.1:6379> hgetall person
1) "name"
2) "smallbear"
3) "age"
4) "21"
5) "hobby"
6) "acg"

五、redis发布订阅
Redis发布订阅(subscribe / publish)是消息通信模式:发布者(pub)发布消息,订阅者(sub)接收消息。
redis客户端可以订阅任意数量的频道。


- 创建订阅频道
sub-channel
127.0.0.1:6379> subscribe sub-channel
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "sub-channel"
3) (integer) 1
- 开启一个新的redis客户端,并发送消息
127.0.0.1:6379> publish sub-channel hello,sub-channel
(integer) 1
127.0.0.1:6379> publish sub-channel "I publish some message to you."
(integer) 1
- 订阅者的客户端显示如下
1) "message"
2) "sub-channel"
3) "hello,sub-channel"
1) "message"
2) "sub-channel"
3) "I publish some message to you."
六、redis事务
- redis事务可以一次执行多个命令,其执行过程分为3个阶段
1、开始事务
2、命令入队
3、执行事务 - 事务有三个重要的保证
1、批量操作在EXEC命令执行前会放到队列中缓存。
2、收到EXEC命令后命令进入事务执行,事务中的任意命令执行失败,其余命令依然执行。
3、在事务执行的过程中,其他客户端提交的命令不会插入到事务的执行命令中。
> multi # 开始事务
OK
> sadd heroes 亚索
QUEUED
> sadd heroes 瑞雯
QUEUED
> sadd heroes 提莫
QUEUED
> exec # 执行事务内的命令
1
1
1
七、spring boot集成redis
- spring boot项目引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
- 配置redisTemplate,指定hashValue/value的存储格式为json(默认为序列化)
@Configuration
public class RedisConfig {
@Bean
public Jackson2JsonRedisSerializer<Object> redisSerializer() {
Jackson2JsonRedisSerializer<Object> redisSerializer =
new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
redisSerializer.setObjectMapper(mapper);
return redisSerializer;
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory,
Jackson2JsonRedisSerializer<Object> jsonRedisSerializer) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
- 配置文件
#Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=1ms
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=3000
- 添加实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User{
private Long id;
private String name;
private Integer age;
}
- 测试
@SpringBootTest
class RedisDemo2ApplicationTests {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Test
public void stringTest() {
ValueOperations<String, Object> opsForValue = redisTemplate.opsForValue();
opsForValue.set("hello", "world");
System.out.println(opsForValue.get("hello")); // world
}
@Test
public void objectTest() {
ValueOperations<String, Object> opsForValue = redisTemplate.opsForValue();
// 保存
User user = new User(1L, "小明", 14);
opsForValue.set("user:" + user.getId() , user);
//获取
User target = (User) opsForValue.get("user:1");
System.out.println(target); // User(id=1, name=小明, age=14)
}
@Test
public void listTest() {
ListOperations<String, Object> opsForList = redisTemplate.opsForList();
// 插入
String key = "numbers";
opsForList.rightPush(key, 4);
opsForList.rightPush(key, 13);
opsForList.rightPush(key, 31);
opsForList.leftPush(key, -3);
opsForList.leftPush(key, 100);
// 遍历
List numbers = opsForList.range(key, 0, -1);
System.out.println(numbers); // [100, -3, 4, 13, 31]
//删除
opsForList.rightPop(key); // 删除最右边 31
opsForList.remove(key, 2, -3); // 删除第二个元素 -3 若object与列表的元素不同,则删除失败
}
@Test
public void hashTest() {
HashOperations<String, String, Object> opsForHash = redisTemplate.opsForHash();
String key = "pet";
// 添加
opsForHash.put(key, "name", "二哈");
opsForHash.put(key, "age", 4);
opsForHash.put(key, "type", "dog");
opsForHash.put(key, "test", "test");
//获取
Map<String, Object> pet = opsForHash.entries("pet");
System.out.println(pet); // {name=二哈, age=4, type=dog, test=test}
opsForHash.delete(key, "test"); // 删除key为"test"的元素
}
}




八、redis实现消息队列
- 添加web依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 创建消息接受者
@Slf4j
public class Receiver {
private CountDownLatch latch = new CountDownLatch(1);
public void receive(String msg) {
log.info("接收消息==>" + msg);
latch.countDown();
}
public CountDownLatch latch() {
return latch;
}
}
- 创建消息监听
@Configuration
public class MQConfig {
@Bean
public Receiver receiver() {
return new Receiver();
}
@Bean
public MessageListenerAdapter listenerAdapter() {
return new MessageListenerAdapter(receiver(), "receive");
}
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter, new PatternTopic("chat"));
return container;
}
}
- 测试
@RestController
@RequestMapping("mqTest")
public class MQController {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private Receiver receiver;
@GetMapping("sendMsg")
public String sendMsg(@RequestParam("msg") String msg) {
redisTemplate.convertAndSend("chat", msg);
try {
receiver.latch().await(1000, TimeUnit.MICROSECONDS);
} catch (InterruptedException e) {
return "消息发送失败!";
}
return "消息发送成功";
}
}
- 测试结果
控制台打印接收消息==>"你好,redis"
。
测试1
九、redis存储session
- 添加
spring-session
依赖
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
- 修改配置文件
application.properties
#session有效期限30秒
#server.servlet.session.timeout=PT30s
#session存储方式
spring.session.store-type=redis
#用于存储会话的秘钥的命名空间
spring.session.redis.namespace=spring:session
#指定更新策略
spring.session.redis.flush-mode=on_save
- 在启动类上添加
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 60 * 60 * 2)
的注解,开启redis缓存session。maxInactiveIntervalInSeconds
表示session的存活时间。 - 创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SysUser implements Serializable {
private Long id;
private String username;
private String password;
}
- 模拟数据库操作
@Service
public class UserService {
private List<SysUser> users = new ArrayList<>();
@PostConstruct // bean初始化
public void init() {
users.add(new SysUser(1L, "tom", "123456"));
users.add(new SysUser(2L, "jack", "123456"));
users.add(new SysUser(3L, "smallbear", "123456"));
}
public Optional<SysUser> login(String username, String password) {
Optional<SysUser> sysUserOpt = users.stream()
.filter(user -> Objects.equals(user.getUsername(), username)
&& Objects.equals(user.getPassword(), password))
.findFirst();
return sysUserOpt;
}
}
- 创建登录
Controller
@RestController
public class LoginController {
@Autowired
private UserService userService;
@GetMapping("home")
public String home() {
return "home";
}
@PostMapping("login")
public String login(HttpServletRequest request, String username, String password) {
Optional<SysUser> userOptional = userService.login(username, password);
if (userOptional.isPresent()) {
request.getSession().setAttribute("user", userOptional.get());
return "登录成功";
}
return "登录失败";
}
@GetMapping("user")
public Object user(HttpServletRequest request) {
return request.getSession().getAttribute("user");
}
}
- 修改web配置
// 拦截器配置
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
SysUser user = (SysUser) request.getSession().getAttribute("user");
if (user != null) {
return true;
}
response.setStatus(403);
response.getWriter().write("please login first");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public LoginInterceptor loginInterceptor() {
return new LoginInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor())
.excludePathPatterns("/home")
.excludePathPatterns("/login")
.excludePathPatterns("/mqTest/**");
}
}
-
测试
不足:redis中的数据还是序列化的,不知道为啥