Java缓存机制
1 Java缓存
1.1 jvm内置缓存
Java中实现缓存的方式有很多,比如用static hashMap基于内存缓存的jvm内置缓存,简单不实用,保对象的有效性和周期无法控制,容易造成内存急剧上升。常用的有Oscache(主要针对jsp页面),Ehcache(主要针对数据库访问层),Jcache,Jbosscache等等很多
缺点:容易内存溢出、没有持久化(服务重启后丢失)、线程安全、多个服务器(多个jvm)之间的数据不能共享。
1.2 java操作eache
利用spring Boot搭建应用(可参考这里的缓存配置)。
访问http://localhost:8080/getUser?name=springboot2.2后查询后返回
- {
- id: 35,
- name: "springboot2.2",
- age: 99
- }
修改数据库中age字段后再次访问http://localhost:8080/getUser?name=springboot2.2后结果不变?代表使用了缓存,成功配置了缓存。
访问http://localhost:8080/removeCache清除缓存后,再次访问http://localhost:8080/getUser?name=springboot2.2后结果就为修改后的值。
原理?
1、当客户端请求数据时,如果服务器端配置了缓存,第一步去缓存里面查找,如果有跳4,没有则往2
2、发送jdbc请求操作数据库查询数据
3、将查询到的数据返回给缓存,并保存在缓存中
4、将从(缓存|数据库)查询到的数据返回给客户端。
好处?效率高(因为不用建立jdbc连接等)、降低了数据库的压力(不用每次请求都要去数据库查,数据库也会累啊)。
缺点?从上述也看到了,有可能产生数据不一致的情况,清除缓存可解决。
和oscache区别? ehcache 主要是对数据库访问的缓存,相同的查询语句只需查询一次数据库,从而提高了查询的速度,使用spring的AOP可以很容易实现这一功能。 oscache 主要是对页面的缓存,可以整页或者指定网页某一部分缓存,同时指定他的过期时间,这样在此时间段里面访问的数据都是一样的。
2 Redis
关系型数据库:持久、主外键、编写SQL语句、存放在硬盘。
非关系型数据库:一般用于缓存、值存放在内存(所以效率是真的高)、key-vakye形式、容易数据丢失(不过很好解决)、有点小类似jvm内置缓存(不过这个更牛嗨,因为可以多个服务器间共享数据)。
redis?可以持久化mongdb?存储json格式
2.1 Redis概述
完全开源免费、最受BSD协议、高性能的key-value费关系型数据库。支持持久化、支持key-value(String)\list\set\zert\hash等数据结构的存储、支持备份
好处?减轻数据库访问的压力。效率高?(访问内存肯定比访问硬盘快,这是常识)
应用场景?(token生成、session共享、分布式锁、验证码、自增id(订单id))
2.2 安装redis
2.2.1 windows安装redis
1、下载redis
2、解压redis-latest-windws.zip文件,将start.bat文件拷贝纸redis目录
3、编辑redis.windows.conf文件,取消requirepass的注释(前面不能有空格),空格然后添加密码比如(123456)保存。以requirepass 123456为例
4、双击start.bat运行
5、通过这个客户端工具测试一下
2.2.2 linux安装redis
1、下载redis
2、mkdir -p /usr/local/redis/bin,mkdir -p /usr/local/redis/etc
3、copy redis-3.0.0.tar.gz到用户目录比如/root
4、解压tar -zxvf redis-3.0.0.tar.gz
5、cd redis-3.0.0/后,make一下
6、进入src目录make install后就安装成功了。
7、cd cd /root/redis-3.0.0/(redis安装目录)
8、cp redis.conf /usr/local/redis/etc
9、cd src
10、cp mkreleasehdr.sh redis-benchmark redis-check-aof redis-check-dump redis-cli redis-server redis-sentinel /usr/local/redis/bin
11、修改 redis.conf文件
daemonize yes --- 修改为yes 后台启动
requirepass 123456 ----注释取消掉设置账号密码
ps aux | grep '6379' --- 查询端口
kill -15 9886 --- 杀死重置
kill -9 9886 --- 强制杀死
service iptables stop 停止防火墙
12、cd /usr/local/redis/bin
./redis-server /usr/local/redis/etc/redis.conf启动服务
13、./redis-cli -h 127.0.0.1 -p 6379 -a "123456" --- redis 使用账号密码连接或者windows下的客户端工具进行连接测试
PING 结果表示成功
14、停止redis
redis-cli shutdown 或者 kill redis进程的pid
15、放开一下端口,好像外部即使注释掉bind 127.0.0.1也不能访问/sbin/iptables -I INPUT -p tcp --dport 6379 -j ACCEPT
2.3 redis基本数据类型
2.3.1 字符串
- 127.0.0.1:6379> set name raolei
- OK
- 127.0.0.1:6379> set itboy www.itboy.com
- OK
- 127.0.0.1:6379> get name
- "raolei"
- 127.0.0.1:6379> get itboy
- "www.itboy.com"
常用命令:
编号 |
命令 |
描述说明 |
1 |
此命令设置指定键的值。 |
|
2 |
获取指定键的值。 |
|
3 |
获取存储在键上的字符串的子字符串。 |
|
4 |
设置键的字符串值并返回其旧值。 |
|
5 |
返回在键处存储的字符串值中偏移处的位值。 |
|
6 |
获取所有给定键的值 |
|
7 |
存储在键上的字符串值中设置或清除偏移处的位 |
|
8 |
使用键和到期时间来设置值 |
|
9 |
设置键的值,仅当键不存在时 |
|
10 |
在指定偏移处开始的键处覆盖字符串的一部分 |
|
11 |
获取存储在键中的值的长度 |
|
12 |
为多个键分别设置它们的值 |
|
13 |
为多个键分别设置它们的值,仅当键不存在时 |
|
14 |
设置键的值和到期时间(以毫秒为单位) |
|
15 |
将键的整数值增加 |
|
16 |
将键的整数值按给定的数值增加 |
|
17 |
将键的浮点值按给定的数值增加 |
|
18 |
将键的整数值减 |
|
19 |
按给定数值减少键的整数值 |
|
20 |
将指定值附加到键 |
2.3.2 list
- 127.0.0.1:6379> lpush listkey redis
- (integer) 1
- 127.0.0.1:6379> lpush listkey mysql
- (integer) 2
- 127.0.0.1:6379> lpush listkey mongdb
- (integer) 3
- 127.0.0.1:6379> lpush listkey hbase
- (integer) 4
- 127.0.0.1:6379> lrange listkey 0 10
- 1) "hbase"
- 2) "mongdb"
- 3) "mysql"
- 4) "redis"
list是简单的字符串列表,按照插入顺序排序,可在头和尾插入,最多包含2^32-1个元素(40多亿)。
常用命令:
序号 |
命令及描述 |
1 |
BLPOP key1 [key2 ] timeout |
2 |
BRPOP key1 [key2 ] timeout |
3 |
BRPOPLPUSH source destination timeout |
4 |
LINDEX key index |
5 |
LINSERT key BEFORE|AFTER pivot value |
6 |
LLEN key |
7 |
LPOP key |
8 |
LPUSH key value1 [value2] |
9 |
LPUSHX key value |
10 |
LRANGE key start stop |
11 |
LREM key count value |
12 |
LSET key index value |
13 |
LTRIM key start stop |
14 |
RPOP key |
15 |
RPOPLPUSH source destination |
16 |
RPUSH key value1 [value2] |
17 |
RPUSHX key value |
2.3.3 set
的Set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
哈希表实现。添加,删除,查找的复杂度都是O(1)。每个集合可存储40多亿个成员
- 127.0.0.1:6379> sadd setkey redis
- (integer) 1
- 127.0.0.1:6379> sadd setkey redis
- (integer) 0
- 127.0.0.1:6379> sadd setkey redis
- (integer) 0
- 127.0.0.1:6379> sadd setkey redis
- (integer) 0
- 127.0.0.1:6379> sadd setkey redis
- (integer) 0
- 127.0.0.1:6379> sadd setkey mysql
- (integer) 1
- 127.0.0.1:6379> sadd setkey mongdb
- (integer) 1
- 127.0.0.1:6379> SMEMBERS setkey
- 1) "mysql"
- 2) "mongdb"
- 3) "redis"
常用命令:
序号 |
命令及描述 |
1 |
SADD key member1 [member2] |
2 |
SCARD key |
3 |
SDIFF key1 [key2] |
4 |
SDIFFSTORE destination key1 [key2] |
5 |
SINTER key1 [key2] |
6 |
SINTERSTORE destination key1 [key2] |
7 |
SISMEMBER key member |
8 |
SMEMBERS key |
9 |
SMOVE source destination member |
10 |
SPOP key |
11 |
SRANDMEMBER key [count] |
12 |
SREM key member1 [member2] |
13 |
SUNION key1 [key2] |
14 |
SUNIONSTORE destination key1 [key2] |
15 |
2.3.4 sorted set
与set一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
- 127.0.0.1:6379> zadd zsetkey 1 redis
- (integer) 1
- 127.0.0.1:6379> zadd zsetkey 2 mysql
- (integer) 1
- 127.0.0.1:6379> zadd zsetkey 2 mongdb
- (integer) 1
- 127.0.0.1:6379> smembers setkey
- 1) "mysql"
- 2) "mongdb"
- 3) "redis"
常用命令:
2.3.5 hash
是一个string类型的field和value的映射表(和map差不多,只是兼职都是字符串),hash特别适合用于存储对象。Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)。
- 127.0.0.1:6379> hmset hmset name "redis tutorial"
- OK
- 127.0.0.1:6379> hmset hmset age 24
- OK
- 127.0.0.1:6379> hgetall hmset
- 1) "name"
- 2) "redis tutorial"
- 3) "age"
- 4) "24"
序号 |
命令及描述 |
1 |
HDEL key field2 [field2] |
2 |
HEXISTS key field |
3 |
HGET key field |
4 |
HGETALL key |
5 |
HINCRBY key field increment |
6 |
HINCRBYFLOAT key field increment |
7 |
HKEYS key |
8 |
HLEN key |
9 |
HMGET key field1 [field2] |
10 |
HMSET key field1 value1 [field2 value2 ] |
11 |
HSET key field value |
12 |
HSETNX key field value |
13 |
HVALS key |
14 |
HSCAN key cursor [MATCH pattern] [COUNT count] |
redis怎么存放对象?通过将对象序列化为json字符串,存放为字符串形式,之后读取在反序列化为对象,这样很快很快
2.4 Spring Boot集成redis
添加依赖:
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <!--spring2.0集成redis所需common-pool2-->
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-pool2</artifactId>
- <version>2.4.2</version>
- </dependency>
配置文件:
- ########################################################
- ###Redis (RedisConfiguration)
- ########################################################
- spring:
- redis:
- database: 0
- host: 192.168.245.134
- port: 6379
- password: 123456
- jedis:
- pool:
- max-idle: 8
- min-idle: 0
- max-active: 8
- max-wait: -1ms
- timeout: 5000ms
service:
- @Service
- public class RedisService {
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
- //字符串
- public void setStringKey(String key,String value,Long time){
- setObject(key,value,time);
- }
- public void setStringKey(String key,String value){
- setObject(key,value,null);
- }
- //set
- public void setSetKey(String key,Set value){
- setObject(key,value,null);
- }
- //list
- public void setListKey(String key,List value){
- setObject(key,value,null);
- }
- public String getStringKey(String key){
- return (String) getObject(key,new String());
- }
- public Set getSetKey(String key){
- return (Set) getObject(key,new HashSet<String>());
- }
- public List getListKey(String key){
- return (List) getObject(key,new ArrayList<String>());
- }
- public void setObject(String key,Object value,Long time){
- if(StringUtils.isEmpty(key)||value==null){
- return;
- }
- //字符串类型
- if(value instanceof String){
- String value1= (String) value;
- if(time!=null){
- stringRedisTemplate.opsForValue().set(key,value1,time,TimeUnit.SECONDS);
- } else{
- stringRedisTemplate.opsForValue().set(key,value1);
- }
- return;
- }
- //list类型
- else if(value instanceof List){
- List<String> list= (List<String>) value;
- for (String s:list) {
- stringRedisTemplate.opsForList().leftPush(key,s);
- }
- return;
- }
- //set
- else if(value instanceof Set){
- Set<String> strings= (Set<String>) value;
- for (String s : strings) {
- stringRedisTemplate.opsForSet().add(key,s);
- }
- return;
- }
- /**
- * .....
- */
- }
- public Object getObject(String key,Object object){
- if(StringUtils.isEmpty(key)||object==null){
- return null;
- }
- else if (object instanceof String){
- return stringRedisTemplate.opsForValue().get(key);
- }
- else if(object instanceof List){
- return stringRedisTemplate.opsForList().range(key,0,stringRedisTemplate.opsForList().size(key));
- }
- else if(object instanceof Set){
- return stringRedisTemplate.opsForSet().members(key);
- }
- return null;
- }
- }
controller:
- @RestController
- public class IndexController {
- @Autowired
- private RedisService redisService;
- @RequestMapping("/setString")
- public String setString(@PathParam("key") String key,
- @PathParam("value") String value){
- redisService.setStringKey(key,value);
- return redisService.getStringKey(key);
- }
- @RequestMapping("/setSet")
- public Set<String> setSet(@PathParam("key") String key,
- @PathParam("value") String value){
- HashSet<String> strings = new HashSet<>();
- strings.add(value);
- strings.add(value+"1");
- strings.add(value+"2");
- strings.add(value+"3");
- redisService.setSetKey(key,strings);
- return redisService.getSetKey(key);
- }
- @RequestMapping("/setList")
- public List<String> setList(@PathParam("key") String key,
- @PathParam("value") String value){
- ArrayList<String> strings = new ArrayList<>();
- strings.add(value);
- strings.add(value+"1");
- strings.add(value+"2");
- strings.add(value+"3");
- redisService.setListKey(key,strings);
- return redisService.getListKey(key);
- }
- }
访问http://localhost:8080/setSet?key=itboySet&value=123456,http://localhost:8080/setString?key=itboySet&value=123456,
http://localhost:8080/setList?key=itboySet&value=123456进行测试一下是否成功!!!!!!!!
2.5 主从复制和哨兵机制理解
为什么?数据备份、读写分离、集群、高可用(宕机容错机制)。
一般情况下,Redis高可用都是一主多从,而不像其他比如Nginx多主多从。
所谓主从复制,主要是为了减轻单台服务器的压力(比如图中的master),通过多台服务器的冗余来保证高可用(单台宕机容错),实现读写分离、数据备份、集群等。
如图,其中master可读可写,但是当有客户端连接达到集群时,如果是读操作就从slave从节点中随机选择一台服务器进行响应,如果是写操作,那么操作主服务器。这就是读写分离了不是吗。。。
问题?主服务器写之后,怎么同步到从服务器?(主从复制搞定,往下看)
问题?主服务器宕机了,怎么写?通过哨兵机制,哨兵其实就是一个监听器,一直监听这主服务器,如果主服务器挂了,他就会使用投票(随机)从主服务器中选择一台服务器作为主服务器,此乃高可用。
问题?那如果整个集群挂了呢?有一个东西叫做keepalived监听器(其实就是一个shell写的重启服务的命令脚本),如果监听到某一台服务器挂了,他就会自动重启的(一般30秒内,听说可能不准确)。如果一直启动失败?那就没办法了,他只能发送一封邮件给运维人员了。
2.5.1 主从复制实现
原理:通过快照文件(类似mysql的二进制可执行文件),当master有更新时,从服务器slave会实时请求得到该快照文件,进行执行,如果网络问题?别担心过,会重试的。
过程:
1:当一个从数据库启动时,会向主数据库发送sync命令,
2:主数据库接收到sync命令后会开始在后台保存快照(执行rdb操作),并将保存期间接收到的命令缓存起来
3:当快照完成后,redis会将快照文件和所有缓存的命令发送给从数据库。
4:从数据库收到后,会载入快照文件并执行收到的缓存的命令。
对于redis服务器来说,和mysql有很大不同,只要设置好主从服务器之后,主服务器master可读可写,从服务器slave仅可读,不像mysql那样需要分配用户和mycat插件来控制读写分离。
配置过程:
1、准备服务器,如上图中三台服务器(192.168.245.134,192.168.245.135,192.168.245.136),选择一台为主服务器master(这台服务器什么也不用做)
2、所以redis主从复制只需要配置从服务器slave就OK,修改redis.conf配置文件,放开以下两行的注释,添加如下内容。两台从服务器都要修改。
- slaveof 192.168.245.134 6379
- #主服务器的ip和端口号
- # If the master is password protected (using the "requirepass" configuration
- # directive below) it is possible to tell the slave to authenticate before
- # starting the replication synchronization process, otherwise the master will
- # refuse the slave request.
- #
- masterauth 123456
- #主服务器的认证密码
3、测试
- #进入master主服务器
- 192.168.245.134:6379> info
- #回车后看到如下内容即代表成功
- # Replication
- role:master
- connected_slaves:2 #两台从服务器
- slave0:ip=192.168.245.135,port=6379,state=online,offset=127,lag=1 #一些描述信息
- slave1:ip=192.168.245.136,port=6379,state=online,offset=127,lag=1 #一些描述信息
- #进入任何一台服务器比如135,同样info以下,看到如下内容即代表成功。
- # Replication
- role:slave #角色从服务器
- master_host:192.168.245.134 #主服务器ip
- master_port:6379 #主服务端口
- master_link_status:up #状态
- master_last_io_seconds_ago:10
- master_sync_in_progress:0
- #主服务器
- 192.168.245.134:6379> set master "192.168.245.134"
- OK
- 192.168.245.134:6379> get master
- "192.168.245.134"
- 192.168.245.134:6379>
- ##可读可写是吧???????
- #########刚刚设置的这条数据从服务器有吗??????############
- 192.168.245.135:6379> get master
- "192.168.245.134"
- 192.168.245.135:6379> set slave "192.168.245.135"
- (error) READONLY You can't write against a read only slave.
- 192.168.245.135:6379>
- ############哟呵有数据的,主从复制成功,主从间数据同步问题解决,而且不能写,读写分离也搞定了########
- [root@localhost bin]# ./redis-cli -h 192.168.245.136 -p 6379 -a "123456"
- 192.168.245.136:6379> ping
- PONG
- 192.168.245.136:6379> get master
- "192.168.245.134"
- 192.168.245.136:6379> set slave "192.168.245.136"
- (error) READONLY You can't write against a read only slave.
- 192.168.245.136:6379>
- ################另一台从服务器也一样######################
2.5.2 哨兵机制实现
原理:哨兵用于管理多个redis服务器,执行以下三个任务:
1、监控(monitoring):哨兵(sentinel)会不断检查你的master和slave是否运作正常
2、提醒(notification):当被监控的某个redis出现问题时,哨兵(sentinel)可以通过API向管理员或者其他应用程序发送通知
3、自动故障迁移(automatic failover):当一个master1不能正常工作时,哨兵会开始一次自动故障迁移操作,他将会失效master1的其中一个slave升级为新的master2,并让失效master1的其他slave的master1改为新的master2。当客户端试图连接失效的master1时,集群也会向客户端返回新的master2地址,使得集群可以使用新的master2代替失效的master1
哨兵是一个分布式系统,可以在一个架构中运行多个哨兵进程,这些进程使用流言协议(gossipprotocols)来接收关于master是否下线的消息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移以及选择哪个slave作为新的master。
每个哨兵会向其他哨兵(sentinel)、master、slave定时发送消息,来确认对方是否还活着,如果对方在指定的时间(可配置)内未响应,则暂时认为对方已挂(主观认为宕机,Subjective Down,sdown)
若哨兵群中的多数sentinel都报告某一个master没响应,系统认为该master彻底死亡(客观真正的宕机,Objective Down,oDwon),通过一定vote算法,从生下的slave节点中选择提升一台为master,然后自动修改相关配置
虽然哨兵(sentinel) 释出为一个单独的可执行文件 redis-sentinel ,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你可以在启动一个普通 Redis 服务器时通过给定 --sentinel 选项来启动哨兵(sentinel).
实现:
这里以上面三台服务器为基础,选择192.168.245.136这台服务器为哨兵(可以选择多台,此处仅一台为例)。
注意:如果主从复制时,主服务器没有配置masterauth 123456请加上,这个坑了我很久很久,导致哨兵时一直连不上,最后查看日志才搞定,记得先将这个加到主服务器的redis.conf中,然后重启一下
1、修改redis安装目录下的sentinel.conf
- sentinel monitor mymaster 192.168.245.134 6379 1
- # 主节点名称 主机ip 端口号 选举次数(就是说当有几台sentinel认为master挂了才是真的挂了,因为这里只有一个哨兵,所以为1)
- sentinel down-after-milliseconds mymaster 30
- #就是说多少ms后没有给老子响应,老子就觉得你挂了。,默认30s,这里设置为30ms,本地测试追求实时
- sentinel config-epoch mymaster 1
- #这个数字表示在发生主从复制的时候,比如master1向master2切换时,可以同时有多少个slave能对master2执行同步(也就是复制其实),越多越好?>如果太多了那么大家都去复制了,谁来响应客户端的请求?太少?太少的话,每个都要来一遍,怕是要到天黑哦,根据实际情况吧,这里只有三台所以
- 为设为1.
- sentinel auth-pass mymaster 123456
- #主服务器密码
2、启动哨兵:nohup ./redis-server ../etc/sentinel.conf --sentinel 2>1 1>nohup.log &
3、如何停止ps -aux | grep 端口号,kill -9 pid即可
4、测试?
- #我们首先查看master(134)的info master
- role:master
- connected_slaves:2
- slave0:ip=192.168.245.135,port=6379,state=online,offset=5349,lag=1
- slave1:ip=192.168.245.136,port=6379,state=online,offset=5349,lag=0
- #135的 slave
- role:slave
- master_host:192.168.245.134
- master_port:6379
- master_link_status:up
- #136 的 slave
- role:slave
- master_host:192.168.245.134
- master_port:6379
- master_link_status:up
- #我现在讲master停掉? master
- #134
- 192.168.245.134:6379> shutdown
- not connected>
- #135 的info replication
- 192.168.245.135:6379> info replication
- # Replication
- role:slave
- master_host:192.168.245.136
- master_port:6379
- master_link_status:up
- #看到主服务器变为136,
- #136?
- 192.168.245.136:6379> info replication
- # Replication
- role:master
- connected_slaves:2
- slave0:ip=192.168.245.135,port=6379,state=online,offset=739,lag=0
- slave1:ip=192.168.245.134,port=6379,state=online,offset=739,lag=0
- #基本上成功了,最后试试读写分离以及在测试一些其他的,这里不再展示
2.6 数据持久化
数据持久化:就是将内存中的数据保存到硬盘,redis支持AOF和RDB两种存储方式
2.6.1 RDB存储
RDB是指在一个时间点,如果达到所配置的数据修改量,就写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。(二进制文件方式)
有两种保存方式:1、(阻塞)主进程直接拍快照(snapshot),然后阻塞客户端请求写入IO磁盘。2、(非阻塞)当要写入磁盘时,新建(fork)一个子进程,子进程将当前数据库快照写入磁盘,而主进程继续处理客户端请求。
每次快照持久化都是将内存数据完整写入到磁盘一次,并不 是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。
优点:使用单独子进程进行持久化,主进程不会进行任何IO操作,保证了redis的高性能
缺点:RDB需要间隔一段时间进行持久化,而且必须达到相应修改数量,所以如果持久化之间发生故障,会造成数据丢失,常用于数据要求不严谨的时候。
配置:
- #dbfilename:持久化数据存储在本地的文件
- dbfilename dump.rdb
- #dir:持久化数据存储在本地的路径,如果是在/redis/redis-3.0.6/src下启动的redis-cli,则数据会存储在当前src目录下
- dir ./
- ##snapshot触发的时机,save
- ##如下距离上一次持久化已经900s了,如果有大于等于1个变更,才会snapshot
- save 900 1
- ##对于此值的设置,需要谨慎,评估系统的变更操作密集程度
- ##可以通过“save “””来关闭snapshot功能
- save 300 10 #表示间隔达到300s时如果更改了10次以上,就snapshot,如果你在10s时已经10次了,立马持久化
- save 60 10000 #同理间隔达到60s时如果更改了10000次以上,就snapshot,如果你在10s时已经10000次了,立马持久化
- ##当snapshot时出现错误无法继续时,是否阻塞客户端“变更操作”,“错误”可能因为磁盘已满/磁盘故障/OS级别异常等
- stop-writes-on-bgsave-error yes
- ##是否启用rdb文件压缩,默认为“yes”,压缩往往意味着“额外的cpu消耗”,同时也意味这较小的文件尺寸以及较短的网络传输时间
- rdbcompression yes
这里我将save 300 10改为save 30 10进行测试一下(首先先停掉哨兵机制,不然宕机后他就重新选一台主的了):
改好后重启一下服务:
- 进行5次更改操作
- 192.168.245.136:6379> get name
- "1"
- 192.168.245.136:6379> set name 1
- OK
- 192.168.245.136:6379> set name 2
- OK
- 192.168.245.136:6379> set name 3
- OK
- 192.168.245.136:6379> set name 4
- OK
- 192.168.245.136:6379> set name 5
- OK
- 192.168.245.136:6379> get name
- "5"
- #立马kill掉redis进程,一定要kill如果主动关闭服务,他是会进行snapshot的
- [root@localhost bin]# ps -aux | grep 6379
- root 1572 0.1 0.9 140840 9640 ? Ssl 02:02 0:00 ./redis-server *:6379
- root 1577 0.0 0.5 20160 5184 pts/0 S+ 02:02 0:00 ./redis-cli -h 192.168.245.136 -p 6379 -a 123456
- root 1580 0.0 0.0 112676 984 pts/1 R+ 02:03 0:00 grep --color=auto 6379
- [root@localhost bin]# kill -9 1572
- #重启服务
- [root@localhost bin]# ./redis-server ../etc/redis.conf
- [root@localhost bin]# ./redis-cli -h 192.168.245.136 -p 6379 -a "123456"
- 192.168.245.136:6379> get name
- "1"
- 192.168.245.136:6379>
- #可以看到我最后的是“5”,而这里是“1”,数据丢失了##########################
- #更改10次呢?,是成功进行持久化了的,这里不展示了,篇幅过大。
- #而且如果你将dump.rdb文件删除后,达到snapshot条件时,会自动创建一个新的文件,持久化其实就是将该文件备份,下次将那些持久化后的文件再放过来不就达到数据恢复了吗????????????
2.6.2 AOF存储
以日志文件方式存储,其实就是将你“操作+数据”指令格式化后追加到操作日志文件的尾部,必须append(已经写入到文件或者即将写入),才会进行数据的实际变更。“日志文件”保存了历史所有的操作过程;当 server 需要数据恢复时,可以直接 replay 此日志文件,即可还原所有的操作过程。内容是字符串,容易阅读和解析。
缺点:AOF 文件比 RDB 文件大,且恢复速度慢。
只会记录“变更操作”(例如:set/del 等),如果 server 中持续的大量变更操作,将会导致 AOF 文件非常的庞大,意味着 server 失效后,数据恢复的过程将会很长;事实上,一条数据经过多次变更,将会产生多条 AOF 记录,其实只要保存当前的状态,历史的操作记录是可以抛弃的;因为 AOF 持久化模式还伴生了“AOF rewrite”。
因为最多丢失最后一次写入文件的数据,所以很好修复,直接手工更改文件或者重新来一次即可。
修改redis.conf文件:
- ##此选项为aof功能的开关,默认为“no”,可以通过“yes”来开启aof功能
- ##只有在“yes”下,aof重写/文件同步等特性才会生效
- appendonly yes
- ##指定aof文件名称
- appendfilename appendonly.aof
- ##指定aof操作中文件同步策略,有三个合法值:always everysec no,默认为everysec每秒
- appendfsync everysec
- ##在aof-rewrite期间,appendfsync是否暂缓文件同步,"no"表示“不暂缓”,“yes”表示“暂缓”,默认为“no”
- no-appendfsync-on-rewrite no
- ##aof文件rewrite触发的最小文件尺寸(mb,gb),只有大于此aof文件大于此尺寸是才会触发rewrite,默认“64mb”,建议“512mb”
- auto-aof-rewrite-min-size 64mb
- ##相对于“上一次”rewrite,本次rewrite触发时aof文件应该增长的百分比。
- ##每一次rewrite之后,redis都会记录下此时“新aof”文件的大小(例如A),那么当aof文件增长到A*(1 + p)之后
- ##触发下一次rewrite,每一次aof记录的添加,都会检测当前aof文件的尺寸。
- auto-aof-rewrite-percentage 100
重启服务后,根据你启动redis所在目录下出现appendonly.aof文件。
执行以下操作:
- 192.168.245.136:6379> set name 124
- OK
- 192.168.245.136:6379> set name 145
- OK
- 192.168.245.136:6379> set age 56
- OK
- 192.168.245.136:6379> del age
- (integer) 1
- 192.168.245.136:6379> get name
- "145"
查看aof文件内容:
- [root@localhost bin]# cat appendonly.aof
- *2
- $6
- SELECT
- $1
- 0
- *3
- $3
- set
- $4
- name
- $3
- 124
- *3
- $3
- set
- $4
- 。。。
查询的是不会出现在里面,而且这个文件是动态的。。看起来挺好的,就是频繁的更改造成文件过大,到时候恢复起来有点太慢了,有人说那么RDB中将时间改快点要求改小点?大哥,人家是全量复制,那岂不是时间都花去复制了,还处理什么请求?这个虽然慢,可是是追加方式啊,将就了。
2.6.3 redis宕机后,值会失效吗?
不会,redis默认开启RDB存储,如果是直接关闭服务,那么会自动备份,如果是kill或者断电如果没达到RDB配置的要求则不会持久化,而且这个虽然是非阻塞的,但是毕竟全量复制啊,保证了redis的性能,但是CPU可受不了啊。因此实际情况中,最好采用AOP方式,实时而且快,但是就是容易造成文件过大,恢复困难。
2.7 redis事务
Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证:
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
multi 开启事务 exec提交事务。
序号 |
命令及描述 |
1 |
DISCARD |
2 |
EXEC |
3 |
MULTI |
4 |
UNWATCH |
5 |
WATCH key [key ...] |
2.8 发布订阅
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。Redis 客户端可以订阅任意数量的频道
下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
例子:
1、创建订阅频道名为 redisChatSimple
- 192.168.245.136:6379> subscribe redsiChatSample
- Reading messages... (press Ctrl-C to quit)
- 1) "subscribe"
- 2) "redsiChatSample"
2、重新开个客户端,同一频道发布消息
- 192.168.245.136:6379> publish redsiChatSample "gogog"
- (integer) 1
- 192.168.245.136:6379> publish redsiChatSample "gogog22"
- (integer) 1
- 192.168.245.136:6379>
3、客户端显示如下信息:
- 192.168.245.136:6379> subscribe redsiChatSample
- Reading messages... (press Ctrl-C to quit)
- 1) "subscribe"
- 2) "redsiChatSample"
- 3) (integer) 1
- 1) "message"
- 2) "redsiChatSample"
- 3) "gogog"
- 1) "message"
- 2) "redsiChatSample"
- 3) "gogog22"
厉害了!!!
序号 |
命令及描述 |
1 |
PSUBSCRIBE pattern [pattern ...] |
2 |
PUBSUB subcommand [argument [argument ...]] |
3 |
PUBLISH channel message |
4 |
PUNSUBSCRIBE [pattern [pattern ...]] |
5 |
SUBSCRIBE channel [channel ...] |
6 |
UNSUBSCRIBE [channel [channel ...]] |
Java缓存机制的更多相关文章
- Map实现java缓存机制的简单实例
缓存是Java中主要的内容,主要目的是缓解项目访问数据库的压力以及提升访问数据的效率,以下是通过Map实现java缓存的功能,并没有用cache相关框架. 一.缓存管理类 CacheMgr.java ...
- Java三大框架之——Hibernate中的三种数据持久状态和缓存机制
Hibernate中的三种状态 瞬时状态:刚创建的对象还没有被Session持久化.缓存中不存在这个对象的数据并且数据库中没有这个对象对应的数据为瞬时状态这个时候是没有OID. 持久状态:对象经过 ...
- Java 日志缓存机制的实现--转载
概述 日志技术为产品的质量和服务提供了重要的支撑.JDK 在 1.4 版本以后加入了日志机制,为 Java 开发人员提供了便利.但这种日志机制是基于静态日志级别的,也就是在程序运行前就需设定下来要打印 ...
- java框架篇---hibernate之缓存机制
一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数 ...
- java整形中的缓存机制
英文原文:Java Integer Cache 翻译地址:Java中整型的缓存机制 原文作者:Java Papers 翻译作者:Hollis 转载请注明出处. 本文将介绍Java中Integer的 ...
- java中字面量,常量和变量之间的区别(附:Integer缓存机制)
一.引子 在各种教科书和博客中这三者经常被引用,今天复习到内存区域,想起常量池中就是存着字面量和符号引用,其实这三者并不是只在java中才有,各个语言中都有类似的定义,所以做一下总结,以示区分. 二. ...
- java对象中的三种状态和脏检查及刷新缓存机制
瞬时状态 瞬时状态又称临时状态.如果java对象与数据库中的数据没有任何的关联,即此java对象在数据库中没有相关联的记录,此时java对象的状态为瞬时状态,session对于 瞬时状态的ava对象是 ...
- java中间变量缓存机制
public class Demo { public static void main(String[] args){ method_1(); method_2(); } private static ...
- 如何利用缓存机制实现JAVA类反射性能提升30倍
一次性能提高30倍的JAVA类反射性能优化实践 文章来源:宜信技术学院 & 宜信支付结算团队技术分享第4期-支付结算部支付研发团队高级工程师陶红<JAVA类反射技术&优化> ...
随机推荐
- Druid + spring 配置数据库连接池
1. Druid的简介 Druid是一个数据库连接池.Druid是目前最好的数据库连接池,在功能.性能.扩展性方面,都超过其他数据库连接池,包括DBCP.C3P0.BoneCP.Proxool.JBo ...
- layer.prompt绑定确认键
case 'eventkc': top.layer.prompt({ formType: , title: '修改<span style="color:red">' + ...
- vue-mixins和vue高阶组件
我们在开发过程中,因为需求的变更,往往会遇见对现有组件的改造和扩展. 那么我们有什么方法对现有组件进行改造和扩展呢? 常见的我们可以使用mixins方式 下面就让我们来看一下怎么使用mixins方式对 ...
- chrom插件1
本文是稀土掘金投稿,虽然其中有倔金的私货,是篇推广文,但我看过后认为内容确实不错,有些好插件还是第一次知道,对我很有帮助,考虑过后还是决定推荐给大家,最近我比较关注各种提高开发效率的工具与技巧,今后看 ...
- 苹果浏览器和ios中,时间字符串转换问题
背景:在开发PC端项目和小程序时,遇到过一个时间字符串转化问题,在苹果浏览器和ios微信客户端里,"2018-10-15 18:20" 以 字符"-"拼接的时间 ...
- Celery多队列配置
Celery多队列配置 Celery官方文档 项目结构 /proj -__init__ -app.py #实例化celery对象 -celeryconfig.py #celery的配置文件 -task ...
- Spring Boot整合Spring Security总结
一.创建Spring Boot项目 引入Thymeleaf和Web模块以及Spring Security模块方便进行测试,先在pom文件中将 spring-boot-starter-security ...
- 使用 gitolite 管理配置 git 库的权限
问题:每次创建git库都需要在gitolite的配置文件中添加git库的配置信息,为了方便管理git库,不在重复提交,所以修改gitolite的配置管理文件. 环境:ubuntu16.04 安装git ...
- 用python实现js语言里的特性
有大佬说:“搜 arraybuffer 的 polyfill 然后翻译成 python就行了” ...
- zabbix简单的操作(添加主机)
zabbix是一种监控软件,我用的是centos7.5版本 Zabbix是一个基于WEB界面的提供分布式监视功能的企业级的开源解决方案. Zabbix既可以监控操作系统(Linux/Windows/A ...