0、Redis
参考文档:https://www.cnblogs.com/linianhui/p/what-problem-does-redis-solve.html
1、redis是什么?
一款基于内存高速缓存数据库
2、redis在是什么方面使用?
缓存,毫无疑问这是Redis当今最为人熟知的使用场景。再提升服务器性能方面非常有效;
排行榜,如果使用传统的关系型数据库来做这个事儿,非常的麻烦,而利用Redis的SortSet数据结构能够非常方便搞定;
计算器/限速器,利用Redis中原子性的自增操作,我们可以统计类似用户点赞数、用户访问数等,这类操作如果用MySQL,频繁的读写会带来相当大的压力;限速器比较典型的使用场景是限制某个用户访问某个API的频率,常用的有抢购时,防止用户疯狂点击带来不必要的压力;
好友关系,利用集合的一些命令,比如求交集、并集、差集等。可以方便搞定一些共同好友、共同爱好之类的功能;
简单消息队列,除了Redis自身的发布/订阅模式,我们也可以利用List来实现一个队列机制,比如:到货通知、邮件发送之类的需求,不需要高可靠,但是会带来非常大的DB压力,完全可以用List来完成异步解耦;
Session共享,以PHP为例,默认Session是保存在服务器的文件中,如果是集群服务,同一个用户过来可能落在不同机器上,这就会导致用户频繁登陆;采用Redis保存Session后,无论用户落在那台机器上都能够获取到对应的Session信息。
3、为什么使用redis?
比如缓存可以用Memcache,Session共享还能用MySql来实现,消息队列可以用RabbitMQ,我们为什么一定要用Redis呢?
1.速度快,完全基于内存,使用C语言实现,网络层使用epoll解决高并发问题,单线程模型避免了不必要的上下文切换及竞争条件;注意:单线程仅仅是说在网络请求这一模块上用一个线程处理客户端的请求,像持久化它就会重开一个线程/进程去进行处理
2.丰富的数据类型,Redis有8种数据类型,当然常用的主要是 String、Hash、List、Set、 SortSet 这5种类型,他们都是基于键值的方式组织数据。每一种数据类型提供了非常丰富的操作命令,可以满足绝大部分需求,如果有特殊需求还能自己通过 lua 脚本自己创建新的命令(具备原子性)
Redis还提供了像慢查询分析、性能测试、Pipeline、事务、Lua自定义命令、Bitmaps、HyperLogLog、发布/订阅、Geo等个性化功能。
3.支持持久化(rdb,aof)它的编译安装也是非常的简单,没有任何的系统依赖;各种客户端的语言支持也是非常完善。另外它还支持事务、主从复制、高可用、分布式。
4、redis使用带来的哪些问题?
1.缓存和数据库间数据一致性问题
分布式环境下(单机就不用说了)非常容易出现缓存和数据库间的数据一致性问题,针对这一点的话,只能说,如果你的项目对缓存的要求是强一致性的,那么请不要使用缓存。我们只能采取合适的策略来降低缓存和数据库间数据不一致的概率,而无法保证两者间的强一致性。合适的策略包括 合适的缓存更新策略,更新数据库后要及时更新缓存、缓存失败时增加重试机制,例如MQ模式的消息队列
2.缓存雪崩
缓存雪崩:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉
事前:尽量保证整个 redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。
事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉
事后:利用 redis 持久化机制保存的数据尽快恢复缓存
3.缓存穿透
缓存穿透:大量缓存中不存在的请求key访问直接落到数据库,一般是恶意攻击;
解决:从DB中查询出来数据为空,也进行空数据的缓存,避免DB数据为空也每次都进行数据库查询;
使用布隆过滤器,但是会增加一定的复杂度及存在一定的误判率;
5、redis自己的理解
客户需要功能显示当前热点新闻信息,用户每次请求,都需要去数据库查询当前的热点新闻信息,由于当前功能访问人数量大,所以将查到的数据缓存到在服务器上,刚开始可以,后来服务器内存就会爆满。这样就需要单独一台专门存储缓存服务器,作为redis服务器,减轻项目服务器的压力。但是redis服务器数据有时候会丢失,所有的缓存丢失了,大量的请求查询就到达了数据库,这就是缓存雪崩,这样就用到了redis的持久化,将预先保存到磁盘的redis持久化文件,redis重启将(rdb、aof)加载进redis内存,redis就重新生效了。redis服务器也会挂掉,如果挂掉就需要备份的机器重新补上空缺,但是管理员不可能随时监控,这就需要配置redis的架构使用,(哨兵监察主机主机挂掉选举从机成为主机,主从复制保证从机的内容跟主机一样),有了哨兵和主从复制,解决了redis的主机挂掉。之后,单台服务器的是有上限的,这就需要配置redis集群,对于用户来说没有什么差别,对于redis服务器集群,每台服务器只负责其中的一部分,让这些所有的服务器构成一个整体。保证了redis服务器安全稳定的提供服务。
1、命令
1.1常用命令
cd: 切换目录
ls: 查看当前或指定目录下的文件 -a -l
mkdir: 创建目录 -p 递归创建
touch:创建文件
pwd: 查看当前所在的目录
cp: 复制文件或者目录
[root@master qy114]# cp yy/* -r test #拷贝路径下所有文件 到对应的目录,如果子目录必须 加-r 循环拷贝 test是文件夹
mv: 重命名 或剪切
rm: 删除 -r(删除目录) -f(强制删除)
cat: 查看文件所有内容
tail num: 查看文件尾部num行的内容 日志
vi: (编辑文件内容) -----一般模式 - ------i 编辑模式- esc:进入一般模式 :wq (推出并保存) q! 强制退出 不保存
查找vmtoolsd进程的id
ps -e | awk '$4=="vmtoolsd"{print $1}'
ps -ef | awk 'BEGIN{ print "begin..."} END{print "end..."} $8=="-bash"{print $2}'
ps -ef | grep redis
1.2 用户名
1.useradd 用户名 : 创建用户
2.passwd 用户名 密码 : 重置密码
3.groupadd 组名: 创建组
4.useradd -g 组名 用户名: 创建用户并添加到指定组中
5.usermod -g 组名 用户名;
1.3 修改文件的权限
chmod 数字 文件
1.4 防火墙的命令
systemctl status firewalld 查看防火墙的状态
systemctl stop firewalld 关闭防火墙
systemctl start firewalld 开启防火墙
systemctl disabled firewalld 永久关闭
1.5 添加端口号 在防火墙中
firewall-cmd --zone=public --add-port=3306/tcp --permanent (permanent 永久)
1.6 压缩文件
tar -zxvf 压缩文件 解压文件到当前目录
1.7 查看进程
ps -ef | grep 进程名
2.NOSQL的介绍
NOSQL是对非关系型数据库的概况,NOSQL翻译(NOT ONLY SQL 不仅仅是sql).适合大数据的存储。
2.1 NOSQL 和RDBMS(关系型数据库管理)区别
.关系型数据库RDBMS:
1.结构化数据库
2.结构化查询语句,定义语句。
3. 数据和关系都存储在表中
. 4 支持事务管理 ACID
**NOSQL:**
1.没有结构化查询语句
2.存储数据 key-value.面向列存储(HBASE),面向文档存储(Mogondb)
3.极易扩展。
2.2 NOSQL产品有哪些
redis :
mogondb
memcache
hbase:
*1,Memcached*
挥发性(临时性)的键值存储
一般作为关系型数据库的缓存来使用
具有非常快的处理速度
由于存在数据丢失的可能,所以一般用来处理不需要持久保存的数据
用于需要使用expires时(需要定期清除数据)
使用一致性散列(Consistent Hashing)算法来分散数据
*2********,Redis*
兼具Memcached和Tokyo Tyrant优势的键值存储
擅长处理数组类型的数据
具有非常快的处理速度
可以高速处理时间序列的数据,易于处理集合运算
拥有很多可以进行原子操作的方法
使用一致性散列(Consistent Hashing)算法来分散数据
*3********,MongoDB*
面向无需定义表结构的文档数据
具有非常快的处理速度
通过BSON的形式可以保存和查询任何类型的数据
无法进行JOIN处理,但是可以通过嵌入(embed)来实现同样的功能
使用sharding(范围分割)算法来分散数据
3、redis简介
redis是nosql中使用最多的非关系型数据库,使用c语言编写,低层存储以key-value键值对存储。
特点:
- 读写速度快。 读110000/s 写81000/s
- 以key-value键值对存储。
- 存储的数据类型丰富。存储string,list(队列),set(集合),hash(哈希),zSet(有序集合)。(redis支持哪些数据类型。)
- 易于集群搭建。
- 可以在内存中运行,并且可以存储到磁盘上
3.1 Redis的安装。
https://redis.io/download 该网址可以下载redis
3.2 安装C语言的环境(**安装redis前确认联网)
yum -y install gcc-c++ 网络安装gcc-c++ install 安装 -y 一直同意
3.2 上传redis安装文件 上传到 /usr/local/
使用xshell
3.3 在该目录解压该文件
tar -zxvf redis-5.0.8.tar.gz
3.4 进入解压后的目录 源码
cd /usr/local/redis-5.0.8
3.5 编译该文件
make
3.6 安装编译后的文件
make install
3.7 启动redis
redis-server 启动redis服务的。
redis-server /目录/redis.conf
客户端连接redis服务
redis-cli 默认连接的是本地的redis服务 并且端口号为6379
redis-cli -h(ip) -p(端口号)
redis-cli -h 127.0.0.1 -p 6379
客户发送一个指令ping 服务器响应一个pong的结果
启动redis后台运行,修改redis.conf第136行 设置成yes
vim redis.conf
136 daemonize yes
然后重启服务
[root@master redis-5.0.8]# redis-server redis.conf
9864:C 17 Aug 2020 18:48:15.948 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
9864:C 17 Aug 2020 18:48:15.948 # Redis version=5.0.8, bits=64, commit=00000000, modified=0, pid=9864, just started
9864:C 17 Aug 2020 18:48:15.948 # Configuration loaded
[root@master redis-5.0.8]# ps -A | grep redis
9865 ? 00:00:00 redis-server
[root@master redis-5.0.8]# redis-cli
127.0.0.1:6379> ping
PONG
4、Redis 常见命令
set key value :往redis中存放字符串数据。
get key: 获取指定key的value值
keys * : 查询所有的key
del key: 删除指定的key
exists key: 判断key是否存在 存在返回1 不存在返回0
expire key : 为指定的key设置时间。
ttl key :查看指定的key的剩余时间
flushdb: 清空该数据库中的所有数据。 <测试用>
5、使用redis-plus连接redis
关闭防火墙
systemctl status firewalld
将redis.conf配置文件中bind 127.0.0.1修改成0.0.0.0
vim /usr/local/redis-5.0.8/redis.conf
重启服务
redis-server /usr/local/redis-5.0.8/redis.conf
启动redisplus连接vmvare中redis
一种使用ip,一种使用ssh
如果连接失败,注意:
1.redis服务没有开启
2.Redis端口号被防火墙拦截
6、 java连接redis
6.1 java连接redis
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.1.0</version>
</dependency>
//创建一个Jedis
Jedis jedis = new Jedis("192.168.91.100", 6379);//操作的哪个redis
String ping = jedis.ping();
System.out.println(ping); //pong
jedis.set("k1", "v1");
jedis.setnx("num", "1");
System.out.println(jedis.incr("num"));
jedis.hset("hset1", "name", "张三");
System.out.println(jedis.get("k1"));
jedis.close();
6.2 spring连接redis
<!-- jar包版本声明 -->
<properties>
<spring.version>4.3.24.RELEASE</spring.version>
<jedis.version>3.1.0</jedis.version>
</properties>
<!-- 声明需要的依赖的具体的资源 -->
<dependencies>
<!-- 导入spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--jedis的jar包-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>
</dependencies>
spring主配置文件中加入bean
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="20"/>
<property name="maxIdle" value="10"/>
<property name="maxWaitMillis" value="10000"/>
</bean>
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg name="poolConfig" ref="poolConfig"></constructor-arg>
<constructor-arg name="host" value="192.168.91.100"/>
<constructor-arg name="port" value="6379"/>
</bean>
测试
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-redis.xml");
JedisPool jedisPool = applicationContext.getBean(JedisPool.class);
Jedis jedis = jedisPool.getResource();
Map<String, String> hashmap = new HashMap<String, String>();
hashmap.put("name", "zs");
hashmap.put("age", "18");
jedis.hset("user", hashmap);
Map<String, String> user = jedis.hgetAll("user");
System.out.println(user);
jedis.close();
spring使用redis作为缓存
@Autowired
private JedisPool jedisPool;
public List<Department> getDepts() {
Jedis jedis = jedisPool.getResource();
String deptAll = jedis.get("deptAll");
if(deptAll==null){
System.out.println("缓存中不存在该数据,需要查询数据库");
List<Department> list = departmentMapper.selectByExample(null);
//放进redis缓存
jedis.set("deptAll",JSON.toJSONString(list));//,JSON.toJSONString(list) @ResponseBody
return list;
}else{
System.out.println("缓存中存在该数据");
//JSON.parseArray(deptAll,Department.class); 把json字符串转化为java对象。等价于controller @RequestBody
return JSON.parseArray(deptAll,Department.class);
}
}
7.3 springboot连接redis
spring:
redis:
jedis:
pool:
max-active: 20
min-idle: 10
max-wait: 10000
host: 192.168.91.100
port: 6379
@Test
void contextLoads() {
//操作字符串类型 需要获取该字符串的对象stringRedisTemplate.opsForValue();
ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
/* opsForValue.set("k1","v1");
opsForValue.set("k2","v2");
System.out.println(opsForValue.get("k1"));*/
HashOperations<String, Object, Object> forHash = stringRedisTemplate.opsForHash();
forHash.put("h1", "name", "张三");
System.out.println(forHash.get("h1", "name"));
Map<String, String> map = new HashMap<>();
map.put("age", "15");
map.put("name", "简素言");
map.put("addr", "郑州");
forHash.putAll("h2", map);
Collection list = new ArrayList<>();
list.add("name");
list.add("age");
list.add("addr");
System.out.println(forHash.multiGet("h2", list));
}