目录
1 redis简介
redis是一种非关系型数据库。
- 使用内存存储。
- 具有:字符串、列表、集合、散列表和有序集合五种存储类型。
附加特性:支持持久化。
实现了主从复制:执行复制的从服务器连上主服务器,接受主服务器发送的整个数据库的初始副本;之后主服务器执行写命令,都会被发给从服务器去执行。
2 redis数据结构简介
Redis可以存储五种类型数据结构:STRING,LIST,SET,HASH和ZSET
。
数据结构说明如下:
结构类型 | 结构存储的值 | 结构的读写能力 |
---|---|---|
STRING | 可以是字符串、整数和浮点数 | 对字符串整个或者一部分执行操作,对整数和浮点数执行自增或自减操作 |
LIST | 一个链表,每个节点包含一个字符串 | 从链表两端推入或者弹出元素,读取多个元素等 |
SET | 包含字符串的无序集合,不能重复 | 添加、获取、移除,判断是否存在等操作 |
HASH | 包含键值对的无序散列表 | 添加、获取、移除键值对,获取所有键值对 |
ZSET | 字符串成员,与浮点数之间的映射,排序按照分值 | 添加、删除、获取;根据分值范围获取 |
2.1 redis中的字符串
字符串基本命令
命令 | 行为 |
---|---|
GET | 获取给定键的值 |
SET | 设置给定键的值 |
DEL | 删除给定键的值 |
测试执行结果:
2.2 redis的列表
redis列表的基本命令如下:
命令 | 行为 |
---|---|
RPUSH | 将给定值推到列表的右端 |
LRANGE | 获取列表给定范围的值 |
LINDEX | 获取列表在给定位置的单个元素 |
LPOP | 从列表的左侧弹出一个值 |
测试执行结果:
2.3 redis的集合
redis集合基本命令
命令 | 行为 |
---|---|
SADD | 将给定元素追加到集合 |
SMEMBERS | 返回集合包含的所有元素 |
SISMEMBER | 检查给定元素是否存在于集合中 |
SREM | 如果给定元素存在于集合中,那么移除这个元素 |
测试结果:
2.4 redis的散列
散列基本命令
命令 | 行为 |
---|---|
HSET | 在散列里面关联给定的键值对 |
HGET | 获取指定散列的键的值 |
HGETALL | 获取散列包含的所有键值对 |
HDEL | 移除存在的散列 |
测试结果:
2.5 redis的有序集合
有序集合的基本命令
命令 | 行为 |
---|---|
ZADD | 将一个带有分数的成员添加到有序集合 |
ZRANGE | 获取多个元素 |
ZRANGEBYSCORE | 根据分数获取多个元素 |
ZREM | 如果成员存在,则移除 |
测试结果:
3 redis示例-文章投票
3.1 数据结构划分
数据结构分析:
- 书籍信息:包含多个键值对,使用hash进行存储,名称为:
article: + 一个时间戳
。 - 由于需要进行文章的投票和排名:需要使用两个有序集合:
- 1 记录文章发布时间,名称为:
time
。 - 2 记录文章投票得分,名称为:
score
。 - 为了防止一个用户对一篇文章多次投票,需要针对每一个文章设置黑名单集合,名称为:
voted+文章id
。
3.2 搭建java环境
具体步骤如下:
-
新疆springboot项目
-
配置redis信息:
-
配置redis连接和序列化fastjson信息
public class ObjectRedisTemplate extends RedisTemplate<String, Object> {
public ObjectRedisTemplate() {
setKeySerializer(RedisSerializer.string());
setValueSerializer(new GenericFastJsonRedisSerializer());
setHashKeySerializer(RedisSerializer.string());
setHashValueSerializer(new GenericFastJsonRedisSerializer());
}
public ObjectRedisTemplate(RedisConnectionFactory connectionFactory) {
this();
setConnectionFactory(connectionFactory);
afterPropertiesSet();
}
@Override
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
return new DefaultStringRedisConnection(connection);
}
}
@Configuration
public class RedisConfig {
@Bean
public ObjectRedisTemplate objectRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new ObjectRedisTemplate(redisConnectionFactory);
}
}
- 修改redis配置信息redis.conf
-主要是两处:protected-mode yes 改为:protected-mode no;注释掉环回地址#bin 127.0.0.1
3.3 编写测试demo
定义实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Article {
private String title;
private String url;
private String user;
private double time;
private double votes;
}
定义服务包括:投递文章、投票文章和获取所有当前文章
服务类
@Service
public class ArticleService {
/**
* 投票截至时间
*/
private static Integer ONE_WEEK_IN_MILLSECONDS = 7 * 86400000;
/**
* 投票对应分数
*/
private static Integer VOTE_SCORE = 432;
/**
* 文章id原子类
*/
private final AtomicLong atomicId = new AtomicLong(System.currentTimeMillis());
@Autowired
ObjectRedisTemplate objectRedisTemplate;
/**
* 对文章进行投票
* @param username 用户名称
* @param article 文章id
* @return 是否成功
*/
public Boolean articleVote(String username, String article) {
Double time = objectRedisTemplate.opsForZSet().score("time", article);
if (time == null) {
return false;
}
double v = System.currentTimeMillis() - time;
if (v > ONE_WEEK_IN_MILLSECONDS) {
return false;
}
Long add = objectRedisTemplate.opsForSet().add("voted"+article, username);
if (add == null || add == 0) {
return false;
}
objectRedisTemplate.opsForZSet().incrementScore("score", article, VOTE_SCORE);
return true;
}
/**
* 投递文章
* @param user 用户名
* @param title 文章title
* @param link 文章链接url
* @return 文章id
*/
public String postArticle(String user, String title, String link) {
Map<String, String> map = new HashMap<>();
map.put("title", title);
map.put("poster", user);
map.put("link", link);
String articleId = "article:" + atomicId.getAndIncrement();
objectRedisTemplate.opsForHash().putAll(articleId, map);
if (Boolean.FALSE.equals(objectRedisTemplate.opsForZSet().add("time", articleId, System.currentTimeMillis()))) {
return "";
}
if (Boolean.FALSE.equals(objectRedisTemplate.opsForZSet().add("score", articleId, 0.0))) {
return "";
}
return articleId;
}
/**
* 返回所有文章信息
* @return 文章列表
*/
public List<Article> getArticle() {
Set<Object> articleId = objectRedisTemplate.opsForZSet().range("time", 0, -1);
List<Article> list = new ArrayList<>();
for (Object o : articleId) {
Map<Object, Object> entries = objectRedisTemplate.opsForHash().entries((String) o);
Double time = objectRedisTemplate.opsForZSet().score("time", o);
Double score = objectRedisTemplate.opsForZSet().score("score", o);
Article build = Article.builder().title((String) entries.get("title")).url((String) entries.get("poster")).user((String) entries.get("link")).time(time).votes(score).build();
list.add(build);
}
return list;
}
}
Controller类
@RestController
public class ArticleController {
@Autowired
ArticleService articleService;
@RequestMapping("/vote")
public boolean vote(@RequestBody User user) {
return articleService.articleVote(user.getName(), user.getArticle());
}
@RequestMapping("/poster")
public String poster(@RequestBody Article article) {
return articleService.postArticle(article.getUser(), article.getTitle(), article.getUrl());
}
@RequestMapping("/getposter")
public List<Article> getPoster() {
return articleService.getArticle();
}
}
这里的articleId使用原子变量类来保证线程安全。这里的服务根据之前描述的数据结构进行了时机编码。
测试结果(postman):
-
模拟投稿
-
模拟投票
-
获取所有文章
参考文献
[1]《Redis实战》.
[2] Java项目连接远程redis.