1. redis
简而言之,redis就是以key-value形式存储数据的非关系型数据库
redis的定位是缓存,提高数据读写速度,减轻对数据库的存储与访问压力
1.1 redis的优缺点
redis具有以下优点:
- 性能极高:对数据进行高并发读写–>因为他是直接在内存中进行读写的
- 对海量数据的高效率存储和访问
- 对数据的可拓展性和高可用性
- 因为是单线程操作,所以天生就是性能安全的
redis具有以下缺点:
- 不支持事务
- 无法处理复杂的关系数据库模型
1.2 redis中的数据类型
redis 中所有命令操作都大致如下 :
类型命令 key 参数数据
1.2.1 String类型
Map<String,String> map
- set key value : 存入键值对
- get key : 根据键,取值
- incr key : 根据键,将该键的值增加 1
- decr key : 根据键,将该键的值减少 1
- setex key timeout value : 存入一个键值对, timeout 表示这个键值对多久后过期,单位秒
- ttl key : 查询指定key还有多久时间过期,返回值为 -1 时说明是永久的, -2 说明不存在或已经销毁
- setnx key value : key不存在时存入该键值对,key存在时不做反应
- mset key1 value1 key2 value2 : 批量存入键值对
存入键值对时,如果key值相同时,后者覆盖前者
可以用于计数器等场景
1.2.2 hash类型
hash类型结构大致如下 :
Map< String , Map<String,String> > map
- hset key hashkey hashvalue : 存入一个hash对象
- hget key hashkey : 根据hash对象的hashkey值,取其对应的hashvalue值
- hexists key hashkey : 判断hash对象是否拥有某个键
- hdel key hashkey : 根据hash对象的hashkey,删除对应的键值对
- hlen key : 获取hash对象键的数量
- hkeys key : 获取hash对象的所有键
- hvals key : 获取hash对象的所有值
- hgetall key : 获取hash对象的所有数据
在实际开发中, hash 类型用的很少,一般不建议使用
1.2.3 list类型
list类型是类似于双向链表的结构,既可以作为栈,又可以作为队列
Map <String,List> map
- rpush key value : 在右边添加键值对
- lpush key value : 在左边添加键值对
- lpop: 弹出最左边的数据
- rpop: 弹出最右边的数据
- lrange key start end : 范围显示列表数据,想全部显示则将范围设为: 0 -1
- llen key : 获取列表长度
- lset key index value : 根据索引修改数据 , 索引是从0开始的
- lindex key index : 根据索引取列表中数据
可以用于一个用户收藏了什么文章的类似场景
1.2.4 set类型
String类型的 , 无序的 集合
- sadd key value : 向集合中添加元素
- smembers key : 列出指定集合的数据
- srem key value : 删除集合中的元素
- spop key count : 随机弹出指定数量的集合中的元素
- sdiff key1 key2 : 返回key1集合的差集
- sinter key1 key2 : 返回两集合交集
- sunion key1 key2 : 返回两集合并集
- scard key : 返回指定集合元素个数
适用于去重 , 或者抽奖之类的应用场景
1.2.5 zset类型
- zadd key num name : 向指定集合中存入分数和名称
- zrange key start end : 从start到end,按照分数升序输出名称
- zrevrange key start end : 从start到end,按照分数降序输出名称
- zrank key name : 按照升序,返回指定集合中,name的排名
- zrevrank key name : 按照降序,返回指定集合中,name的排名
- zcard key : 返回指定集合中的元素个数
zset可以用来做 实时排行 榜
总结 :
- 需要排序 : zset
- 数据是多个,允许重复:list
- 数据是多个,不允许重复:set
- 其他 :string
设计key和value的原则:保证 :唯一性,可读性,灵活性,时效性
1.3 redis进阶
1.3.1 高级命令
- keys * : 返回所有键 , 可以拓展使用 , 比如 : keys ha* 就是返回以 ha 开头的所有 key
- exists key : 查询是否存在指定 key , 存在返回 1 , 不存在返回 0
- expire key seconds : 为一个 key 设置过期时间,时间一到该 key 就会销毁
- persist key : 取消指定 key 的过期操作
- flushdb : 清空当前数据库
- flushall : 清空所有数据库
- select [数字0~15] : 选择进入某个数据库,一共有16个,默认在第0个数据库
- dbsize : 查看数据库中 key 的数量
1.3.2 redis的持久化机制
redis缓存是暂时将数据保存在内存中的,但是有时候数据会需要持久化,这需要将内存中的数据同步到硬盘来保证数据的持久化,而 Redis 进行持久化有两种方式:
-
RDB方式 :
可以通过配置,使 redis 在n秒内,如果超过指定数量的key被修改了,就自动同步
但是这种方式存在缺点,因为有时间间隔,如果在间隔内出现意外,数据就会丢失
-
AOF方式 :
append-only file 的缩写
使用这种方式,redis会将所有收到的命令,用类似于MySQL中的SQL语句一样的形式,写在文件中,当redis再启动时,再读取这些命令,将数据写回内存中
启动 aop 持久化有三种方式:
- always :收到命令就立刻写到磁盘
- everysecond :每秒写入一次(折中方式,建议)
- no :完全依赖os
1.4 redis的实际运用
1.4.1 jedis的使用
使用jedis之前先导入相关依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
注意在 redis 中的命令,就是 jedis 中的方法,两者长得一模一样
@Test
public void testJedis(){
// 1:创建Jedis连接池,告诉端口在哪
JedisPool pool = new JedisPool("localhost", 6379);
// 2:从连接池中获取Jedis对象
Jedis jedis = pool.getResource();
// 3:我们只需要操作这里就行
// 4:关闭资源
jedis.close();
pool.destroy();
}
其中,1,2,4 步是固定的,只需要去操作第三步就行,比如:
//存入一个键值对
jedis.set("no1", "66");
//删除一个键值对
jedis.del("no1");
....
1.4.2 SpringBootTemplate 使用
继承SpringBoot,以SpringBootTemplate 的方式使用
导入相关依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
修改配置文件:
#application.properties
spring.redis.host=localhost
spring.redis.port=6379
#spring.redis.password=admin #根据情况配置
使用 SpringBootTemplate 操作 redis , 相关的类是:RedisTemplate<Object,Object> template
但是因为一些公司规定,key-value都使用string类型,所以 上面那个类等价于:StringRedisTemplate
在调用的方法上来说,一般用 StringRedisTemplate 调用的方法和jedis有所区别,很多都是名称补全的
调用不同类型的方法的对象如下:
template.opsForValue().xxx(); //操作字符串
template.opsForHash().xxx(); //操作hash类型
template.opsForList().xxx(); //操作list类型
template.opsForSet().xxx(); //操作set类型
template.opsForZSet().xxx(); //操作Zset类型
对于一些 redis 的全局命令而言,直接就是 template 的方法
//删除所有键值对
Set<String> keys = template.keys("*");
template.delete(keys);
//查询 xx 过期时间
template.getExpire("xx");
//是否存在某个键
template.hasKey("xx");
//取消某个键过期时间
template.persist("xx");
1.5 其他
项目中操作涉及到缓存操作,首选redis,但是不是唯一可选的,还有 memcache
有些公司会约定:所有的redis的key跟value都使用字符串(排除使用zeset场景)
redis事务的执行不是原子性的(所以其实就是不支持事务,没意思~)
默认缓存大小为100M,所以当缓存在快要达到100m时,就会触发redis的内存淘汰机制
实战小项目:
需求 :模拟显示一篇文章的访问量 :
思路:
- 每次访问,访问数加1并显示在页面上
- 每次访问页面,立刻 发送异步请求到控制层,带上当前文章的 id
- 控制层接受到文章 id , 调用业务层方法,将对应文章的访问数加1,并将访问数返回给前端页面的回调函数
- 在回调函数中,把参数设置给页面的访问数量
业务层:
@Autowired
private StringRedisTemplate template;
// 拿到 id 之后,给对应id的文章的阅读数量,加一,然后将阅读数返回
@Override
public int CountRead(String id) {
//因为我们的key设计,需要考虑到可读性,此处一个id,让人摸不着头脑
//增强其可读性,做到见名知义
String key = "articleId:" + id;
// 找到数据库中 key 对应的value,将其递增1,返回value值,是Long类型
Long totalRead = template.opsForValue().increment(key);
// Long类型转化为 int 类型并返回
return totalRead.intValue();
}
控制层:
@RestController
@RequestMapping("read")
public class ReadController {
@Autowired
private IReadService readService;
@PostMapping("/countAdd")
public Object countAdd(String id){
//在这里调用业务方法,完成阅读数+1
//拿到返回的阅读数,返回给前端
int totalRead = readService.CountRead(id);
return totalRead;
}
}
前端页面:
<script>
// 页面加载完毕执行
$(function () {
// 发送异步请求,参数为文章的 id , 模拟写一个假id上来
$.post("/read/countAdd",{id:666},function (data) {
// 回调函数data就是文章的阅读数,就是存在 redis 中的key-value的value值
// 将这个value值赋值给对应标签
$("#totalRead").html(data);
})
}</script>
2 MongoDB和Redis的区别
Redis 缓存 : 数据存在内存中,其“缓存”的性质,远远大于其“数据存储”的性质,其数据的增删改查也只是像变量操作一样简单
MongoDB 数据库 :存储数据的系统,增删改查可以添加很多条件,就像SQL数据库一样很灵活
两者都是非关系型数据库–NoSQL,采用结构型数据存储,主要区别在于:
-
内存管理:
- Redis数据全部写在内存中,定期写入磁盘,内存不够时,采用LRU算法删除数据
- MongoDB的数据会优先存在内存中,内存不够时,只将热点数据放入内存,其他数据存在磁盘
-
数据结构:
- Redis支持丰富的数据结构:包括hash,set,list
- MongoDB数据结构比较单一,但是支持丰富的数据表达,索引,最类似关系型数据库
-
数据量和性能:
-
当物理内存够用的时候,性能:redis>MongoDB>mysql
数据量:mysql>mongodb>redis
-
-
可靠性:
- MongoDB 采用binlog的方式,支持持久化,增加可靠性
- redis 依赖快照进行持久化,AOF增强可靠性,但是同时会影响到访问性能
因此MongoDB的可靠性优于redis
两者各自的应用场景:
在说应用场景之前先说一个概念 :
TPS : Transactions Per Second : 每秒传输的事务处理个数 ,即服务器每秒处理的事务数;TPS 包括一条消息入和一条消息出,加上一次用户数据库访问
场景一:项目要求 TPS 特别高的,比如微博点赞数,触发频率很高,并发量很大 : 使用 redis
场景二:项目中设计评论的内容,且这个评论表的数据后期会很大,而且还会要求在这个庞大数据量的情况下进行复杂的查询:使用 MongoDB
场景三:推送,评论,用MongoDB,目前是百万级数据,以后是千万级,亿级
ongoDB 采用binlog的方式,支持持久化,增加可靠性
- redis 依赖快照进行持久化,AOF增强可靠性,但是同时会影响到访问性能
因此MongoDB的可靠性优于redis
两者各自的应用场景:
在说应用场景之前先说一个概念 :
TPS : Transactions Per Second : 每秒传输的事务处理个数 ,即服务器每秒处理的事务数;TPS 包括一条消息入和一条消息出,加上一次用户数据库访问
场景一:项目要求 TPS 特别高的,比如微博点赞数,触发频率很高,并发量很大 : 使用 redis
场景二:项目中设计评论的内容,且这个评论表的数据后期会很大,而且还会要求在这个庞大数据量的情况下进行复杂的查询:使用 MongoDB
场景三:推送,评论,用MongoDB,目前是百万级数据,以后是千万级,亿级
场景四:直播弹幕等用redis,用来存储一些热数据,量不大但是操作频繁