vi /etc/ssh/sshd_config
把端口和两个连接地址的注释去掉,还有允许root远程登录以及密码验证注释去掉
开启服务 这玩意和Ubuntu不一样 开启的是sshd
service sshd start
查看当前服务列表
systemctl list-units --type=service
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
#备份一下原来的文件
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
#更换配置文件
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
#添加epel
#清空原来的缓存
yum clean all
#生成新缓存
yum makecache
yum install gcc
wget http://download.redis.io/releases/redis-5.0.5.tar.gz
mkdir /usr/local/redis
tar -zxvf redis-5.0.5.tar.gz -C /usr/local/redis
cd /usr/local/redis/redis-5.0.5
make
cd src
make install
回到上级目录
进入redis.conf修改如下
注释掉 bind 127.0.0.1 这一行(解决只能特定网段连接的限制)
将 protected-mode 属性改为 no (关闭保护模式,不然会阻止远程访问)
将 daemonize 属性改为 yes (这样启动时就在后台启动)
设置密码
添加 requirepass 123456即可 当然这步完全看心情
firewall-cmd --zone=public --add-port=6379/tcp --permanent
firewall-cmd --reload
redis-server /usr/local/redis/redis-5.0.5/redis.conf
redis-cli
auth 密码
shutdown
exit
重启redis服务
#直接kill掉服务重启即可
去etc下创建个redis目录
copy一份配置文件到这个目录
cp /usr/local/redis/redis-5.0.5/redis.conf /etc/redis/6379.conf
copy一份初始化脚本到本目录
cp /usr/local/redis/redis-5.0.5/utils/redis_init_script /etc/init.d/redisd
进入init.d设置一下开机自启
cd /etc/init.d
chkconfig redisd on
最后就可以使用service愉快的玩耍了
service redisd start
service redisd stop
Redis 是开源免费的,遵守BSD协议,是一个高性能的key-value非关系型数据库。
所谓的单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。
redis采用多路复用机制:即多个网络socket复用一个io线程,实际是单个线程通过记录跟踪每一个Sock(I/O流)的状态来同时管理多个I/O流.
Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
Redis不仅仅支持简单的key-value类型的数据,同时还提供String,list,set,zset,hash等数据结构的存储。
Redis支持数据的备份,即master-slave模式的数据备份。
性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
丰富的特性 – Redis还支持设置key有效期等等特性。
可以减轻数据库压力,查询内存比查询数据库效率高。
是单线程的,原子性的所以都是线程安全的
string 可以存字符串,数值和位图
set key名称 值 #一般来说 key名不超过1024 值不超过512mb
get key名称
append name ,world #将 ‘,world追加到name结尾’
strlen key名称 #字节长度
incr key名称 #将key+1
decr key名称 #将key-1
setbit key名称 偏移 1 #01000000 下标为1二进制位 (偏移为1)因为最高位是用来区分字符集的
二进制安全:存储东西时候没有类型的概念(序列化成字节数组 不会因为数据很大而产生问题)全部识别为字节 不会去转换字符集这就是二进制安全
ascii是基本字符集 其他是扩展字符集
其实就可以想象成hashmap
hset hwx name hwx #设置key名称为hwx 的name的value为hwx
hset hwx age 18 #同上
hgetall hwx 会把hwx这个key里边的内容全部取出
hkeys hwx #取出hwx key里边的所有key名
hvals hwx #取出hwx key里边的所有val值
hincrby hwx age 1 #给hwx里头的key为age的值+1
hget hwx age #查看key为hwx 的key为age的值
应用场景:
list的底层是双向链表
可以模拟出数据结构 同向就是栈 逆向就是队列 还有数组
lpush key名称 a b c d #abcd是value 从左往右push
rpush key名称 a b c d #从右往左push
lpop key名称 #从左弹栈
rpop key名称 #从右弹栈
lrange key名称 0 -1#查看数据 0 -1表示区间
ltrim key名称 0 -1#删除给定区间外的东西
应用场景
特点
sadd key名称 a b c d #向集合添加数据机是添加重复的也会自动去重
SMEMBERS key名称 #取集合所有东西
SRANDMEMBER key名称 count(多少个)#count有这么几种情况 是正数 返回不重复的数据 负数 返回可能有重复的
SPOP key名称 count #取出不放回
SINTER k1 k2 #取k1 k2 的交集
SUNION K1 K2 #取k1 k2 的并集
SDIFF k1 k2 #取k1 k2的左差集
SDIFF K1 K2 #取k1 k2的右差集
应用场景
特点
## 创建集合时候必须带分值
ZADD k1 2 a 3 b 1 c 6 d 5 e# k1为key的名称 2 为a的权重
ZRANGE k1 0 -1 withscores #查看排序好的数据
ZRANGE k1 0 2 #取从小到大的的前三名
ZREVRANGE k1 0 2 #取从大到小的前三名
ZINCRBY k1 4 a#可以改变权重值 为a的权重值增加4 而且可以做到动态排序
应用场景
SortedSet排序 skiplist(跳跃表)
redis变成阻塞状态,对外无法提供服务
然后去写当前时间段的东西
redis比如在8点要去使用rdb的时候去持久化数据
save <seconds> <changes>
Redis默认配置文件中提供了三个条件:
save 900 1
save 300 10
save 60 10000
分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改
指定本地数据库文件名,默认值为dump.rdb
dbfilename dump.rdb
12. 指定本地数据库存放目录
dir ./
[root@x1a0g x1a0g]# num=2
[root@x1a0g x1a0g]# echo $num
2
[root@x1a0g x1a0g]# ((num++))
[root@x1a0g x1a0g]# echo $num
3
[root@x1a0g x1a0g]# ((num++)) | echo ok
ok
[root@x1a0g x1a0g]# echo $num
3
[root@x1a0g x1a0g]# ((num++)) | echo $num
3
[root@x1a0g x1a0g]# echo $BAHSPID
[root@x1a0g x1a0g]# echo $BASHPID
77121
[root@x1a0g x1a0g]# echo $BASHPID | more
77351
[root@x1a0g x1a0g]# echo $BASHPID | echo $BASHPID
77362
[root@x1a0g x1a0g]# echo $BASHPID | more
77363
[root@x1a0g x1a0g]#
进程创建时候有两个成本:1.内存大小 2. 速度
fork会在内存中创建出一个子进程(并不会把所有数据来拷贝,仅仅是指针的指向)
appendfsync everysec 表示每秒同步一次(折中,默认值)
appendfsync no 表示等操作系统进行数据缓存同步到磁盘(快)
appendfsync always 表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)
指定是否在每次更新操作后进行日志记录,默认为no
appendonly no #关闭aof
指定更新日志文件名,默认为appendonly.aof
appendfilename appendonly.aof
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
因为5.0是默认开启aof与rdb混合,可以先关掉
redis-server ./redis.conf 这个配置文件是/usr/local/redis/redis-5.0.5下的
使用事前记得把aof日志清一下 路径 /var/redis/
执行bgrewrite之后 aof中写入了rdb
这些东西就是增量就是aof与rdb的混合形式[
数据一致性问题如何解决?
最终一致性
kafka或者其他东西,必须保证他是可靠的,响应速度快的,要保证可靠,他也必须是一个集群(不能出现单点故障)
我们 一变多 就是为了解决***单点故障*** 或者说解决可用性的问题
最终一致性也有可能出现问题 ,加入后头那俩redis还没消费数据,可能会导致数据不一致的问题
假如redis主机挂掉,那么是什么来判断这个主机是挂的,来看看这个图
既然要来作为监控,那么这个监控一定是高可用的,也就是说是一定是集群的,如上图
其实可以把这个图反转一下,也是一样的
那么随之而来的就是第一个问题,假如我有三台监控,那么如何判定我的redis是挂的
假如3台都说挂才是挂,那么就又会是强一致性,出现可用性问题
假如1台说我挂了,但是可能由于其他问题其他两台看的没挂,就会出现统计不准确,或者说势力范围不够。会造成的现象是出现 网络分区 也可以说出现精神分裂(脑裂),就是说我同一个服务,用户一个数据进来返回不一样的结果
但是网络分区是可以容忍的(分区容忍性)《注册发现例子》,不是很要求数据强一致性
假如2台说我挂了,会进行一个投票,说挂的会结成势力范围,在当前场景下说挂的两台的势力范围是2,那么剩下的一台势力范围是1,1的就没有决策权,那么当前的就只会有两种状态,挂或者不挂,不会出现中间态
最后解决网络分区问题,监控判断挂不挂的条件是2n+1台,通俗来说 过半
一般来说 集群的数量都是奇数台
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾 (比如强一致性与可用性不能同时满足)
本机启动配置如下
redis-6379 service redisd start
redis-6380 路径:/usr/local/redis-6381是端口为6380的配置文件名字起错了
执行 redis-server ./redis.conf
redis-6381 路径 /usr/local/redis-5.0.5
执行 redis-server ./redis.conf
如何修改?
服务器配置,要前台阻塞,看实时效果
然后这次测试的三台机器全在 /home/x1a0g/test 目录下,演示之前记得清一下rdb文件
如果主机设置了密码,必须在从机中添加一条 masterauth 123456
首先是主从复制
让 6380 和 6381 两台机器追随 6379 即 6379为主,6381、6381为从
命令如下:
6380从机执行:
REPLICAOF 127.0.0.1 6379
这种方式常用 :redis-server ./6380.conf --replicaof 127.0.0.1 6379
6381从机执行
REPLICAOF 127.0.0.1 6379
可以看到 当从机第一次挂到主机上时候,一定会先把自己的老数据删除掉,之后会落一个rdb到从机,从而保证数据同步,
在这时候,如果从机突然挂了,再次连回来的时候,就不会再去落全量的rdb,只会去把它挂掉这一段时间的增量给他
flushing 这个操作就是删除老数据
这个截图是主机 的 可以看到6380先lost 就是挂了 下头那句是6380又通了 主机发了一个offset过去,就是做的增量同步 并没有去落rdb
加入 我 这么启动 redis-server ./6380.conf --appendonly yes 就是开启aof 会发现我又开始落rdb了 ,这是因为有aof的时候,会以重新把rdb写到aof
如果主从做好之后,从机默认无法写,这个改一下配置文件就ok
主机身上 可以知道有多少个从机,加入从挂了 我们在没有哨兵的情况下:
手动切换方式
6379挂了 ,假如我想把6380变成主 然后6381追随6380,操作如下
6380执行:REPLICAOF no one
6381执行: REPLICAOF 127.0.0.1 6380
当6379在跑起来的时候让他去追随6380即可:REPLICAOF 127.0.0.1 6380
然后就完成了
在什么时候发生的?作为缓存的时候(一定发生了高并发的情况)
key过期或者被清理掉了,造成并发访问数据库
并发有了,阻止并发到达db,redis中又没有key
假如有5000的并发
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.2.0version>
dependency>
@Test
public void test03(){
Jedis jedis = new Jedis("192.168.70.131",6379);
jedis.auth("123456");
String k1 = jedis.get("key");
System.out.println(k1);
}
package com.dysc.utils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class RedisUtil {
//Redis服务器IP
private static String ADDR = "192.168.70.131";
//Redis的端口号
private static int PORT = 6379;
//redis的密码
private static String PASS = "123456";
//可用连接实例的最大数目,默认值为8;
//如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
private static int MAX_ACTIVE = 1024;
//控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
private static int MAX_IDLE = 200;
//等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
private static int MAX_WAIT = 10000;
//在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
private static boolean TEST_ON_BORROW = true;
private static JedisPool jedisPool = null;
/**
* 初始化Redis连接池
*/
static {
try {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(MAX_ACTIVE);
config.setMaxIdle(MAX_IDLE);
config.setMaxWaitMillis(MAX_WAIT);
config.setTestOnBorrow(TEST_ON_BORROW);
jedisPool = new JedisPool(config, ADDR, PORT,10000,PASS);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取Jedis实例
* @return
*/
public synchronized static Jedis getJedis() {
try {
if (jedisPool != null) {
Jedis resource = jedisPool.getResource();
return resource;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 释放jedis资源
* @param jedis
*/
public static void returnResource(final Jedis jedis) {
if (jedis != null) {
jedisPool.close();
}
}
}
方法 | 解释 |
---|---|
new Jedis(host, port) | 创建jedis对象,参数host是redis服务器地址,参数port是redis服务端口 |
set(key,value) | 设置字符串类型的数据 |
get(key) | 获得字符串类型的数据 |
hset(key,field,value) | 设置哈希类型的数据 |
hget(key,field) | 获得哈希类型的数据 |
lpush(key,values) | 设置列表类型的数据 |
lpop(key) | 列表左面弹栈 |
rpop(key) | 列表右面弹栈 |
del(key) | 删除指定的key |
有时我们会在短时间内发送大量互不依赖的命令(例如:后执行的命令不需要使用前面返回的结果)。由于网络传输不可避免的会造成一定的延迟,特别是在跨机器远程访问redis的时候,如果使用常规的方式,一条命令对应一次请求和响应的话,大量命令累计的延迟会显得很高。redis的设计者考虑到这一点,在底层的通信协议上,通过支持"管道(pipeline)"来解决这一问题。
Redis Pipelining
: 客户端可以向服务器发送多个请求而无需等待回复, 最后只需一步即可读取回复.
RTT(Round Trip Time)
: 往返时间.
Pipeline 操作:request request request — — — — — response response response
单次操作:request —response request —response request —response
jdeis测试
@Test
public void test04(){
Jedis jedis = RedisUtil.getJedis();
Pipeline pipelined = jedis.pipelined();
long start = System.currentTimeMillis();
pipelined.set("tes","1");
for (int i = 0; i < 10000 ; i++) {
pipelined.incr("tes");
}
pipelined.sync();
long end = System.currentTimeMillis();
System.out.println("tes为:"+jedis.get("tes"));
System.out.println("管道技术的时间"+(end-start));
long start2 = System.currentTimeMillis();
jedis.set("tes2","1");
for (int i = 0; i < 10000 ; i++) {
jedis.incr("tes2");
}
long end2 = System.currentTimeMillis();
System.out.println("tes2为:"+jedis.get("tes2"));
System.out.println("普通时间"+(end2-start2));
RedisUtil.returnResource(jedis);
}
结果
tes为:10001
管道技术的时间43
tes2为:10001
普通时间4270
Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令
一个事务从开始到执行会经历以下三个阶段:
批量操作在发送 EXEC 命令前被放入队列缓存,并不会被实际执行,也就不存在事务内的查询要看到事务里的更新,事务外查询不能看到。
Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
redis 127.0.0.1:7000> multi
OK
redis 127.0.0.1:7000> set a aaa
QUEUED
redis 127.0.0.1:7000> set b bbb
QUEUED
redis 127.0.0.1:7000> set c ccc
QUEUED
redis 127.0.0.1:7000> exec
1) OK
2) OK
3) OK
当用户高频访问key1的时候,key1突然失效,然后就会出现用户大量访问key1的请求全部打到db
如果说缓存雪崩是缓存这个桶彻底没了,那么缓存击穿就是桶上破了一个洞把
解决办法
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求
比如用户一直去请求 id=-1 每一次都可以绕开缓存直接请求数据库 ,然后并发到达一定高度数据库也就挂了
解决办法:
大面积缓存失效,从而一下子打崩db
就电商来说吗,大多数热点key是采用定时任务的方式来进行刷新,或者是查不到之后去更新的
同一时间大面积失效,那一瞬间Redis跟没有一样,那这个数量级别的请求直接打到数据库几乎是灾难性的,你想想如果打挂的是一个用户服务的库,那其他依赖他的库所有的接口几乎都会报错,如果没做熔断等策略基本上就是瞬间挂一片的节奏,你怎么重启用户都会把你打挂,等你能重启的时候,用户早就睡觉去了,并且对你的产品失去了信心,什么垃圾产品。
处理缓存雪崩简单,在批量往Redis
存数据的时候,把每个Key的失效时间都加个随机值就好了,这样可以保证数据不会在同一时间大面积失效,我相信,Redis这点流量还是顶得住的。
setRedis(Key,value,time + Math.random() * 10000);
原文链接:https://blog.csdn.net/qq_35190492/article/details/102889333
当用户数据发生修改的时候,mysql中的数据首先需要更新,之后删除redis中的记录,然后当修改完成之后再去修改redis中的记录
分布式锁是控制分布式系统或不同系统之间共同访问共享资源的一种锁实现,如果不同的系统或同一个系统的不同主机之间共享了某个资源时,往往需要互斥来防止彼此干扰来保证一致性。
setnx(SET if Not eXists)
为了保持数据的一致性,经常碰到需要对资源加锁的情形。 利用redis来实现分布式锁就是其中的一种实现方案。
setnx key value
将 key 的值设为 value ,当且仅当 key 不存在。
若给定的 key 已经存在,则 SETNX 不做任何动作。
设置成功,返回 1 。
设置失败,返回 0 。
我们使用上面这个命令配合删除key的命令可以做到如下效果
redis> SETNX lock true # 获得锁成功设置key
(integer) 1
进行的操作
redis> DEL lock # 释放锁就是删除key
(integer) 1
这样的话存在问题 :在del之前报错就没办法释放锁
解决办法:
redis> SETNX lock true # 获得锁成功
(integer) 1
redis> EXPIRE lock 5 # 设置5秒的过期时间
(integer) 1
进行的操作
redis> DEL lock # 释放锁
(integer) 1
还有个问题:如果在SETNX和EXPIRE之间程序又发生了错误,当前锁又无法释放。所以说到底还是需要一个原子的操作,在获得锁的同时能够同时设置锁的过期时间。
注意在reids2.8之后set命令已经更为全面,在这只是作为原理探究
在redis2.8之后set指令的参数更加全面 可以这么使用
在SET命令中,有很多选项可用来修改命令的行为。 以下是SET命令可用选项的基本语法。
redis 127.0.0.1:6379> SET KEY VALUE [EX seconds] [PX milliseconds] [NX|XX]
(1)设置指定的到期时间(以秒为单位)。
ShellEX seconds −
(2)设置指定的到期时间(以毫秒为单位)
PX milliseconds
(3)仅在键不存在时设置键
NX
(4)只有在键已存在时才设置
XX
示例
redis 127.0.0.1:6379> SET lock “requestid” EX 60 NX
OK
#意思是 ex表示过期时间为60秒 在lock不存在时才进行设置
将 key 的值设为 value ,当且仅当 key 不存在。
若给定的 key 已经存在,则 SETNX 不做任何动作。
设置成功,返回 1 。
设置失败,返回 0 。
我们使用上面这个命令配合删除key的命令可以做到如下效果
redis> SETNX lock true # 获得锁成功设置key
(integer) 1
进行的操作
redis> DEL lock # 释放锁就是删除key
(integer) 1
这样的话存在问题 :在del之前报错就没办法释放锁
解决办法:
redis> SETNX lock true # 获得锁成功
(integer) 1
redis> EXPIRE lock 5 # 设置5秒的过期时间
(integer) 1
进行的操作
redis> DEL lock # 释放锁
(integer) 1
还有个问题:如果在SETNX和EXPIRE之间程序又发生了错误,当前锁又无法释放。所以说到底还是需要一个原子的操作,在获得锁的同时能够同时设置锁的过期时间。
注意在reids2.8之后set命令已经更为全面,在这只是作为原理探究
在redis2.8之后set指令的参数更加全面 可以这么使用
在SET命令中,有很多选项可用来修改命令的行为。 以下是SET命令可用选项的基本语法。
redis 127.0.0.1:6379> SET KEY VALUE [EX seconds] [PX milliseconds] [NX|XX]
(1)设置指定的到期时间(以秒为单位)。
ShellEX seconds −
(2)设置指定的到期时间(以毫秒为单位)
PX milliseconds
(3)仅在键不存在时设置键
NX
(4)只有在键已存在时才设置
XX
示例
redis 127.0.0.1:6379> SET lock “requestid” EX 60 NX
OK
#意思是 ex表示过期时间为60秒 在lock不存在时才进行设置