redis学习
redis学习
去官网上面去下载,然后解压之后就可以直接使用了
redis.window.conf :配置文件
redis-cli.exe:redis的客户端
redis-server.exe:redis服务器端
然后简单的使用:以管理员的身份运行服务器端和客户端
然后服务器端会开放一个端口,客户端也会开放一个端口,这里的端口是6379
然后在客户端可以使用我们的数据库进行存储数据和读取数据:
redis的数据结构:redis存储的key value的数据,其中key都是字符串,value有五种不同的数据结构
字符串类型 string
哈希类型 hash map
列表类型 list: linkedList格式
集合类型set:hashset
有序集合类型 sortedset
string命令操作:get set del
哈希类型操作:hset key field value hget key field hdel key field hgetall获取全部
列表命令操作:list可以添加元素在列表首部或者尾部
左添加:lpush key value
右边添加:rpush key value
范围获取:lrange key start end
删除:lpop key:删除列表最左侧元素,并将其返回
rpop:删除最右侧元素,并返回
set操作:sadd key value 可以存储多个
smembers key
srem key value
sortedset有序集合:不允许重复元素,且元素有顺序
存储:zadd key score value
获取: zrange key start end
删除:zrem key value
有序集合可以做一个实时的动态排行榜,这样就比较方便
通用命令
keys * :去查询所有的键,也可以用正则表达式
type:查看一个键是什么类型的
del key:删除指定的key value
redis的持久化:
1.redis是一个内存数据库,当redis服务器重启,或电脑重启,数据会丢失,可以将redis中的数据持久化保存到
硬盘的文件中
save 900 1
save 300 10
save 60 10000
这些参数就是redis的配置文件里面的配置信息,代表持久化的参数,我们如果符合这些条件就会将信息存储到一个rdb文件中,下次我们再次启动redis服务器还是可以看到我们上次的信息,
2.redis持久化机制:
RDB:默认方式,不需要进行配置,默认使用这种配置
在一定的间隔时间中,检测key的变化清空,然后持久化数据,
AOF:日志记录的方式,可以记录每一条命令的操作,可以每一次命令操作后来持久化数据
不推荐使用
如果我们想要使用持久化的化,那就需要使用conf的配置文件,但是如果我们使用conf配置文件启动的话,就不可避免的需要使用管理员全选,而默认我们又是没有管理员权限的,所以这个时候就会出现许多的问题,也就是我们执行redis-server.exe redis.windows.conf的时候会报出permission denied的错误,这里有解决的办法:
就是把我们的cmd命令窗口的执行权限往上提一提,我们变成管理员就有权限来执行了
https://www.cnblogs.com/chenmingjun/p/10323441.html
上面为解决方案的网址,这样我们就可以看到我们redis数据库里面保存的信息了
下面是使用AOF的操作,默认是关闭的,需要在配置文件里面开启:appendonly yes
appendfsync always:每一次操作都进行持久化
appendfsync everysec:每一秒都进行持久化
appendfsync no:不进行持久化
JAVA使用redis :jedis
下载jar包,jar包直接使用视频资源里面给出的就可以了,要按照上面的提示自己创建项目,视频给出的项目不能直接运行不知道为什么,然后操作就比较简单
使用junit直接进行测试就可以了:
@Testpublic void test1(){
//1.获取连接
Jedis jedis = new Jedis("localhost",6379);
//2.操作
jedis.set("username","zhangsan");
//3.关闭连接
jedis.close();
}
jedis操作redis中各种的数据结构
具体请看代码,
jedis连接池: JedisPool
使用:
创建Jedi是Pool连接池对象
调用方法getResource()方法获取Jedis连接
由于我们每次创建jedis连接池对象的时候都需要写重复的代码,所以我们可以把这些东西抽取出来成为一个工具类,直接getRedis就可以获取到我们的代码,
具体的操作有些神奇
public class JedisPoolUtils {
private static JedisPool jedisPool;
static {
//类加载的时候就读取配置文件
InputStream is = JedisPoolUtils.class.getClassLoader().getResourceAsStream("jedis.properties");
//创建Properties对象
Properties properties = new Properties();
//关联文件
try{
properties.load(is);
}catch (IOException e){
e.printStackTrace();
}
//获取数据,设置到JedisPoolConfig中
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(Integer.parseInt(properties.getProperty("maxTotal")));
config.setMaxIdle(Integer.parseInt(properties.getProperty("maxIdle")));
//初始化JedisPool
jedisPool = new JedisPool(config,properties.getProperty("host"), Integer.parseInt(properties.getProperty("port")));
}
/*
* 获取连接方法
*/
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
如何在idea里面使用tomcat
首先去官网里面去下载tomcat,就下载8吧,然后解压就可以直接使用,这个时候tomcat就可以直接使用的,
去tomcat目录/bin的startup就可以直接启动,但是会出现乱码,这个时候需要对conf/logging.properties做出修改,把里面的encoding=utf-8全部替换为encoding=GBK编码,这个时候控制台打印的东西就是中文了,
我们写好了一个上面创建的web项目的话,在原始的方法里是需要将其打成一个war包,然后就可以往tomcat里面添加了,这里是点击file 然后project Structure配置,接下来按照图中的配置去
然后在Name上给项目取一个名字,在右侧如下图进行点击
这里点击编辑
这里选择我们tomcat存放的位置
然后点击加号将我们打成的war包添加进来
这样就可以运行成功了
具体的请看代码
redis的实际使用:使用redis是经常缓存一些不常变化的数据,
如果数据库发生了变化,那么就需要立即更新缓存,
需要将redis缓存数据的情况进行更新,更新的操作就是再次存入,
在service对应的增删改方法中,将redis数据进行删除,这样我们下次想要使用缓存的时候就会发现没有了,然后就再次从数据库里面进行查询,查询完之后就存储进去,这样就完成了更新,
传统的关系型数据库,
CAP和BASE
传统的ACID:
atomicity原子性
Consistency一致性
Isolation独立性
Durability持久性
CAP:
Consistency强一致性
Availability可用性
Partition tollerance分区容错性
CAP三个只能选择两个,一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个
因此,根据CAP原理将NoSQL数据库分成了满足CA原则、满足CP原则和满足AP原则三大类
CA:单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大,
CP:满足一致性,分区容忍的系统,通常性能不是特别高
AP:满足可用性,分区容忍的系统,通常可能对一致性要求低一些,
我觉得要是想要同时满足这三个数据库,那得是量子数据库才行,
CA:传统Oracle数据库,有时候用不了,这也是没有办法的事情
AP:大多数网站架构的选择,在一些小型的细节数据上面可以不太一致,但是一定要被大家看到,
CP:Redis、Mongodb,
BASE
基本可用(Basically Available)
软状态(Soft state)
最终一致(Eventually consistent)
BASE就是为了解决关系数据库强一致性引起的问题而引起的可用性降低而提出的解决方案
它的思想是通过让系统放松对某一时刻数据一致性的要求来换取系统整体伸缩性和性能上的改观,为什么这么说呢,原有就在于大型系统往往由于地域分布和极高性能的要求,不可能采用分布式事务来完成这些指标,要想获取这些指标,我们必须采取另外一种方式来完成,
Redis Remote dictionary Server(远程调用服务器)
Redis在Linux下面的安装
首先下载redis的安装包,然后tar-zxvf进行解压,
解压完成之后进行目录,执行make进行安装,但是有的时候可能并没有gcc,所以make会执行错误
gcc是linux下的一个编译程序,是c程序的编译工具,
安装gcc步骤:
可以使用yum进行安装,但是因为无法上外网,所以也不好上
一个一个安装,上网上应该都可以搜到,然后再make,
然后修改conf配置文件,然后启动redis就可以看到
redis装了16个库,默认就是16个库 0-15
select命令可以切换数据库
flush 和flushall一个是删除当前库,一个是删除所有的16个库的内容
redis五大数据类型
键 key
字符串 String
列表 List
集合 Set
哈希 Hash
有序集合 Zset(sorted set)
String类型是redis最基本的类型,与Memcached一样的类型,一个key对应一个value
String 类型是二进制安全的,意思是redis的string可以包含任何数据,比如jpg图片或者序列化对象
一个redis中字符串value最多可以是512M
Hash:类似java中的map,是一个string类型的field和value的映射表,hash特别适合用于存储对象
List:列表是简单的字符串列表,按照插入顺序排序,你可以添加一个元素到列表的头部或者尾部,它的底层实际是个链表,
Set:是string类型的无序集合,它是通过HashTable实现的
Zset:这个和set一样也是string类型元素的集合,且不允许重复的成员。redis正是通过分数来为集合中的成员进行从小到大的排序,zset的成员是唯一的,但分数却可以重复,
关于key的用法:
keys * 查看所有的key
exists k1 判断是否有k1这个键
move k3 2 将k3移动到二号库
ttl (time to leave) k1 表示还能活多久,-1表示永不过期
expire k3 10 给k3一个过期时间,10秒钟
如果一个键过期了,那么通过get 和keys* 都不会再访问到它,它已经被移出内存系统
type k1 查看一个key的类型,
lpush mylist 1 2 3 4 5 向list里面放东西,
lrange mylist 0 -1查看list里面的元素
del mylist 从缓存中删除
append k1 12345 可以把k1所对应的值追加一些东西,
strlen 查看k1字符串的长度
incr 可以给key值增加数值,默认是1,也可以增加多一些
Decr可以减少值,
incrby k2 3 一下子给k2增加3,多路递增
descby k2 2 一次给
注意: 如果给一个字符串去加,显然不是特别的合适
GETRANGE k1 获取字符串的数据,
getrange k1 0 -1 全部获取字符串,
getrange k1 0 3 获取k1的前四个字符
setrange k1 0 xxx 在k1的第0个位置开始设置xxx放进去,
setex k4 10 v4 给k4放一个只存在10秒钟的v4
setnx k4 v4 如果没有设置就设置一个过期时间,
mset k1 v1 k2 v2 k3 v3 这样可以添加多个键值对,
mget k1 k2 k3 可以一下子获取到多个键值对,
单独的set k4 v4 k5 v5 是无法成功的,
msetnx k4 v4 k5 v5 可以直接放
list 列表
lpush list01 1 2 3 4 5 往list01里面放好多东西, 从左边往集合里面放东西,这就像一个栈一样,
lrange list 0 -1 5 4 3 2 1 从左边进去,正着进去反着出来,
rpush list02 1 2 3 4 5 从右边进去,相当于一个链表或者数组的结构
lrange list02 0 -1 怎么进去怎么出来,
lpop 和rpop就是相当于从左边弹出还是从右边弹出,
lindex list01 1 查看list01中的1这个元素在第几个位置
llen list01 查看list01这个元素的
lrem list01 2 3 删除list01里面的两个3,
ltrim list01 0 4 相当于给list01减少一下去掉0-4
rpoplpush list01 list02 把 list01右边弹出来,放到list02的左边,然后list02的元素多了一个,list01少了一个
linsert list01 before x java 相当于在list01这个集合的x元素之前插入一个java
linsert list01 after x oracle 相当于在list01 这个列表中的x之后插入oracle
Set
set其实和list差不多,不过就是不能重复,
sadd set01 1 1 2 2 3 3 这样只会往set01里面放入1 2 3
smemebers set01 直接查看set01里面的所有元素,
sismember set01 1 查看set01里面有无1这个元素,如果返回1,说明
srem set01 3 把set01中的3这个元素删除掉
srandmember set 3 从set里面任意取出三个元素
spop set01 将set01这个集合中的数据弹出去
smove set01 set02 5 把set01中的5移走到set02中
数学集合类,
交集 sinter set01 set02
差集, sdiff set01 set02 在set01里面,但是不再set02里面的元素
补集
并集 sunion set01 set02
全集
Hash
kv模式不变,但是value是一个键值对,
hset user id 11
hget user id 可以取出值
hmset customer id 11 name li4 age 26 这个意思就是放入的这个对象的多个值,
hmget customer id name age 可以获取到customer的这些值,
hgetall customer相当于把customer中的所有的键值对都获取到
hdel user name 将user的name删除
hlen user 1 表示user里面有多少个键值对
hexist customer id 判断customer中也没有id 如果有就是1,没有就是0
hkeys customer 把customer所有的key打印出来,
hvals customer 把customer中所有的values打印出来
hincrby customer age 2 把customer的age增加2,
hincrbyfloat customer score 05 给customer的score增加0.5分
hsetnx customer age 26 如果不存在就往里面放,放进去了返回1,没有放进去返回0,
Zset
zadd zset01 60 v1 70 v2 80 v3 90 v4 100 v5 往sort set里面放置东西,
zrange zset01 0 -1 取出来zset里面的所有东西,
zrange zset01 0 -1 withscores 带着分数把所有的东西取出来,
zrangebyscore zset01 60 90 取出在这些分数范围里面的值,
zrangebyscore zset01 (60 (90 大于60小于90的分数
zrangebyscore zset01 60 90 limit 2 2 大于等于60小于等于90分的这些值中,从第二个开始取,取出两个,
zrem zset01 v5 把里面的v5删除了
zcard zset01 统计里面有几个数据
zcount zset01 60 80 统计zset01 中60分到80分的数据个数,
zrank zset01 v4 看一下v4是在第几位
zscore zset01 v4 查看zset01中v4的分数
zrevrank zset01 v4 就是相当于将zset01逆序反转时,v4所在的位置,
zrevrange zset 0 -1 逆序打印数据
zrevrangebyscore zset 90 60 就是将zset中从结束分数到开始分数打印出来
Redis配置文件介绍
大小写随意
include可以包含有其他的配置文件,
damonize yes
pidfile /var/run/redis.pid 进行编号的地方
tcp-backlog 这是一个连接队列,在一个高并发的环境下,需要一个高的backlog值来避免慢客户端连接的问题,
timeout 0 相当于空闲了多少秒只会会关闭这个连接,0相当于不关闭,一直连着
tcp-keepalive 单位为秒,集群情况下需要使用,看看集群中的其他服务器有没有或者,如果设置为0就不会进行检测,建议设置成60
loglevel notice 日志级别 debug verbose notice warning
开发阶段可以调成debug,开发完成了之后可以选择后两个
Syslog-enabled 是否把日志输出到syslog中
Syslog-ident 指定syslog里的日志标识,
Syslog-facility 指定日志输出设备,可以时USER 或者Local0 到 local7
Databases 16有这么多的数据库
save db on disk
SECURITY
dir ./ 指定本地数据库的存放目录
config get requirepass 获取当前配置的登录密码,如果没有设置那么默认就是空的
config set requirepass “123456” 就是相当于设置了一个密码,设置密码了之后要是想要再次操作redis就需要输入密码了
auth 密码 相当于输入了密码,然后就可以继续使用了
limits限制
Maxclients 10000 默认最大有多少人来连接
maxmemory 最大的内存
maxmemory policy 缓存的过期策略,
volatile-lru 最近最少使用 只对设置了过期时间的键
allkeys-lru 使用lru算法移出key
volatile-random 随机删除
volatile-ttl 有限时间内的先删除 最低有效时间的先删除
noeviction 永不过期 默认
Maxmemory-samples 设置样本数量,
Redis的持久化
rdb: Redis DataBase 在指定的时间间隔内将内存中的数据集快照写入磁盘,Snapshot快照,它恢复时是将快照文件直接读到内存里
Redis会单独创建fork一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件,整个过程中,主进程是不进行任何IO操作的,这确保了极高的性能,如果需要进行大规模数据的恢复,且对数据的完整性不是非常敏感,那么RDB比AOF方式更加的高效,RDB的缺点是最后一次持久化后的数据k可能丢失。
fork:fork的作用是复制一个与当前进程一样的进程,新进程的所有数据(变量,环境变量,程序计数器)数值都和原进程一直,但是是一个全新的进程,并作为原进程的子进程,
Rdb保存的是dump.rdb文件
默认在三种情况下,会进行保存,
save 900 1 在15分钟之内改变了一次
save 300 10 在5分钟内改变了10次
save 60 10000 在一分钟内改变了一万次
save “ ” 是禁用备份恢复的意思
当我们点击 shutdown的时候,redis会迅速斩断所有的连接,并快速生成dump.rdb文件, 这个不太好恢复,
因为dump.rdb里面什么都没有了,但是我们可以把原来备份的东西复制过来,然后修改名字为dump.rdb,这样下次再登录就可以看到原先的内容了,不过如果时间过久进行备份,可能会出现数据的丢失,
命令save或者bgsave : save时只管保存,其他不管,全部阻塞,
bgsave:Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求,可以通过lastsave命令获取最后一次成功执行快照的时间
stop-writes-on-bgsave-error yes
rdbcompression: 对于存储到磁盘中的快照,可以设置是否进行压缩存储,如果是的话,redis会采用LZF算法进行压缩,如果不想消耗CPU来进行压缩的话,可以设置为关闭此功能
rdbchecksum:在存储快照后,还可以让redis使用crc64算法来进行数据校验,这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能,
优势: 适合大规模的数据恢复,对数据完整性和一致性要求不高,
劣势:在一定间隔时间做一次备份,所有如果redis意外down掉的话,
AOF(append only File):以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
appendonly no 默认是关闭的, apendfilename 追加日志文件的保存名称,
appendonly.aof不但记录了写操作,在哪个库里面的操作也会有记录, 删除也会被记录下来,如果是不小心删除的,那可以在这个文件中删除掉,然后下次再重新启动redis的时候就可以通过日志文件将所有的信息全部恢复,
有时候可能会因为断电或是其他的原因,导致这个记录日志的文件会出现问题,好比一些乱码之类的,然后就不能成功的启动redis了,这个时候需要把这个日志文件给修正一下
redis-check-aof --fix appendonly.aof
AOF和RDB可以同时使用的,
Appendfsync: always:同步持久化,每次发生数据变更会被立即记录到磁盘 性能较差但是数据完整性比较好
Everysec : 出厂默认推荐,异步操作,每秒记录,如果一秒内宕机,有数据丢失,
No
No-appendfsync-on-rewrite: 重写时是否可以运用Appendfsync,用默认no即可,保证数据安全性,
Auto-aof-rewrite-min-size: 设置重写基准值,
Auto-aof-rewrite-percentage: 设置重写的基准值,
rewrite:
是什么? AOF采用文件追加的方式,文件越来越大为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集,可以使用命令
bgrewriteaof
重写原理: AOF文件持续增长而过大时,会fork出一条新进程来将文件重写,(也是先写临时文件最后再rename)遍历新进程的内存中数据,每条记录有一条的Set语句,重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,和快照有些类似
触发机制: Redis会记录上次重写时AOF的大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍,且文件大于64M才触发,
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
劣势:相同的数据集的数据而言 aof文件要远远大于rdb文件,恢复速度慢于rdb
aof运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同
对于AOF和RDB到底要使用哪个?
RDB持久化方式能够在指定的时间间隔对数据进行快照存储,
AOF记录对服务器的写操作,当服务器重启时会重新执行这些,每次写的追加到文件末尾,
Redis可以多AOF文件进行重写,使文件不至于过大,
只做缓存:如果只希望数据在服务器运行期间存在,可以不使用任何持久方式,
同时开启两种持久化方式: 在这种情况下,redis重启首先会加载AOF文件来恢复原始数据,因为通常情况下AOF比RDB要更加完整一点,
RDB数据不实时,同时使用两者时服务器重启也只会找AOF文件,那要不要只使用AOF呢?建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份)快速重启,而且不会有AOF可能潜在的乱码的bug,留着作为一个万一的手段
性能建议: 因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留 save 900 1 这一条规定
如果Enable AOF ,好处时在最恶劣的情况下也只会丢失不超过两秒的数据,启动脚本较简单只load自己的AOF文件即可,代价一是带来了持续的IO,而是AOF rewrite 的最后将rewrite过程中产生的新数据写道新文件造成的阻塞几乎是不可避免的,只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M 太小了,可以设到5G以上,默认超过原大小100%大小时重写可以改到适当的数值,
如果不Eable AOF,仅靠Master-Slave Replication 实现高可用性也是可以的,能节省掉一大笔IO,也减少了rewrite时带来的系统波动,代价时如果Master/Slave 同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个,
Redis事务
是什么? 可以一次执行多个命令,本质时一组命令的集合,一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其他命令插入,不许加塞,
怎么用?
Redis事务命令
DISCARD :取消事务,放弃执行事务块内的所有命令,
EXEC : 执行所有事务块内的命令
MULTI:标记一个事务块的开始
UNWATCH:取消WATCH命令对所有key的监视
WATCH key [key…] : 监视一个或多个key,如果在事务执行之前执行这个(或这些) key被其他命令所改动,那么事务将被打断
MULTI 表明开启了一个事务的队列,
然后我们输入执行命令,都会加入队列中去, set k1 v1 之后就会显示 QUEUED
什么命令都可以往里面去加,只要符合规范即可,
然后 EXEC 直接执行完所有的命令,各取所需,插入的返回OK,取值的返回结果,
如果我们在输入队列中的任务时,突然想要放弃,可以使用DISCARD进行放弃,
当我们执行一个事务的时候,如果这个事务中的一个明显有异常,那么就不必担心,这整个事务都执行不了,如果这个异常不是很大,那么可能只有它没有通过,其他的都可以通过,
悲观锁/乐观锁/CAS(Check And Set)
悲观锁:每次拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁,传统的关系型数据库里面就用到了很多这种锁的机制,比如行锁,表锁,读锁,写锁等,都是在做操作之前先上锁。
乐观锁: 每次去拿数据的时候都会认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制,乐观锁适用于多读的应用类型,这样可以提高吞吐量,
乐观锁策略:提交版本必须大于记录当前版本才能执行更新,
watch balance //监听余额
multi //开启事务
decrby balance 20
incrby debt 20
exec //这样就相当于执行完了一整个流程
但是有时候这个流程会出现问题,好比我在修改之前监听了这个balance,但是我在改动的时候发现它在此期间又被其他的人给修改了,这个时候执行就无法通过了,
一旦执行了exec 之前加的监控锁都会被取消掉了,
事务的三个阶段:
开启:以MULTI开始一个事务,
入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
执行:由EXEC命令触发事务
特性:单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行,事务在执行的过程中,不会被其他客户端发送来的命令请求所打断,没有隔离级别的概念,队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在 事务内的查询要看到事务里的更新,在事务外查询不能看到, 这个让人万分头痛的问题, 不保证原子性,redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚,
Redis的发布订阅
是什么? 进程间的一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息
订阅/发布消息图
Redis很少用消息的发布与订阅,一般都是用RocketMQ来做的
subscribe c1 c2 c3 相当于订阅了c1 c2 c3 这三个服务
publish c2 xxxxx 相当于给订阅了c2 这个服务的所有用户都发送一个xxxxx ,然后其他的人可以收到这个
可以按照名称去订阅,当然也可以按照通配符去订阅, psubscribe new* 相当于订阅了所有new开头的消息
publish new1 xxxx 就好比这个消息,因为符合上面的通配符规则,所以就可以被搜到
Redis的复制
是什么? 也就是我们所说的主从复制,主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slaver以读为主,
能干吗: 读写分离,容灾恢复,
怎么玩:
配从不配主 在从机上要配置,
从库配置:slaveof主库IP主库端口 每次与master断开后,都需要重新连接,除非配置进redis.conf文件
修改配置文件的细节操作,拷贝多个redis.conf文件,开启daemonize yes 表示可以使用集群,Pid文件名字,
配置文件指定端口,修改日志文件的名字, logfile “ “ 备份文件的名字也要修改 dump.rdb
常用三招:
一主二仆
info replication 查看当前服务的信息,在这个集群里面的地位,是主还是从,
我们默认启动一个redis服务的实例,这个就是服务主,
如果想要把一个redis的服务当成从的 ,就按照下面的例子来写, slaveof 127.0.0.1 6379
这样在主端口里面的数据也会被复制到从端口里面一份,
然后再次使用 info replication 就会发现有变化
注意:只有主机器是可以修改数据的,从机器只有读的权限,没有写的权限
如果主机死了,默认从机还是向原来一样,原地待命,然后主机再次上线之后继续开始工作,
如果从机死了,那么原有的主写从读的关系没有被破坏,从机再次上线的时候是不能连接上主机的,需要再次发送连接的命令 slaveof 127.0.0.1 6379 才能读取到主机上面的数据
薪火相传
上一个slave 可以是下一个slave的Master,slave同样可以接受其他slaves的连接和同步请求,那么该slaveu作为了链条中的下一个的master,可以有效减轻master的写压力,
中途变更转向:会清除之前的数据,重新建立拷贝最新的, 注意,这里面只会有一个master
这个时候如果主机挂了,当别人主机的那个从机,可以使用slaveof no one 命令成为新的主机,然后从机再次声明连接这个新的主机,这样这个集群就变成了他们两个之间的沟通,
slaveof no one 使当前数据库停止与其他数据库同步,转成主数据库,
复制原理
Slave启动成功连接到master后会发送一个sync命令,Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步,
全量复制:slave服务在接收到数据库文件数据后,将其存盘并加载到内存中,
增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步,
但是只要是重新连接master,一次完全同步,(全量复制)将会自动执行
反客为主 哨兵模式 sentinel
是什么? 反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库,
怎么玩? 新建一个 sentinel.conf 的文件,然后在里面编写内容,
sentinel monitor 127.0.0.1 6379 1
意思就是监听这个主机的端口,这个主机的端口挂掉之后,剩余的端口进行投票,谁的票数多,谁就变成了主机
启动哨兵: Redis-sentinel /usr/common/sentinel.conf
这个哨兵配置文件的目录依据实际情况而定,
如果主机死了,就从从机里面找一个当作主机,如果原来的主机又回来了,然后就把它当作从机,
复制延迟
由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以Master同步到Slave机器有一定的延迟
当系统很繁忙的时候,延迟问题会更加严谨,Slave机器数量的增加也会使这个问题更加严重
使用Jedis来连接Redis数据库
public class JedisTest {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1",6379);
System.out.println(jedis.getClient().getPort());
System.out.println("连接本地的Redis服务器成功");
//查看服务是否运行
System.out.println("服务正在运行:" + jedis.ping());
}
}
一些乱七八糟的常用方法
public class TestAPI {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.set("k1", "v1");
jedis.set("k2", "v2");
jedis.set("k3", "v3");
System.out.println(jedis.get("k3"));
Set<String> keys = jedis.keys("*");
for (Iterator iterator = keys.iterator(); iterator.hasNext(); ) {
String key = (String) iterator.next();
System.out.println(key);
}
System.out.println("jedis.exists ===> " + jedis.exists("k2"));
jedis.mset("str1", "v1", "str2", "v2");
System.out.println(jedis.mget("str1", "str2"));
jedis.lpush("mylist", "v1", "v2", "v3", "v4", "v5");
//list
List<String> mylist = jedis.lrange("mylist", 0, -1);
for (String element :
mylist) {
System.out.println(element);
}
//set
jedis.sadd("myset", "jd001");
jedis.sadd("myset", "jd002");
Set<String> myset = jedis.smembers("myset");
for (Iterator iterator = myset.iterator(); iterator.hasNext(); ) {
String string = (String) iterator.next();
System.out.println(string);
}
jedis.srem("myset","jd002");
System.out.println("删除set之后的大小"+jedis.smembers("myset").size());
//hash
jedis.hset("hash1","username","杨梦");
System.out.println(jedis.hget("hash1","username"));
HashMap<String, String> map = new HashMap<String, String>();
map.put("telephone","136733397491");
map.put("address","atguigu");
map.put("email","abc@163.com");
jedis.hmset("hash2",map);
List<String> result = jedis.hmget("hash2", "telephone", "email");
for (String element :
result) {
System.out.println(element);
}
//zset
jedis.zadd("zset01",60d,"v1");
jedis.zadd("zset01",70d,"v2");
jedis.zadd("zset01",80d,"v3");
jedis.zadd("zset01",90d,"v4");
Set<String> zset01 = jedis.zrange("zset01", 0, -1);
for (Iterator iterator = zset01.iterator();iterator.hasNext();){
String string = (String)iterator.next();
System.out.println(string);
}
}
}
我们也可以自己模拟出一个jedis的连接线程池,使用单例模式进行构建
public class JedisPoolUtil {
private static volatile JedisPool jedisPool = null;
private JedisPoolUtil() {
}
public static JedisPool getJedisPoolInstance() {
if (null == jedisPool) {
synchronized (JedisPoolUtil.class) {
if (null == jedisPool) {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(1000);
poolConfig.setMaxIdle(32);
poolConfig.setMaxWaitMillis(100 * 1000);
poolConfig.setTestOnBorrow(true); //获取一个实例的时候是否检查连接可用性
jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);
}
}
}
return jedisPool;
}
public static void release(JedisPool jedisPool, Jedis jedis){
if (null != jedis) {
jedisPool.returnResourceObject(jedis);
}
}
}