Java中要用到缓存的地方不少,首当其冲的就是持久层缓存,针对持久层谈一下:
要实现java缓存有不少种方式,最简单的无非就是static HashMap,这个显然是基于内存缓存,一个map就能够搞定引用对象的缓存,最简单也最不实用,首要的问题就是保存对象的有效性以及周期没法控制,这样很容易就致使内存急剧上升,周期没法控制能够采用SoftReference,WeakReference,PhantomReference这三种对象来执行(看了Ibatis的缓存机制才发现JDK竟然还提供了PhantomReference这玩意儿,得恶补基础啊),这三种都是弱引用,区别在于强度不一样,至于弱引用概念我的理解就是对象的生命周期与JVM挂钩,JVM内存不够了就回收,这样能很好的控制OutOfMemoryError 异常。 html
经常使用的有Oscache,Ehcache,Jcache,Jbosscache等等不少java
ehcache 主要是对数据库访问的缓存,相同的查询语句只需查询一次数据库,从而提升了查询的速度,使用spring的AOP能够很容易实现这一功能。
oscache 主要是对页面的缓存,能够整页或者指定网页某一部分缓存,同时指定他的过时时间,这样在此时间段里面访问的数据都是同样的。
NoSQL 是 Not Only SQL 的缩写,意即"不只仅是SQL"的意思,泛指非关系型的数据库。强调Key-Value Stores和文档数据库的优势,而不是单纯的反对RDBMS。mysql
NoSQL产品是传统关系型数据库的功能阉割版本,经过减小用不到或不多用的功能,来大幅度提升产品性能linux
NoSQL产品 Redis、mongodb Membase、HBase 程序员
Redis支持数据的持久化,能够将数据存放在硬盘上。web
Memcache不支持数据的之久存储。redis
Redis数据类型丰富,支持set liset等类型算法
Memcache支持简单数据类型,须要客户端本身处理复制对象spring
Redis 是彻底开源免费的,遵照BSD协议,是一个高性能的key-value数据库。sql
Redis 与其余 key - value 缓存产品有如下三个特色:
Redis支持数据的持久化,能够将内存中的数据保存在磁盘中,重启的时候能够再次加载进行使用。
Redis不只仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
Redis支持数据的备份,即master-slave模式的数据备份。
主要可以体现 解决数据库的访问压力。
例如:短信验证码时间有效期、session共享解决方案
性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操做。
原子 – Redis的全部操做都是原子性的,同时Redis还支持对几个操做全并后的原子性执行。
丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过时等等特性。
Redis有着更为复杂的数据结构而且提供对他们的原子性操做,这是一个不一样于其余数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
Redis运行在内存中可是能够持久化到磁盘,因此在对不一样数据集进行高速读写时须要权衡内存,由于数据量不能大于硬件内存。在内存数据库方面的另外一个优势是,相比在磁盘上相同的复杂的数据结构,在内存中操做起来很是简单,这样Redis能够作不少内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,由于他们并不须要进行随机访问。
新建start.bat 批处理文件、内容: redis-server.exe redis.windows.conf
双击start.bat启动
修改密码 # requirepass foobared 修改成requirepass 123456
注意:修改密码的时候前面不要加空格
Redis的官方下载网址是:http://redis.io/download (这里下载的是Linux版的Redis源码包)
Redis服务器端的默认端口是6379。
这里以虚拟机中的Linux系统如何安装Redis进行讲解。
在windows系统中下载好Redis的源码包。
1. 经过WinSCP工具,将Redis的源码包由windows上传到Linux系统的这个目录/opt/redis (即根目录下的lamp文件夹)。
2. 解压缩。
tar -zxf redis-2.6.17.tar.gz
3. 切换到解压后的目录。
cd redis-2.6.17 ( 通常来讲,解压目录里的INSTALL文件或README文件里写有安装说明,可参考之)
4. 编译。
make
(注意,编译须要C语言编译器gcc的支持,若是没有,须要先安装gcc。可使用rpm -q gcc查看gcc是否安装)
(利用yum在线安装gcc的命令 yum -y install gcc )
(若是编译出错,请使用make clean清除临时文件。以后,找到出错的缘由,解决问题后再来从新安装。 )
5. 进入到src目录。
cd src
6. 执行安装。
make install
到此就安装完成。可是,因为安装redis的时候,咱们没有选择安装路径,故是默认位置安装。在此,咱们能够将可执行文件和配置文件移动到习惯的目录。
cd /usr/local mkdir -p /usr/local/redis/bin mkdir -p /usr/local/redis/etc cd /lamp/redis-2.6.17 cp ./redis.conf /usr/local/redis/etc cd src cp mkreleasehdr.sh redis-benchmark redis-check-aof redis-check-dump redis-cli redis-server redis-sentinel /usr/local/redis/bin
7.开放linux 6379 端口
1.编辑 /etc/sysconfig/iptables 文件:
vi /etc/sysconfig/iptables
加入内容并保存:
-A RH-Firewall-1-INPUT -m state –state NEW -m tcp -p tcp –dport 6379 -j ACCEPT
2.重启服务:
/etc/init.d/iptables restart
3.查看端口是否开放:
/sbin/iptables -L -n
比较重要的3个可执行文件:
redis-server:Redis服务器程序 redis-cli:Redis客户端程序,它是一个命令行操做工具。也可使用telnet根据其纯文本协议操做。 redis-benchmark:Redis性能测试工具,测试Redis在你的系统及配置下的读写性能。
/usr/local/redis/bin/redis-server
或
cd /usr/local/redis/bin
./redis-server /usr/local/redis/etc/redis.conf 为redis-server指定配置文
daemonize yes --- 修改成yes 后台启动 requirepass 123456 ----注释取消掉设置帐号密码 ps aux | grep '6379' --- 查询端口 kill -15 9886 --- 杀死重置 kill -9 9886 --- 强制杀死 service iptables stop 中止防火墙
./redis-cli -h 127.0.0.1 -p 6379 -a "123456" --- redis 使用帐号密码链接 PING 结果表示成功
redis-cli shutdown 或者 kill redis进程的pid
service iptables stop
使用redisclient-win32.x86.1.5
redis 127.0.0.1:6379> SET mykey "redis" OK redis 127.0.0.1:6379> GET mykey "redis"
在上面的例子中,SET
和GET
是redis中的命令,而mykey
是键的名称。
Redis字符串命令用于管理Redis中的字符串值。如下是使用Redis字符串命令的语法。
redis 127.0.0.1:6379> COMMAND KEY_NAME
示例
redis 127.0.0.1:6379> SET mykey "redis" OK redis 127.0.0.1:6379> GET mykey "redis"
在上面的例子中,SET
和GET
是redis中的命令,而mykey
是键的名称。
Redis字符串命令
下表列出了一些用于在Redis中管理字符串的基本命令。
编号 |
命令 |
描述说明 |
1 |
此命令设置指定键的值。 |
|
2 |
获取指定键的值。 |
|
3 |
获取存储在键上的字符串的子字符串。 |
|
4 |
设置键的字符串值并返回其旧值。 |
|
5 |
返回在键处存储的字符串值中偏移处的位值。 |
|
6 |
获取全部给定键的值 |
|
7 |
存储在键上的字符串值中设置或清除偏移处的位 |
|
8 |
使用键和到期时间来设置值 |
|
9 |
设置键的值,仅当键不存在时 |
|
10 |
在指定偏移处开始的键处覆盖字符串的一部分 |
|
11 |
获取存储在键中的值的长度 |
|
12 |
为多个键分别设置它们的值 |
|
13 |
为多个键分别设置它们的值,仅当键不存在时 |
|
14 |
设置键的值和到期时间(以毫秒为单位) |
|
15 |
将键的整数值增长 |
|
16 |
将键的整数值按给定的数值增长 |
|
17 |
将键的浮点值按给定的数值增长 |
|
18 |
将键的整数值减 |
|
19 |
按给定数值减小键的整数值 |
|
20 |
将指定值附加到键 |
Redis列表是简单的字符串列表,按照插入顺序排序。你能够添加一个元素到列表的头部(左边)或者尾部(右边)
一个列表最多能够包含 232 - 1 个元素 (4294967295, 每一个列表超过40亿个元素)。
redis 127.0.0.1:6379> LPUSH runoobkey redis (integer) 1 redis 127.0.0.1:6379> LPUSH runoobkey mongodb (integer) 2 redis 127.0.0.1:6379> LPUSH runoobkey mysql (integer) 3 redis 127.0.0.1:6379> LRANGE runoobkey 0 10 1) "mysql" 2) "mongodb" 3) "redis"
Redis 列表命令
下表列出了列表相关的基本命令:
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 |
Redis的Set是string类型的无序集合。集合成员是惟一的,这就意味着集合中不能出现重复的数据。
Redis 中集合是经过哈希表实现的,因此添加,删除,查找的复杂度都是O(1)。
集合中最大的成员数为 232 - 1 (4294967295, 每一个集合可存储40多亿个成员)。
实例
redis 127.0.0.1:6379> SADD runoobkey redis (integer) 1 redis 127.0.0.1:6379> SADD runoobkey mongodb (integer) 1 redis 127.0.0.1:6379> SADD runoobkey mysql (integer) 1 redis 127.0.0.1:6379> SADD runoobkey mysql (integer) 0 redis 127.0.0.1:6379> SMEMBERS runoobkey 1) "mysql" 2) "mongodb" 3) "redis"
在以上实例中咱们经过 SADD 命令向名为 runoobkey 的集合插入的三个元素。
Redis 集合命令
下表列出了 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 |
Redis 有序集合和集合同样也是string类型元素的集合,且不容许重复的成员。
不一样的是每一个元素都会关联一个double类型的分数。redis正是经过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是惟一的,但分数(score)却能够重复。
集合是经过哈希表实现的,因此添加,删除,查找的复杂度都是O(1)。集合中最大的成员数为 232 - 1 (4294967295, 每一个集合可存储40多亿个成员)。
实例
redis 127.0.0.1:6379> ZADD runoobkey 1 redis (integer) 1 redis 127.0.0.1:6379> ZADD runoobkey 2 mongodb (integer) 1 redis 127.0.0.1:6379> ZADD runoobkey 3 mysql (integer) 1 redis 127.0.0.1:6379> ZADD runoobkey 3 mysql (integer) 0 redis 127.0.0.1:6379> ZADD runoobkey 4 mysql (integer) 0 redis 127.0.0.1:6379> ZRANGE runoobkey 0 10 WITHSCORES 1) "redis" 2) "1" 3) "mongodb" 4) "2" 5) "mysql" 6) "4"
在以上实例中咱们经过命令 ZADD 向 redis 的有序集合中添加了三个值并关联上分数。
Redis 有序集合命令
下表列出了 redis 有序集合的基本命令:
Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。
Redis 中每一个 hash 能够存储 232 - 1 键值对(40多亿)。
实例
127.0.0.1:6379> HMSET runoobkey name "redis tutorial" 127.0.0.1:6379> HGETALL runoobkey 1) "name" 2) "redis tutorial" 3) "description" 4) "redis basic commands for caching" 5) "likes" 6) "20" 7) "visitors" 8) "23000"
hset key mapHey MapValue
在以上实例中,咱们设置了 redis 的一些描述信息(name, description, likes, visitors) 到哈希表的 runoobkey 中。
Redis hash 命令
下表列出了 redis hash 基本的相关命令:
序号 |
命令及描述 |
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] |
package com.hongmoshui.test; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.junit.Before; import org.junit.Test; import redis.clients.jedis.Jedis; public class TestRedis { private Jedis jedis; @Before public void setup() { // 链接redis服务器,127.0.0.1:6379 jedis = new Jedis("127.0.0.1", 6379); // 权限认证 jedis.auth("123456"); } /** * redis存储字符串 */ @Test public void testString() { // -----添加数据---------- jedis.set("name", "xinxin");// 向key-->name中放入了value-->xinxin System.out.println(jedis.get("name"));// 执行结果:xinxin jedis.append("name", " is my lover"); // 拼接 System.out.println(jedis.get("name")); jedis.del("name"); // 删除某个键 System.out.println(jedis.get("name")); // 设置多个键值对 jedis.mset("name", "liuling", "age", "23", "qq", "476777XXX"); jedis.incr("age"); // 进行加1操做 System.out.println(jedis.get("name") + "-" + jedis.get("age") + "-" + jedis.get("qq")); } /** * redis操做Map */ @Test public void testMap() { // -----添加数据---------- Map<String, String> map = new HashMap<String, String>(); map.put("name", "xinxin"); map.put("age", "22"); map.put("qq", "123456"); jedis.hmset("user", map); // 取出user中的name,执行结果:[minxr]-->注意结果是一个泛型的List // 第一个参数是存入redis中map对象的key,后面跟的是放入map中的对象的key,后面的key能够跟多个,是可变参数 List<String> rsmap = jedis.hmget("user", "name", "age", "qq"); System.out.println(rsmap); // 删除map中的某个键值 jedis.hdel("user", "age"); System.out.println(jedis.hmget("user", "age")); // 由于删除了,因此返回的是null System.out.println(jedis.hlen("user")); // 返回key为user的键中存放的值的个数2 System.out.println(jedis.exists("user"));// 是否存在key为user的记录 // 返回true System.out.println(jedis.hkeys("user"));// 返回map对象中的全部key System.out.println(jedis.hvals("user"));// 返回map对象中的全部value Iterator<String> iter = jedis.hkeys("user").iterator(); while (iter.hasNext()) { String key = iter.next(); System.out.println(key + ":" + jedis.hmget("user", key)); } } /** * jedis操做List */ @Test public void testList() { // 开始前,先移除全部的内容 jedis.del("java framework"); System.out.println(jedis.lrange("java framework", 0, -1)); // 先向key java framework中存放三条数据 jedis.lpush("java framework", "spring"); jedis.lpush("java framework", "struts"); jedis.lpush("java framework", "hibernate"); // 再取出全部数据jedis.lrange是按范围取出, // 第一个是key,第二个是起始位置,第三个是结束位置,jedis.llen获取长度 // -1表示取得全部 System.out.println(jedis.lrange("java framework", 0, -1)); jedis.del("java framework"); jedis.rpush("java framework", "spring"); jedis.rpush("java framework", "struts"); jedis.rpush("java framework", "hibernate"); System.out.println(jedis.lrange("java framework", 0, -1)); } /** * jedis操做Set */ @Test public void testSet() { // 添加 jedis.sadd("user", "liuling"); jedis.sadd("user", "xinxin"); jedis.sadd("user", "ling"); jedis.sadd("user", "zhangxinxin"); jedis.sadd("user", "who"); // 移除noname jedis.srem("user", "who"); // 获取全部加入的value System.out.println(jedis.smembers("user")); // 判断who 是不是user集合的元素 System.out.println(jedis.sismember("user", "who")); System.out.println(jedis.srandmember("user")); // 返回集合的元素个数 System.out.println(jedis.scard("user")); } @Test public void test() throws InterruptedException { // jedis 排序 // 注意,此处的rpush和lpush是List的操做。是一个双向链表(但从表现来看的) jedis.del("a");// 先清除数据,再加入数据进行测试 jedis.rpush("a", "1"); jedis.lpush("a", "6"); jedis.lpush("a", "3"); jedis.lpush("a", "9"); System.out.println(jedis.lrange("a", 0, -1)); // [9,3, 6,1] System.out.println(jedis.sort("a")); // [1,3,6,9] // 输入排序后结果 System.out.println(jedis.lrange("a", 0, -1)); } // @Test // public void testRedisPool() // { // RedisUtil.getJedis().set("newname", "中文测试"); // System.out.println(RedisUtil.getJedis().get("newname")); // } }
Maven依赖jar包
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <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> <!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <!-- <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> --> </dependencies>
######################################################## ###Redis (RedisConfiguration) ######################################################## spring.redis.database=0 spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.password=123456 spring.redis.pool.max-idle=8 spring.redis.pool.min-idle=0 spring.redis.pool.max-active=8 spring.redis.pool.max-wait=-1 spring.redis.timeout=5000
package com.hongmoshui.service; import java.util.List; import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; @Service public class RedisService { @Autowired private StringRedisTemplate stringRedisTemplate; public void setObject(String key, Object value) { this.setObject(key, value, null); } public void setObject(String key, Object value, Long time) { if (StringUtils.isEmpty(key) || value == null) { return; } if (value instanceof String) { // 存放string类型 String stringValue = (String) value; if (time == null) { stringRedisTemplate.opsForValue().set(key, stringValue); } else { stringRedisTemplate.opsForValue().set(key, stringValue, time, TimeUnit.SECONDS); } return; } if (value instanceof List) { // 存放list類型 List<String> listValue = (List<String>) value; for (String string : listValue) { stringRedisTemplate.opsForList().leftPush(key, string); } } } public void delKey(String key) { stringRedisTemplate.delete(key); } public String getString(String key) { return stringRedisTemplate.opsForValue().get(key); } }
克隆三台linux虚拟机
redis主从复制
概述
一、redis的复制功能是支持多个数据库之间的数据同步。一类是主数据库(master)一类是从数据库(slave),主数据库能够进行读写操做,当发生写操做的时候自动将数据同步到从数据库,而从数据库通常是只读的,并接收主数据库同步过来的数据,一个主数据库能够有多个从数据库,而一个从数据库只能有一个主数据库。
二、经过redis的复制功能能够很好的实现数据库的读写分离,提升服务器的负载能力。主数据库主要进行写操做,而从数据库负责读操做。
主从复制过程
主从复制过程:见下图
过程:
1:当一个从数据库启动时,会向主数据库发送sync命令,
2:主数据库接收到sync命令后会开始在后台保存快照(执行rdb操做),并将保存期间接收到的命令缓存起来
3:当快照完成后,redis会将快照文件和全部缓存的命令发送给从数据库。
4:从数据库收到后,会载入快照文件并执行收到的缓存的命令。
修改从redis中的 redis.conf文件
slaveof 192.168.33.130 6379
masterauth 123456--- 主redis服务器配置了密码,则须要配置
Redis的哨兵(sentinel) 系统用于管理多个 Redis 服务器,该系统执行如下三个任务:
· 监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master和Slave是否运做正常。
· 提醒(Notification):当被监控的某个 Redis出现问题时, 哨兵(sentinel) 能够经过 API 向管理员或者其余应用程序发送通知。
· 自动故障迁移(Automatic failover):当一个Master不能正常工做时,哨兵(sentinel) 会开始一次自动故障迁移操做,它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其余Slave改成复制新的Master; 当客户端试图链接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可使用Master代替失效Master。
哨兵(sentinel) 是一个分布式系统,你能够在一个架构中运行多个哨兵(sentinel) 进程,这些进程使用流言协议(gossipprotocols)来接收关于Master是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪一个Slave做为新的Master.
每一个哨兵(sentinel) 会向其它哨兵(sentinel)、master、slave定时发送消息,以确认对方是否”活”着,若是发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂(所谓的”主观认为宕机” Subjective Down,简称sdown).
若“哨兵群”中的多数sentinel,都报告某一master没响应,系统才认为该master"完全死亡"(即:客观上的真正down机,Objective Down,简称odown),经过必定的vote算法,从剩下的slave节点中,选一台提高为master,而后自动修改相关配置.
虽然哨兵(sentinel) 释出为一个单独的可执行文件 redis-sentinel ,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你能够在启动一个普通 Redis 服务器时经过给定 --sentinel 选项来启动哨兵(sentinel).
哨兵(sentinel) 的一些设计思路和zookeeper很是相似
单个哨兵(sentinel)
实现步骤:
1.拷贝到etc目录
cp sentinel.conf /usr/local/redis/etc
2.修改sentinel.conf配置文件
sentinel monitor mymast 192.168.110.133 6379 1 #主节点 名称 IP 端口号 选举次数
3. 修改心跳检测 5000毫秒
sentinel down-after-milliseconds mymaster 5000
4.sentinel parallel-syncs mymaster 2 --- 作多多少合格节点
5. 启动哨兵模式
./redis-server /usr/local/redis/etc/sentinel.conf --sentinel &
6. 中止哨兵模式
Redis 事务能够一次执行多个命令, 而且带有如下两个重要的保证:
事务是一个单独的隔离操做:事务中的全部命令都会序列化、按顺序地执行。事务在执行的过程当中,不会被其余客户端发送来的命令请求所打断。
事务是一个原子操做:事务中的命令要么所有被执行,要么所有都不执行。
一个事务从开始到执行会经历如下三个阶段:
开始事务。
命令入队。
执行事务。
如下是一个事务的例子, 它先以 MULTI 开始一个事务, 而后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的全部命令:
redis 127.0.0.1:6379> MULTI OK redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days" QUEUED redis 127.0.0.1:6379> GET book-name QUEUED redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series" QUEUED redis 127.0.0.1:6379> SMEMBERS tag QUEUED redis 127.0.0.1:6379> EXEC 1) OK 2) "Mastering C++ in 21 days" 3) (integer) 3 4) 1) "Mastering Series" 2) "C++" 3) "Programming"
下表列出了 redis 事务的相关命
序号 命令及描述 1 DISCARD #取消事务,放弃执行事务块内的全部命令。 2 EXEC #执行全部事务块内的命令。 3 MULTI #标记一个事务块的开始。 4 UNWATCH #取消 WATCH 命令对全部 key 的监视。 5 WATCH key [key ...] #监视一个(或多个) key ,若是在事务执行以前这个(或这些) key 被其余命令所改动,那么事务将被打断。
什么是Redis持久化,就是将内存数据保存到硬盘。
Redis 持久化存储 (AOF 与 RDB 两种模式)
RDB 是在某个时间 点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。
优势:使用单独子进程来进行持久化,主进程不会进行任何 IO 操做,保证了 redis 的高性能
缺点:RDB 是间隔一段时间进行持久化,若是持久化之间 redis 发生故障,会发生数据丢失。因此这种方式更适合数据要求不严谨的时候
这里说的这个执行数据写入到临时文件的时间点是能够经过配置来本身肯定的,经过配置redis 在 n 秒内若是超过 m 个 key 被修改这执行一次 RDB 操做。这个操做就相似于在这个时间点来保存一次 Redis 的全部数据,一次快照数据。全部这个持久化方法也一般叫作 snapshots。
RDB 默认开启,redis.conf 中的具体配置参数以下;
#dbfilename:持久化数据存储在本地的文件 dbfilename dump.rdb #dir:持久化数据存储在本地的路径,若是是在/redis/redis-3.0.6/src下启动的redis-cli,则数据会存储在当前src目录下 dir ./ ##snapshot触发的时机,save ##以下为900秒后,至少有一个变动操做,才会snapshot ##对于此值的设置,须要谨慎,评估系统的变动操做密集程度 ##能够经过“save “””来关闭snapshot功能 #save时间,如下分别表示更改了1个key时间隔900s进行持久化存储;更改了10个key300s进行存储;更改10000个key60s进行存储。 save 900 1 save 300 10 save 60 10000 ##当snapshot时出现错误没法继续时,是否阻塞客户端“变动操做”,“错误”可能由于磁盘已满/磁盘故障/OS级别异常等 stop-writes-on-bgsave-error yes ##是否启用rdb文件压缩,默认为“yes”,压缩每每意味着“额外的cpu消耗”,同时也意味这较小的文件尺寸以及较短的网络传输时间 rdbcompression yes
Append-only file,将“操做 + 数据”以格式化指令的方式追加到操做日志文件的尾部,在 append 操做返回后(已经写入到文件或者即将写入),才进行实际的数据变动,“日志文件”保存了历史全部的操做过程;当 server 须要数据恢复时,能够直接 replay 此日志文件,便可还原全部的操做过程。AOF 相对可靠,它和 mysql 中 bin.log、apache.log、zookeeper 中 txn-log 简直殊途同归。AOF 文件内容是字符串,很是容易阅读和解析。
优势:能够保持更高的数据完整性,若是设置追加 file 的时间是 1s,若是 redis 发生故障,最多会丢失 1s 的数据;且若是日志写入不完整支持 redis-check-aof 来进行日志修复;AOF 文件没被 rewrite 以前(文件过大时会对命令进行合并重写),能够删除其中的某些命令(好比误操做的 flushall)。
缺点:AOF 文件比 RDB 文件大,且恢复速度慢。
咱们能够简单的认为 AOF 就是日志文件,此文件只会记录“变动操做”(例如:set/del 等),若是 server 中持续的大量变动操做,将会致使 AOF 文件很是的庞大,意味着 server 失效后,数据恢复的过程将会很长;事实上,一条数据通过屡次变动,将会产生多条 AOF 记录,其实只要保存当前的状态,历史的操做记录是能够抛弃的;由于 AOF 持久化模式还伴生了“AOF rewrite”。
AOF 的特性决定了它相对比较安全,若是你指望数据更少的丢失,那么能够采用 AOF 模式。若是 AOF 文件正在被写入时忽然 server 失效,有可能致使文件的最后一次记录是不完整,你能够经过手工或者程序的方式去检测并修正不完整的记录,以便经过 aof 文件恢复可以正常;同时须要提醒,若是你的 redis 持久化手段中有 aof,那么在 server 故障失效后再次启动前,须要检测 aof 文件的完整性。
AOF 默认关闭,开启方法,修改配置文件 reds.conf:appendonly yes
##此选项为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
AOF 是文件操做,对于变动操做比较密集的 server,那么必将形成磁盘 IO 的负荷加剧;此外 linux 对文件操做采起了“延迟写入”手段,即并不是每次 write 操做都会触发实际磁盘操做,而是进入了 buffer 中,当 buffer 数据达到阀值时触发实际写入(也有其余时机),这是 linux 对文件系统的优化,可是这却有可能带来隐患,若是 buffer 没有刷新到磁盘,此时物理机器失效(好比断电),那么有可能致使最后一条或者多条 aof 记录的丢失。经过上述配置文件,能够得知 redis 提供了 3 中 aof 记录同步选项:
always:每一条 aof 记录都当即同步到文件,这是最安全的方式,也觉得更多的磁盘操做和阻塞延迟,是 IO 开支较大。
everysec:每秒同步一次,性能和安全都比较中庸的方式,也是 redis 推荐的方式。若是遇到物理服务器故障,有可能致使最近一秒内 aof 记录丢失(可能为部分丢失)。
no:redis 并不直接调用文件同步,而是交给操做系统来处理,操做系统能够根据 buffer 填充状况 / 通道空闲时间等择机触发同步;这是一种普通的文件操做方式。性能较好,在物理服务器故障时,数据丢失量会因 OS 配置有关。
其实,咱们能够选择的太少,everysec 是最佳的选择。若是你很是在乎每一个数据都极其可靠,建议你选择一款“关系性数据库”吧。
AOF 文件会不断增大,它的大小直接影响“故障恢复”的时间, 并且 AOF 文件中历史操做是能够丢弃的。AOF rewrite 操做就是“压缩”AOF 文件的过程,固然 redis 并无采用“基于原 aof 文件”来重写的方式,而是采起了相似 snapshot 的方式:基于 copy-on-write,全量遍历内存中数据,而后逐个序列到 aof 文件中。所以 AOF rewrite 可以正确反应当前内存数据的状态,这正是咱们所须要的;*rewrite 过程当中,对于新的变动操做将仍然被写入到原 AOF 文件中,同时这些新的变动操做也会被 redis 收集起来(buffer,copy-on-write 方式下,最极端的多是全部的 key 都在此期间被修改,将会耗费 2 倍内存),当内存数据被所有写入到新的 aof 文件以后,收集的新的变动操做也将会一并追加到新的 aof 文件中,此后将会重命名新的 aof 文件为 appendonly.aof, 此后全部的操做都将被写入新的 aof 文件。若是在 rewrite 过程当中,出现故障,将不会影响原 AOF 文件的正常工做,只有当 rewrite 完成以后才会切换文件,由于 rewrite 过程是比较可靠的。*
触发 rewrite 的时机能够经过配置文件来声明,同时 redis 中能够经过 bgrewriteaof 指使人工干预。
redis-cli -h ip -p port bgrewriteaof
由于 rewrite 操做 /aof 记录同步 /snapshot 都消耗磁盘 IO,redis 采起了“schedule”策略:不管是“人工干预”仍是系统触发,snapshot 和 rewrite 须要逐个被执行。
AOF rewrite 过程并不阻塞客户端请求。系统会开启一个子进程来完成。
OF 和 RDB 各有优缺点,这是有它们各自的特色所决定:
1) AOF 更加安全,能够将数据更加及时的同步到文件中,可是 AOF 须要较多的磁盘 IO 开支,AOF 文件尺寸较大,文件内容恢复数度相对较慢。
*2) snapshot,安全性较差,它是“正常时期”数据备份以及 master-slave 数据同步的最佳手段,文件尺寸较小,恢复数度较快。
能够经过配置文件来指定它们中的一种,或者同时使用它们(不建议同时使用),或者所有禁用,在架构良好的环境中,master 一般使用 AOF,slave 使用 snapshot,主要缘由是 master 须要首先确保数据完整性,它做为数据备份的第一选择;slave 提供只读服务(目前 slave 只能提供读取服务),它的主要目的就是快速响应客户端 read 请求;可是若是你的 redis 运行在网络稳定性差 / 物理环境糟糕状况下,建议你 master 和 slave 均采起 AOF,这个在 master 和 slave 角色切换时,能够减小“人工数据备份”/“人工引导数据恢复”的时间成本;若是你的环境一切很是良好,且服务须要接收密集性的 write 操做,那么建议 master 采起 snapshot,而 slave 采用 AOF。
Redis 发布订阅(pub/sub)是一种消息通讯模式:发送者(pub)发送消息,订阅者(sub)接收消息。
Redis 客户端能够订阅任意数量的频道。
下图展现了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
当有新消息经过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
如下实例演示了发布订阅是如何工做的。在咱们实例中咱们建立了订阅频道名为 redisChat:
redis 127.0.0.1:6379> SUBSCRIBE redisChat Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "redisChat" 3) (integer) 1
如今,咱们先从新开启个 redis 客户端,而后在同一个频道 redisChat 发布两次消息,订阅者就能接收到消息。
redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique" (integer) 1 redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis by runoob.com" (integer) 1 # 订阅者的客户端会显示以下消息 1) "message" 2) "redisChat" 3) "Redis is a great caching technique" 1) "message" 2) "redisChat" 3) "Learn redis by runoob.com"
发布订阅命令
下表列出了 redis 发布订阅经常使用命令:
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 ...]] |