JavaEE进阶之Redis

作者: 码上搞定 | 来源:发表于2019-06-19 13:52 被阅读1次

    Redis是一个key-value的nosql数据库.先存到内存中,会根据一定的策略持久化到磁盘,即使断电也不会丢失数据。支持的数据类型比较多。

    主要用来做缓存数据库的数据和web集群时当做中央缓存存放seesion。

    守护进程:在linux或者unix操作系统中在系统引导的时候会开启很多服务,这些服务就叫做守护进程。为了增加灵活性,root可以选择系统开启的模式,这些模式叫做运行级别,每一种运行级别以一定的方式配置系统。 守护进程是脱离于终端并且在后台运行的进程。守护进程脱离于终端是为了避免进程在执行过程中的信息在任何终端上显示并且进程也不会被任何终端所产生的终端信息所打断。

    CAP定理

    高性能,高可用性和可伸缩性

    1)nosql数据库分类

    Redis和Memcache区别:

    • Redis和Memcache都是将数据存放在内存中,都是内存数据库。不过memcache还可用于缓存其他东西,例如图片、视频等等。

    • Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储。

    • 虚拟内存--Redis当物理内存用完时,可以将一些很久没用到的value 交换到磁盘

    nosql数据库分类:


    2)Redis使用场景

    缓存

    把经常需要查询的、很少修改数据,放到读速度很快的空间(内存),以便下次访问减少时间。减轻压力,减少访问时间。

    计数器

    • Redis中的计数器是原子性的内存操作。

    • 可以解决库存溢出问题.进销存系统库存溢出。

    session缓存服务器

    • web集群时作为session缓存服务器

    • 缓存队列等

    3)Redis对象保存方式

    Json字符串:

    需要把对象转换为json字符串,当做字符串处理。直接使用set get来设置或者或。

    优点:设置和获取比较简单

    缺点:没有提供专门的方法,需要把把对象转换为json。(jsonlib)

    字节:需要做序列号,就是把对象序列化为字节保存。

    如果是担心JSON转对象会消耗资源的情况,这个问题需要考量几个地方:

    第一点:就是使用的JSON转换lib是否就会存在性能问题。

    第二点:就是数据的数据量级别,如果是存储百万级的大数据对象,建议采用存储序列化对象方式。如果是少量的数据级对象,或者是数据对象字段不多,还是建议采用JSON转换成String方式。

    毕竟redis对存储字符类型这部分优化的非常好。具体采用的方式与方法,还要看你所使用的场景。

    4)Redis数据淘汰机制

    在 Redis 中,允许用户设置最大使用内存大小 server.maxmemory,在内存限定的情况下是很有用的。譬如,在一台 8G 机子上部署了 4 个 Redis 服务点,每一个服务点分配 1.5G 的内存大小,减少内存紧张的情况,由此获取更为稳健的服务。

    内存大小有限,需要保存有效的数据?

    Redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。

    Redis 提供 6种数据淘汰策略:

    volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

    volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

    volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

    allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

    allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

    no-enviction(驱逐):禁止驱逐数据

    5)Java操作Redis方式

    • 使用jedis java客户端来访问redis服务器,有点类似通过jdbc访问mysql一样。

    • 当然如果是spring进行集成时,可以使用spring data来访问redis,spring data只是对jedis的二次封装。jdbcTemplate jdbc关系一样。

    6)Redis存储的数据类型

    1.String

    要把一个String保存到redis中,用set(key,value),获取值用get(key)。

    2.Hash(一般用于保存对象)

    要把一个Hash保存到redis中,遍历Map<String,String>,逐个调用hset(key,hashKey,hashValue),获取所有值有hgetAll(key)。

    3.List

    要把一个List保存到redis中,遍历List<String>,逐个调用lpush(key,value),获取值用lrange(key,start,end),start代表开始位置,end代表结束位置,如果为-1则代表到未尾。

    这里lpush的意思是从左边保存,也就是后来居上。

    4.Set

    要把一个Set保存到redis中,遍历Set<String>,逐个调用sadd(key,value),获取值用smembers(key)。

    5.SortedSet

    SortedSet的意思是他的每一个元素是有顺序的,顺序根据其score来决定,如果socre一样,则按value排序。保存到redis的方法是,对每一个要保存的元素,调用zadd(key,score,value),获取值用zrange(key,satrt,end),start代表开始位置,end代表结束位置,如果为-1则代表到未尾。

    7)Redis的主从复制

    类似mysql的master-slave模式一样,redis的master-slave可以提升系统的可用性,master节点写入cache后,会自动同步到slave上。Master以写为主,Slave以读为主。

    可以实现:读写分离和容灾恢复

    缺点:延时,由于所有的写操作都是在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使得这个问题更加严重。

    配置过程:

    主Redis:192.168.10.1 6379 从Redis:192.168.10.2 6380

    • 将主从redis配置文件redis.conf中的aemonize no 改为 yes

    • 修改从redis配置文件redis.conf中的port 6379 改为 6380,添加slaveof 192.168.10.1 6379

    • 启动主从服务

    主Redis:

    [root@localhost redis-2.8.3]# src/redis-server /soft/redis-2.8.3-master/redis-2.8.3/redis.conf
    

    从Redis:

    [root@localhost redis-2.8.3]# src/redis-server /soft/redis-2.8.3-slave/redis-2.8.3/redis.conf
    

    测试数据同步

    主Redis:

    [root@localhost redis-2.8.3]# src/redis-cli -p 6379 ​ 127.0.0.1:6379> set name abc ​ OK ​ 127.0.0.1:6379> get name ​ "abc" ​ 127.0.0.1:6379>
    

    从Redis:

    [root@localhost redis-2.8.3]# src/redis-cli -p 6380 ​ 127.0.0.1:6380> get name ​ "abc" ​ 127.0.0.1:6380>
    

    默认是读写分离的

    在从Redis:

    [root@localhost redis-2.8.3]# src/redis-cli -p 6380 ​ 127.0.0.1:6380> set name 123 ​ (error) READONLY You can't write against a read only slave.
    

    主从切换

    1、停止主Redis

    [root@localhost redis-2.8.3]# src/redis-cli -n 6379 shutdown ​ [root@localhost redis-2.8.3]# src/redis-cli -p 6379 ​ Could not connect to Redis at 127.0.0.1:6379: Connection refused ​ not connected>
    

    2、将从Redis设成主Redis ​

    [root@localhost redis-2.8.3]# src/redis-cli -p 6380 slaveof NO ONE ​ OK
    

    3、测试从Redis是否切换从主redis

    [root@localhost redis-2.8.3]# src/redis-cli -p 6380 ​ 127.0.0.1:6380> set name 123 ​ OK ​ 127.0.0.1:6380> get name ​ "123" ​ 127.0.0.1:6380>
    

    4、原来的主Redis恢复正常了,要重新切换回去

    1)将现在的主redis的数据进行保存

    [root@localhost redis-2.8.3]# src/redis-cli -p 6380 ​ 127.0.0.1:6380> get name ​ "abc" ​ 127.0.0.1:6380> set name 123 ​ OK ​ 127.0.0.1:6380> get name ​ "123" ​ 127.0.0.1:6380> save ​ OK ​ 127.0.0.1:6380> get name ​ "123" ​ 127.0.0.1:6380>
    

    2)将现在的主redis根目录下dump.rdb文件拷贝覆盖到原来主redis的根目录

    3)启动原来的主redis

    [root@localhost redis-2.8.3]# src/redis-server /soft/redis-2.8.3-master/redis-2.8.3/redis.conf ​ 
    

    4)在现在的主redis中切换

    [root@localhost redis-2.8.3]# src/redis-cli -p 6380 slaveof 192.168.10.1 6379 ​ OK
    

    常用的设计方案:

    一主二仆:一个Master,两个Slave,Slave只能读不能写;当Slave与Master断开后需要重新slave of连接才可建立之前的主从关系;Master挂掉后,Master关系依然存在,Master重启即可恢复。

    薪火相传:上一个Slave可以是下一个Slave的Master,Slave同样可以接收其他slaves的连接和同步请求,那么该slave作为了 链条中下一个slave的Master,如此可以有效减轻Master的写压力。如果slave中途变更转向,会清除之前的数据,重新建立最新的。

    反客为主: 当Master挂掉后,Slave可键入命令 slaveof no one使当前Redis停止与其他Master redis数据同步,转成Master redis。

    复制原理:

    1. Slave启动成功连接到master后会发送一个sync命令;

    2. Master接到命令启动后的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步;

    3. 全量复制:而slave服务在数据库文件数据后,将其存盘并加载到内存中;

    4. 增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步;

    5. 但是只要是重新连接master,一次完全同步(全量复制)将被自动执行。

    哨兵模式:

    反客为主的自动版,能够后台监控Master库是否故障,如果故障了根据投票数自动将slave库转换为主库。一组sentinel能同时监控多个Master。

    使用步骤:

    • 1、在Master对应redis.conf同目录下新建sentinel.conf文件,名字绝对不能错;

    • 2、配置哨兵,在sentinel.conf文件中填入内容:

    sentinel monitor 被监控数据库名字(自己起名字) ip port 1

    说明:上面最后一个数字1,表示主机挂掉后slave投票看让谁接替成为主机,得票数多少后成为主机。

    • 3、启动哨兵模式:

      命令键入:redis-sentinel /myredis/sentinel.conf

      注:上述sentinel.conf路径按各自实际情况配置

    8)Redis序列化器

    普通的连接使用没有办法把Java对象直接存入Redis,而需要我们自己提供方案-对象序列化,然后存入redis,取回序列化内容后,转换为java对象。Spring模板中提供了封装的方案,在它内部提供了RedisSerializer接口(org.springframework.data.redis.serializer.RedisSerializer)和一些实现类。也可以自定义序列化器,实现RedisSerializer接口。常用的有:StringRedisSerializer,JdkSerializationRedisSerializer<T>,GenericToStringSerializer

    RedisTemplate默认的系列化类是JdkSerializationRedisSerializer,用JdkSerializationRedisSerializer序列化的话,被序列化的对象必须实现Serializable接口。在存储内容时,除了属性的内容外还存了其它内容在里面,总长度长,且不容易阅读。 我们要求是存储的数据可以方便查看,也方便反系列化,方便读取数据。

    Jackson2JsonRedisSerializer和GenericJackson2JsonRedisSerializer,两者都能序列化成json,但是后者会在json中加入@class属性,类的全路径包名,方便反系列化。前者如果存放了List则在反系列化的时候如果没指定TypeReference则会报错:java.util.LinkedHashMap cannot be cast to

    9)websocket

    WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

    WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

    在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

    现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

    WebSocket 协议本质上是一个基于 TCP 的协议。

    为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息"Upgrade: WebSocket"表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。

    websocket事件触发机制:

    open

    一旦服务器响应了WebSocket连接请求,open事件触发并建立一个连接。open事件对应的回调函数称作onopen。

    到open事件触发时,协议握手已经完成,WebSocket已经准备好发送和接收数据。如果应用程序接收到一个open事件,那么 可以确定WebSocket服务器成功地处理了连接请求,并且同意与应用程序通信。

    message

    WebSocket消息包含来自服务器的数据。你也可能听说过组成WebSocket消息的WebSocket帧(Frame)。第3章将详细讨论消息和帧的概念。为了理解消息使用API的方式,WebSocket API只输出完整的消息,而不是WebSocket帧。message事件在接收到消息时触发,对应于该事件的回调函数是onmessage。

    除了文本,WebSocket消息还可以处理二进制数据,这种数据作为Blob消息或者ArrayBuffer消息处理。因为设置WebSocket消息二进制数据类型的应用程序会影响二进制消息,所以必须在读取数据之前决定用于客户端二进制输入数据的类型。

    error

    error事件在响应意外故障的时候触发。与该事件对应的回调函数为onerror。错误还会导致WebSocket连接关闭。如果你接收一个error事件,可以预期很快就会触发close事件。close事件中的代码和原因有时候能告诉你错误的根源。error事件处理程序是调用服务器重连逻辑以及处理来自WebSocket对象的异常的最佳场所。

    close

    close事件在WebSocket连接关闭时触发。对应于close事件的回调函数是onclose。一旦连接关闭,客户端和服务器不再能接收或者发送消息。

    说明:WebSocket规范还定义了ping和pong帧,可以用于持续连接(keep-alive)、心跳、网络状态检测、延迟测量等,但是WebSocket API目前没有输出这些特性。尽管浏览器接受ping帧,但是不会触发对应WebSocket上的ping事件。相反,浏览器将自动响应pong帧。然而,浏览器实例化的ping如果在一段时间内没有得到pong应答,可能会触发连接的close事件。

    当调用close()方法终止与服务器的连接时,也会触发onclose事件处理程序;WebSocket close事件在连接关闭时触发,这可能有多种原因,比如连接失败或者成功的WebSocket关闭握手。WebSocket对象特性readyState反映了连接的状态(2为正在关闭,3为已关闭)。

    close事件有3个有用的属性(property),可以用于错误处理和恢复:wasClean、code和error。wasClean属性是一个布尔属性,表示连接是否顺利关闭。如果WebSocket的关闭是对来自服务器的一个close帧的响应,则该属性为true。如果连接是因为其他原因(例如,因为底层TCP连接关闭)关闭,则该属性为false。code和reason属性表示服务器发送的关闭握手状态。这些属性和WebSocket.close()方法中的code和reason参数一致。

    相关文章

      网友评论

        本文标题:JavaEE进阶之Redis

        本文链接:https://www.haomeiwen.com/subject/wxtkqctx.html