学习一门技术,我们首先得明白以下几点:
带着这几个问题去学,我们才能将它的衣服一件件的扒光,最后看到它的本质。不然面试的时候面试官稍微问得深入一点就凉凉了。接下来聊聊NoSql。
NoSql意思是Not Only Sql,不仅仅是SQL。NoSql也被称作非关系型数据库,那么与之相对的就是关系型数据库。你去百度“什么叫关系型数据库”,搜到的是非常官方的答案,说是依据关系模型来创建的数据库。看了半天,所有字都认识,但是连在一起就不知道是什么意思了。这里不搞那些花里胡哨的,用最简单的话说明白:
在此之间,先来说说互联网架构的演变(数据库层面)。
关于redis的安装、五种数据类型以及jedis我在之前的文章中有说过,这里来聊聊剩下的内容。 1、发布与订阅:
public class SubUtil extends JedisPubSub {
@Override
public void onMessage(String channel, String message) {
System.out.println(String.format("receive redis published message, channel %s, message %s", channel, message));
}
@Override
public void onSubscribe(String channel, int subscribedChannels) {
System.out.println(String.format("subscribe redis channel success, channel %s, subscribedChannels %d",
channel, subscribedChannels));
}
@Override
public void onUnsubscribe(String channel, int subscribedChannels) {
System.out.println(String.format("unsubscribe redis channel, channel %s, subscribedChannels %d",
channel, subscribedChannels));
}
}
订阅者:
/**
* 订阅者(先启动订阅者,再启动发布者)
*/
public class SubOne extends SubUtil {
public static void main(String[] args) throws Exception{
Jedis jedis = new Jedis("192.168.x.xx", 6379);
SubOne subOne = new SubOne();
jedis.subscribe(subOne, "test");
System.in.read();
}
}
发布者:
public class Pub {
public static void main(String[] args) throws Exception {
Jedis jedis = new Jedis("192.168.2.43",6379);
long result = jedis.publish("test","hello");
}
}
这样就搞定了发布与订阅,注意,先启动订阅者,再启动发布者。按顺序启动后会看到如下运行结果:
运行结果
2、管道技术: 在说管道技术之前先说说TCP请求响应模型。
系统提示:抱歉,连接超时了……
以上就是TCP请求响应模型(本故事纯属虚构,程序员哪来的女朋友?)。
redis其实也是这种请求响应模型的服务,客户端向服务端发送一个命令,等待服务端的返回;服务端接收到命令进行执行,然后将结果返回给客户端。在这个回合中,服务端是接收不了其他命令的。假如一个回合需要250毫秒,即使服务端一次性能接收100k的请求数,那么1秒钟也只能处理4个请求。使用管道技术就可以解决这种问题。使用管道就相当于可以并发处理,客户端不用等待服务端的响应,继续发起下一个请求。
那么jedis如何使用管道技术呢?请看下面的代码:
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.X.XX",6379);
// 未使用管道技术
long start1 = System.currentTimeMillis();
for (int i = 1; i <= 10000; i++) {
jedis.set(UUID.randomUUID().toString(), String.valueOf(i));
}
long end1 = System.currentTimeMillis();
System.out.println("未使用管道技术插入10000条数据耗时:" + (end1 - start1) + "毫秒");
// 使用管道技术
Pipeline pipeline = jedis.pipelined();
long start2 = System.currentTimeMillis();
for (int i = 1; i <= 10000 ; i++) {
pipeline.set(UUID.randomUUID().toString(), String.valueOf(i));
}
long end2 = System.currentTimeMillis();
System.out.println("使用管道技术插入10000条数据耗时:" + (end2 - start2) + "毫秒");
}
看运行结果:
运行结果
不得不说这管道技术还是有两把刷子的,这耗时相差将近40倍。
3、为key设置过期时间: 我们写入redis中的内容,可以对key设置过期时间。设置key的过期时间,超过时间后,将会自动删除该key(只有执行对key值有影响的操作时才会清除)。设置过期时间的方法是expire,如下:
jedis.expire("hash",30);
这就是将"hash"这个key设置为30秒后过期。 通过persist方法可以清除过期。
jedis.persist("hash");
这就是将"hash"这个key过期清除掉,也就是相当于上面设置的过期无效了。 如果对一个设置了过期时间的key再次设置过期时间,那么将刷新这个过期时间。比如“hash”这个key我设置了30秒过期,当过了24秒的时候我再次使用expire方法设置它的过期时间为60秒,那么这个key的离过期又还有60秒的时间。
4、redis的事务: redis的事务,本质是一组命令的集合。所有的命令都会序列化,然后按顺序串行地执行。事务开启后,所有的命令先入队,提交事务的时候,要么全执行,要么全不执行。常用命令如下:
命令 | 作用 |
---|---|
multi | 开启事务 |
exec | 提交事务 |
discard | 取消事务 |
watch key... | 监视一个或多个key,如果事务提交前这些key被改动,事务将被打断 |
unwatch | 取消对所有key的监视 |
下面来使用一下事务:
正常情况
这种是正常执行,如果最后不是exec,而是discard,那就是放弃事务了。接下来看看下面这种情况:
事务中有语法错误
set k3的时候,小手一抖,写成了sete,最后发现事务提交不了,所有的命令都未执行。这种就相当于java中的编译时期就报错了,所以肯定是提交不了的。接下来再看另外一种情况:
运行时异常
开启事务后set了一个String类型的k1,然后让其自增,再set其他的key,最后提交。结果是只有自增的那条命令执行报错,其他的正常执行。
最后来看看事务中的watch和unwatch命令。watch相当于乐观锁,如果watch的key在事务提交前被修改了,那么事务就会提交失败,得重新watch,获取到最新值(watch应该在事务开启之前)。
watch
我开了两个窗口,watch的k1初始值是100,然后在事务中改成200,在事务提交之前,另外一边将其改成了50,现在提交事务就会出现如下结果:
结果
这时候再重新开启事务去修改,如果这个过程没有再被修改,事务才能提交成功,这就类似于CAS。
5、redis执行txt文件中的命令: 如果我们需要执行的redis命令很多,可以写到一个txt文件中,然后读取这个txt文件即可执行里面的命令。具体步骤如下:
set name tom
set age 20
set sex man
set phone 8008208820
set country china
unix2dos redispipedata.txt
如果找不到命令,yum install 一下即可。
cat /srv/redispipedata.txt | ./redis-cli
即可批量执行txt文件中的命令了,执行后可以看到如下结果:
执行结果
上面介绍了管道技术,其实执行txt文件中的命令也可以使用管道技术,前面的步骤都一样,只不过是执行命令改成为:
cat /srv/redispipedata.txt | ./redis-cli --pipe
执行成功的话会看到如下结果:
执行结果
6、redis的持久化: 接触过redis的童鞋一定听过RDB和AOF这两个词,这是redis持久化的两种方式,下面就介绍一下RDB和AOF。
这两种持久化方式原理其实都是利用了写时复制,redis搞出两个进程,父进程与子进程,RDB方式的时候,子进程以快照形式保存数据,AOF方式的时候,子进程记录写操作。
config get dir
命令可以查看dump.rdb文件所在的位置。
被动保存: 在redis.conf中,有一项配置叫SNAPSHOTTING,这里就是关于持久化的一些配置。
rdb的配置
这三行配置分别表示:15分钟内改了1次;5分钟内改了10次;1分钟内改了1万次。只要满足这三条中的任意一条,就会将数据保存到dump.rdb中。(测试的时候可以先删除掉dump.rdb文件,然后5分钟内让10个key发生改变,看看是否重新生成了dump.rdb,然后直接让redis shutdown,然后重新启动,keys * 看看之前设置的key是否还在)。
主动保存: 假如你设置的某个key十分重要,你想设置完就写到rdb文件中,那么可以在redis-cli中设置完后用save命令。
主动保存
AOF配置
可以看到,AOF默认是关闭的,默认文件名为appendonly.aof。上面说到过AOF有三种同步策略,请看下图,确实是3种,我不是吹的。
AOF同步策略
所以,要使用AOF只需要把上面的no改成yes,然后选择策略就可以了。现在的问题是,如果同时存在RDB文件和AOF文件,那么redis启动的时候根据谁来恢复?
测试的办法:vim打开appendonly.aof文件,在末尾加上一些乱七八糟的不正确的指令,保存退出。然后再启动redis,如果redis能够正常启动,说明启动时是优先根据RDB来恢复的,如果不能正常启动,说明优先根据AOF来恢复。接下来就开打:
同时存在RDB和AOF
将appendonly.aof乱改一通:
aof文件
然后启动redis:
启动redis
结果报错了,说明启动时优先根据AOF来恢复数据的。AOF文件中有不可执行的命令,redis启动就会报错,那么怎么修复呢?不要告诉我你打算手动的去将AOF文件中那些不可执行的命令删掉,万一你手一抖多删了怎么办。我们看看redis的src目录:
src目录
没错,就是这两个文件,一个是修复RDB文件的,一个是修复AOF文件的。在src目录下执行
./redis-check-aof --fix appendonly.aof
就可以修复了。
在上面AOF的介绍中提到了会对aof文件进行压缩重写,那么什么时候会触发重写呢?看看配置文件中的相关配置:
重写策略
这两行配置的意思是:当文件大小大于上次rewrite时文件大小的100%且大于64MB时进行重写。其实如果一个公司如果大规模使用redis的话,rewrite-min-size 至少3GB起步。
redis的持久化小总结: RDB方式性能更好,但是数据完整性不如AOF,所以如果对数据完整性要求不高,使用RDB即可;如果对数据完整性要求高,那么请同时使用RDB和AOF;如果数据量大,使用AOF会进行大量的IO操作,对性能的影响十分明显,那么就使用接下来要介绍的主从复制。
7、redis主从: 所谓主从,就是主从复制,主机数据更新后自动同步到从机的机制,Master以写为主,Slave以读为主。主从可以实现读写分离以及容灾恢复。主从可以分为三种模式,一主二从、薪火相传和反客为主(这里的二是泛指,其实是一主n从)。
(1)、主从之一主二从
info replication
命令,可以看到下图信息:
info
可以看到,目前启动的三个redis的角色都是master,slave都是0。接下来,在6379的redis中set几个值,然后在6380和6381的redis中输入
slaveof 127.0.0.1 6379
接下来你就会发现,6379的redis中set的值在另外两台机器中也有。
一主二仆
这里我先set 了k1,然后再将6380和6381slaveof 6379的,最后发现get k1也是可以取到值。说明从机会把主机所有数据到拉到从机上,包括监视主机之前主机上的数据。另外,从机是不能set 值的,读写分离,主机负责写,从机是不能写的,在从机set 值会报如下错误:
从机不能写数据
完成了一主二从的配置,看看下面几个问题:
主机挂了
可以看到,主机挂了,从机还是从机,没有造反。
主机复活
可以看到,原先的主从体系并没有土崩瓦解,还可以用。
从机挂了
从机挂了再启动,从机就不再是从机了,而变成了另外一个master。所以用slaveof命令配置的从机重启后需要重新配置。
(2)、主从之薪火相传: 上面的一主N从,只有一个boss,所有的小弟都直接跟boss打交道,小弟一多boss也会很累的。所以就有了薪火相传这种模式。6379是主机,6380认6379做大哥,6381认6380做大哥,就这样一脉相承。
薪火相传
将6381这台从机挂在6380下,这就是薪火相传。
(3)、主从之反客为主: 一主二从中说到过,主机挂了,从机原地待命,而反客为主就是主机挂了,从机上位了。要上位的从机执行如下命令即可:
slaveof no one
然后将另外一台从机挂在反客为主的这台机器下,那么原先的这两台从机就变成了一个新的一主一从体系。即使原先的主机回来了,也跟这个一主一从没关系了。
8、哨兵模式: 上面讲了主从的三种模式,那么哨兵又是什么鬼?说白了,哨兵模式就是上面“反客为主”的自动版。上面说的“反客为主”,主机挂了需要手动地将从机设置为主机,哨兵模式就不需要手动设置了。哨兵模式相当于有一个哨兵在巡逻,一旦发现主机挂了,就会以投票的方式立即选出新的老大。
#监控主机,主机名,主机ip,主机端口,1表示投票,谁的票数多就晋升为主机
sentinel monitor host6379 127.0.0.1 6379 1
#后台启动
daemonize yes
然后启动哨兵,在redis的src目录下执行如下命令:
./redis-sentinel /etc/sentinel.conf
启动成功后,再让主机挂掉,等几秒钟,就会发现已经自动选出新的主机了。
哨兵
可以看到,主机挂了之后,哨兵自动选出了6381为新的主机,6380也跟着6381混了。如果6379又复活了,它会继续担任6380和6381的老大呢还是与6380和6381没关系了?还是成了6381的小弟?看图说话:
哨兵
6379不再是主,成为了6381的小弟,6381反客为主成功。