赞
踩
HBase是Hadoop项目中的数据库。它用于需要对大量的数据进行随机、实时读写操作的场景中。HBase的目标就是处理数据量非常庞大的表,可以用普通的计算机处理超过10亿行数据,还可处理有数百万列元素的数据表。
redis官网地址:
https://redis.io/
中文网站
http://www.redis.cn/
比如典型的取网站最新文章,可以将最新的5000条评论ID放在Redis的List集合中,并将超出集合部分从数据库获取
这个需求与上面需求的不同之处在于,前面操作以时间为权重,这个是以某个条件为权重,比如按顶的次数排序,可以使用Redis的sorted set,将要排序的值设置成sorted set的score,将具体的数据设置成相应的value,每次只需要执行一条ZADD命令即可
比如可以把上面说到的sorted set的score值设置成过期时间的时间戳,那么就可以简单地通过过期时间排序,定时清除过期数据了,不仅是清除Redis中的过期数据,你完全可以把Redis里这个过期时间当成是对数据库中数据的索引,用Redis来找出哪些数据需要过期删除,然后再精准地从数据库中删除相应的记录。
Redis的命令都是原子性的,你可以轻松地利用INCR,DECR命令来构建计数器系统。
这个使用Redis的set数据结构最合适了,只需要不断地将数据往set中扔就行了,set意为集合,所以会自动排重。
通过上面说到的set功能,你可以知道一个终端用户是否进行了某个操作,可以找到其操作的集合并进行分析统计对比等。没有做不到,只有想不到。
将数据直接存放到内存中,性能优于Memcached,数据结构更多样化。
高效性
原子性
支持多种数据结构类型,操作非常灵活
稳定性:持久化,主从复制(集群)
其他特性:支持过期时间,支持事务,消息订阅。
bigdata-pro-m07服务器执行以下命令下载redis安装包
cd /opt/software
wget http://download.redis.io/releases/redis-3.2.8.tar.gz
chmod u+x redis-3.2.8.tar.gz
tar -zxvf redis-3.2.8.tar.gz -C /opt/modules/
yum -y install gcc-c++
cd /opt/software wget http://downloads.sourceforge.net/tcl/tcl8.6.1-src.tar.gz chmod u+x tcl8.6.1-src.tar.gz # 解压tcl tar -zxvf tcl8.6.1-src.tar.gz -C /opt/modules/ # 进入指定目录 cd /opt/modules/tcl8.6.1/unix # 进入root su ./configure make && make install
yum -y install tcl
cd /opt/modules/redis-3.2.8
#或者使用命令 make 进行编译
make MALLOC=libc
make test && make install PREFIX=/opt/modules/redis-3.2.8
cd /opt/modules/redis-3.2.8
mkdir log
mkdir data
vim redis.conf
# 修改第61行
bind node1.itcast.cn
# 修改第128行
daemonize yes
# 修改第163行
logfile "/opt/modules/redis-3.2.8/log/redis.log"
# 修改第247行
dir /opt/modules/redis-3.2.8/data
cd /opt/modules/redis-3.2.8/
bin/redis-server redis.conf
bin/redis-cli -h bigdata-pro-m07 shutdown
注意:
bin/redis-cli -h bigdata-pro-m07
redis当中一共支持五种数据类型,分别是:
通过这五种不同的数据类型,可以实现各种不同的功能,也可以应用在各种不同的场景。
Redis当中各种数据类型结构如上图:
Redis当中各种数据类型的操作
https://www.runoob.com/redis/redis-keys.html
下表列出了常用的 redis 字符串命令
命令及描述 | 示例 |
---|---|
SET key value 设置指定 key 的值 | 示例:SET hello world |
GET key 获取指定 key 的值 | 示例:GET hello |
GETSET key value将给定 key 的值设为 value ,并返回 key 的旧值(old value) | 示例:GETSET hello world2 |
MGET key1 [key2…]获取所有(一个或多个)给定 key 的值 | 示例:MGET hello world |
SETEX key seconds value将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位) | 示例:SETEX hello 10 world3 |
SETNX key value只有在 key 不存在时设置 key 的值 | 示例:SETNX itcast redisvalue |
STRLEN key返回 key 所储存的字符串值的长度 | 示例:STRLEN itcast |
MSET key value [key value …]同时设置一个或多个 key-value 对 | 示例:MSET itcast2 itcastvalue2 itcast3 itcastvalue3 |
MSETNX key value [key value …] 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在 | 示例:MSETNX itcast4 itcastvalue4 itcast5 itcastvalue5 |
PSETEX key milliseconds value 这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。 | 示例:PSETEX itcast6 6000 itcast6value |
INCR key 将 key 中储存的数字值增一。 | 示例:SET itcast7 1 、INCR itcast7 、GET itcast7 |
INCRBY key increment将 key 所储存的值加上给定的增量值(increment) | 示例:INCRBY itcast7 2、GET itcast7 |
INCRBYFLOAT key increment 将 key 所储存的值加上给定的浮点增量值(increment) | 示例:INCRBYFLOAT itcast7 0.8 |
DECR key 将 key 中储存的数字值减一。 | 示例:SET itcast8 1、DECR itcast8、GET itcast8 |
DECRBY key decrement key 所储存的值减去给定的减量值(decrement) | 示例:DECRBY itcast8 3 |
APPEND key value如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾。 | 示例:APPEND itcast8 hello |
注意:
Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。
Redis 中每个 hash 可以存储 2的32次方 - 1 键值对(40多亿)
下表列出了 redis hash 基本的相关命令:
命令及描述 | 示例 |
---|---|
HSET key field value 将哈希表 key 中的字段 field 的值设为 value 。 | 示例:HSET key1 field1 value1 |
HSETNX key field value 只有在字段 field 不存在时,设置哈希表字段的值。 | 示例:HSETNX key1 field2 value2 |
HMSET key field1 value1 [field2 value2 ] 同时将多个 field-value (域-值)对设置到哈希表 key 中。 | 示例:HMSET key1 field3 value3 field4 value4 |
HEXISTS key field 查看哈希表 key 中,指定的字段是否存在。 | 示例:HEXISTS key1 field4、HEXISTS key1 field6 |
HGET key field 获取存储在哈希表中指定字段的值。 | 示例:HGET key1 field4 |
HGETALL key 获取在哈希表中指定 key 的所有字段和值 | 示例:HGETALL key1 |
HKEYS key 获取所有哈希表中的字段 | 示例:HKEYS key1 |
HLEN key 获取哈希表中字段的数量 | 示例:HLEN key1 |
HMGET key field1 [field2] 获取所有给定字段的值 | 示例:HMGET key1 field3 field4 |
HINCRBY key field increment 为哈希表 key 中的指定字段的整数值加上增量 increment 。 | 示例:HSET key2 field1 1、HINCRBY key2 field1 1、HGET key2 field1 |
HINCRBYFLOAT key field increment 为哈希表 key 中的指定字段的浮点数值加上增量 increment 。 | 示例:HINCRBYFLOAT key2 field1 0.8 |
HVALS key 获取哈希表中所有值 | 示例:HVALS key1 |
HDEL key field1 [field2] | |
删除一个或多个哈希表字段 | 示例:HDEL key1 field3 、HVALS key1 |
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
一个列表最多可以包含 2的32次方 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。
下表列出了列表相关的基本命令:
命令及描述 | 示例 |
---|---|
LPUSH key value1 [value2] 将一个或多个值插入到列表头部 | 示例:LPUSH list1 value1 value2 |
LRANGE key start stop 查看list当中所有的数据 | 示例:LRANGE list1 0 -1 |
LPUSHX key value 将一个值插入到已存在的列表头部 | 示例:LPUSHX list1 value3、LINDEX list1 0 |
RPUSH key value1 [value2] 在列表中添加一个或多个值到尾部 | 例:RPUSH list1 value4 value5、LRANGE list1 0 -1 |
RPUSHX key value 为已存在的列表添加单个值到尾部 | 示例:RPUSHX list1 value6 |
LINSERT key BEFORE /AFTER pivot value 在列表的元素前或者后插入元素 | 示例:LINSERT list1 BEFORE value3 beforevalue3 |
LINDEX key index 通过索引获取列表中的元素 | 示例:LINDEX list1 0 |
LSET key index value 通过索引设置列表元素的值 | 示例:LSET list1 0 hello |
LLEN key 获取列表长度 | 示例:LLEN list1 |
LPOP key 移出并获取列表的第一个元素 | 示例:LPOP list1 |
RPOP key 移除列表的最后一个元素,返回值为移除的元素。 | 示例:RPOP list1 |
BLPOP key1 [key2 ] timeout 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 | 示例:BLPOP list1 2000 |
BRPOP key1 [key2 ] timeout 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 | 示例:BRPOP list1 2000 |
RPOPLPUSH source destination 移除列表的最后一个元素,并将该元素添加到另一个列表并返回 | 示例:RPOPLPUSH list1 list2 |
BRPOPLPUSH source destination timeout 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 | 示例:BRPOPLPUSH list1 list2 2000 |
LTRIM key start stop 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 | 示例:LTRIM list1 0 2 |
DEL key1 key2 删除指定key的列表 | 示例:DEL list2 |
下表列出了 Redis 集合基本命令:
命令及描述 | 示例 |
---|---|
SADD key member1 [member2] 向集合添加一个或多个成员 | 示例:SADD set1 setvalue1 setvalue2 |
SMEMBERS key 返回集合中的所有成员 | 示例:SMEMBERS set1 |
SCARD key 获取集合的成员数 | 示例:SCARD set1 |
SDIFF key1 [key2] 返回给定所有集合的差集 | 示例:SADD set2 setvalue2 setvalue3、SDIFF set1 set2 |
SDIFFSTORE destination key1 [key2] 返回给定所有集合的差集并存储在 destination 中 | 示例:SDIFFSTORE set3 set1 set2 |
SINTER key1 [key2] 返回给定所有集合的交集 | 示例:SINTER set1 set2 |
SINTERSTORE destination key1 [key2] 返回给定所有集合的交集并存储在 destination 中 | 示例:SINTERSTORE set4 set1 set2 |
SISMEMBER key member 判断 member 元素是否是集合 key 的成员 | 示例:SISMEMBER set1 setvalue1 |
SMOVE source destination member 将 member 元素从 source 集合移动到 destination 集合 | 示例:SMOVE set1 set2 setvalue1 |
SPOP key 移除并返回集合中的一个随机元素 | 示例:SPOP set2 |
SRANDMEMBER key [count] 返回集合中一个或多个随机数 | 示例:SRANDMEMBER set2 2 |
SREM key member1 [member2] 移除集合中一个或多个成员 | 示例:SREM set2 setvalue1 |
SUNION key1 [key2] 返回所有给定集合的并集 | 示例:SUNION set1 set2 |
SUNIONSTORE destination key1 [key2] 所有给定集合的并集存储在 destination 集合中 | 示例:SUNIONSTORE set5 set1 set2 |
下表给出了与 Redis 键相关的基本命令:
命令及描述 | 示例 |
---|---|
DEL key 该命令用于在 key 存在时删除 key。 | 示例:del itcast5 |
DUMP key | |
序列化给定 key ,并返回被序列化的值。 | 示例:DUMP key1 |
EXISTS key 检查给定 key 是否存在。 | 示例:exists itcast |
EXPIRE key seconds 为给定 key 设置过期时间,以秒计。 | 示例:expire itcast 5 |
PEXPIRE key milliseconds 设置 key 的过期时间以毫秒计。 | 示例:PEXPIRE set3 3000 |
KEYS pattern 查找所有符合给定模式( pattern)的 key | 示例:keys * |
PERSIST key 移除 key 的过期时间,key 将持久保持。 | 示例:persist set2 |
PTTL key 以毫秒为单位返回 key 的剩余的过期时间。 | 示例:pttl set2 |
TTL key 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。 | 示例:ttl set2 |
RANDOMKEY 从当前数据库中随机返回一个 key 。 | 示例: randomkey |
RENAME key newkey 修改 key 的名称 | 示例:rename set5 set8 |
RENAMENX key newkey 仅当 newkey 不存在时,将 key 改名为 newkey 。 | 示例:renamenx set8 set10 |
TYPE key 返回 key 所储存的值的类型。 | 示例:type set10 |
下表列出了列表相关的基本命令:
命令及描述 | 示例 |
---|---|
ZADD key score1 member1 [score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数 | 向ZSet中添加页面的PV值 ZADD pv_zset 120 page1.html 100 page2.html 140 page3.html |
ZCARD key 获取有序集合的成员数 | 获取所有的统计PV页面数量 ZCARD pv_zset |
ZCOUNT key min max 计算在有序集合中指定区间分数的成员数 | 获取PV在120-140在之间的页面数量 ZCOUNT pv_zset 120 140 |
ZINCRBY key increment member 有序集合中对指定成员的分数加上增量 increment | 给page1.html的PV值+1 ZINCRBY pv_zset 1 page1.html |
ZINTERSTORE destination numkeys key [key …] 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中 | 创建两个保存PV的ZSET:ZADD pv_zset1 10 page1.html 20 page2.html、ZADD pv_zset2 5 page1.html 10 page2.html、ZINTERSTORE pv_zset_result 2 pv_zset1 pv_zset2 |
ZRANGE key start stop [WITHSCORES] 通过索引区间返回有序集合指定区间内的成员 | 获取所有的元素,并可以返回每个key对一个的score ZRANGE pv_zset_result 0 -1 WITHSCORES |
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] 通过分数返回有序集合指定区间内的成员 | 获取ZSET中120-140之间的所有元素 ZRANGEBYSCORE pv_zset 120 140 |
ZRANK key member 返回有序集合中指定成员的索引 | 获取page1.html的pv排名(升序) ZRANK pv_zset page3.html |
ZREM key member [member …] 移除有序集合中的一个或多个成员 | 移除page1.html ZREM pv_zset page1.html |
ZREVRANGE key start stop [WITHSCORES] 返回有序集中指定区间内的成员,通过索引,分数从高到低 | 按照PV降序获取页面 ZREVRANGE pv_zset 0 -1 |
ZREVRANK key member 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序 | 获取page2.html的pv排名(降序) ZREVRANK pv_zset page2.html |
ZSCORE key member 返回有序集中,成员的分数值 | 获取page3.html的分数值 ZSCORE pv_zset page3.html |
SETBIT key offset value
setbit命令设置的vlaue只能是0或1两个值
setbit unique:users:2016-04-05 0 1
setbit unique:users:2016-04-05 5 1
setbit unique:users:2016-04-05 11 1
setbit unique:users:2016-04-05 15 1
setbit unique:users:2016-04-05 19 1
GETBIT key offset
getbit unique:users:2016-04-05 8
BITCOUNT key [start end]
例:下面操作计算2016-04-05这天的独立访问用户数量:
bitcount unique:users:2016-04-05
BITOP operation destkey key [key, …]
setbit unique:users:2016-04-04 1 1
setbit unique:users:2016-04-04 2 1
setbit unique:users:2016-04-04 5 1
setbit unique:users:2016-04-04 9 1
例1:下面操作计算出2016-04-04和2016-04-05两天都访问过网站的用户数量, 如下所示。
bitop and unique:users:and:2016-04-04_05 unique:users:2016-04-04 unique:users:2016-04-05
bitcount unique:users:2016-04-04_05
例2:如果想算出2016-04-04和2016-04-03任意一天都访问过网站的用户数量(例如月活跃就是类似这种) , 可以使用or求并集, 具体命令如下:
bitop or unique:users:or:2016-04-04_05 unique:users:2016-04-04 unique:users:2016-04-05
bitcount unique:users:or:2016-04-04_05
HyperLogLog常用于大数据量的统计,比如页面访问量统计或者用户访问量统计。
要统计一个页面的访问量(PV),可以直接用redis计数器或者直接存数据库都可以实现,如果要统计一个页面的用户访问量(UV),一个用户一天内如果访问多次的话,也只能算一次,这样,我们可以使用SET集合来做,因为SET集合是有去重功能的,key存储页面对应的关键字,value存储对应的userid,这种方法是可行的。但如果访问量较多,假如有几千万的访问量,这就麻烦了。为了统计访问量,要频繁创建SET集合对象。
Redis实现HyperLogLog算法,HyperLogLog 这个数据结构的发明人 是Philippe Flajolet(菲利普·弗拉若莱)教授。Redis 在 2.8.9 版本添加了 HyperLogLog 结构。
bigdata-pro-m07:6379> help @hyperloglog
PFADD key element [element ...]
summary: Adds the specified elements to the specified HyperLogLog.
since: 2.8.9
PFCOUNT key [key ...]
summary: Return the approximated cardinality of the set(s) observed by the HyperLogLog at key(s).
since: 2.8.9
PFMERGE destkey sourcekey [sourcekey ...]
summary: Merge N different HyperLogLogs into a single one.
since: 2.8.9
Redis集成的HyperLogLog使用语法主要有pfadd和pfcount,顾名思义,一个是来添加数据,一个是来统计的。为什么用pf?是因为HyperLogLog 这个数据结构的发明人 是Philippe Flajolet教授 ,所以用发明人的英文缩写,这样容易记住这个语法了。
下面我们通过一个示例,来演示如何计算uv。
bigdata-pro-m07:6379> PFADD uv1 user1 (integer) 1 bigdata-pro-m07:6379> pfcount uv1 (integer) 1 bigdata-pro-m07:6379> pfadd uv1 user2 (integer) 1 bigdata-pro-m07:6379> pfcount uv1 (integer) 2 bigdata-pro-m07:6379> pfadd uv1 user3 (integer) 1 bigdata-pro-m07:6379> pfcount uv1 (integer) 3 bigdata-pro-m07:6379> pfadd uv1 user4 (integer) 1 bigdata-pro-m07:6379> pfcount uv1 (integer) 4 bigdata-pro-m07:6379> pfadd uv1 user5 user6 user7 user8 user9 user10 (integer) 1 bigdata-pro-m07:6379> pfcount uv1 (integer) 10 bigdata-pro-m07:6379> pfadd uv1 user5 user6 user7 user8 user9 user1 (integer) 0 bigdata-pro-m07:6379> pfcount uv1 (integer) 10
HyperLogLog算法一开始就是为了大数据量的统计而发明的,所以很适合那种数据量很大,然后又没要求不能有一点误差的计算,HyperLogLog 提供不精确的去重计数方案,虽然不精确但是也不是非常不精确,标准误差是 0.81%,不过这对于页面用户访问量是没影响的,因为这种统计可能是访问量非常巨大,但是又没必要做到绝对准确,访问量对准确率要求没那么高,但是性能存储方面要求就比较高了,而HyperLogLog正好符合这种要求,不会占用太多存储空间,同时性能不错
pfadd和pfcount常用于统计,需求:假如两个页面很相近,现在想统计这两个页面的用户访问量呢?这里就可以用pfmerge合并统计了,语法如例子:
bigdata-pro-m07:6379> pfadd page1 user1 user2 user3 user4 user5
(integer) 1
bigdata-pro-m07:6379> pfadd page2 user1 user2 user3 user6 user7
(integer) 1
bigdata-pro-m07:6379> pfmerge page1+page2 page1 page2
OK
bigdata-pro-m07:6379> pfcount page1+page2
(integer) 7
什么是基数?
比如:数据集{1, 3, 5, 7, 5, 7, 8},那么这个数据集的基数集{1, 3, 5, 7, 8},基数(不重复元素)为5。基数估计就是在误差可接受的范围内,快速计算基数。
Redis不仅可以通过命令行进行操作,也可以通过JavaAPI操作,通过使用Java API来对Redis数据库中的各种数据类型操作。
离线架构和实时架构流程:
groupId | cn.itcast |
---|---|
artifactId | redis_op |
<dependencies> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>6.14.3</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> <!-- <verbal>true</verbal>--> </configuration> </plugin> </plugins> </build>
因为后续测试都需要用到Redis连接,所以,我们先创建一个JedisPool用于获取Redis连接。此处,我们基于TestNG来测试各类的API。使用@BeforeTest在执行测试用例前,创建Redis连接池。使用@AfterTest在执行测试用例后,关闭连接池。
实现步骤:
package cn.itcast.redis.api_test; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import java.util.List; import java.util.Set; /** * @author :caizhengjie * @description: * 1. 创建JedisPoolConfig配置对象,指定最大空闲连接为10个、最大等待时间为3000毫秒、最大连接数为50、最小空闲连接5个 * 2. 创建JedisPool * 3. 使用@Test注解,编写测试用例,查看Redis中所有的key * a) 从Redis连接池获取Redis连接 * b) 调用keys方法获取所有的key * c) 遍历打印所有key * @date :2021/1/26 11:28 上午 */ public class RedisTest { private JedisPool jedisPool; @BeforeTest public void redisConnectionPool(){ // 创建JedisPoolConfig配置对象 JedisPoolConfig config = new JedisPoolConfig(); // 指定最大空闲连接为10个 config.setMaxIdle(10); // 最小空闲连接5个 config.setMinIdle(5); // 最大等待时间为3000毫秒 config.setMaxWaitMillis(3000); // 最大连接数为50 config.setMaxTotal(50); jedisPool = new JedisPool(config,"bigdata-pro-m07"); } @Test public void keysTest(){ // 从redis连接池获取redis连接 Jedis jedis = jedisPool.getResource(); // 调用keys方法获取所有的key Set<String> keySet = jedis.keys("*"); for (String key : keySet){ System.out.println(key); } } @AfterTest public void afterTest(){ // 关闭连接池 jedisPool.close(); } }
注意:
/** * 操作string类型数据 */ @Test public void stringTest(){ // 从redis连接池获取redis连接 Jedis jedis = jedisPool.getResource(); // 1.添加一个string数据类型,key为pv,用于保存pv的值,初始值为0 jedis.set("pv","0"); // 2.查询key对应的数据 System.out.println("pv:"+jedis.get("pv")); // 3.修改pv为1000 jedis.set("pv","1000"); // 4.实现整形数据原子自增操作+1 jedis.incr("pv"); // 5.实现整形数据原子自增操作+1000 jedis.incrBy("pv",1000); System.out.println(jedis.get("pv")); }
注意:
/** * 操作hash列表类型数据 */ @Test public void hashTest(){ // 从redis连接池获取redis连接 Jedis jedis = jedisPool.getResource(); // 1.往Hash结构中添加以下商品库存 // (a)iPhone11 => 10000 // (b)MacBookPro => 9000 jedis.hset("goods","iPhone11","10000"); jedis.hset("goods","MacBookPro","9000"); // 2.获取Hash中所有的商品 Set<String> goodSet = jedis.hkeys("goods"); System.out.println("所有商品:"); for (String good : goodSet) { System.out.println(good); } // 3.新增3000个MacBookPro库存 // String storeMacBook = jedis.hget("goods","MacBookPro"); // long longStore = Long.parseLong(storeMacBook); // long addStore = longStore + 3000; // jedis.hset("goods","MacBookPro",addStore + ""); jedis.hincrBy("goods","MacBookPro",3000); // 4.删除整个Hash的数据 jedis.del("goods"); jedis.close(); }
注意:
/** * 操作list类型数据 */ @Test public void listTest(){ // 从redis连接池获取redis连接 Jedis jedis = jedisPool.getResource(); // 1.向list的左边插入以下三个手机号:18511310001、18511310002、18511310003 jedis.lpush("tel_list","18511310001","18511310002","18511310003"); // 2.从右面移除一个手机号码 jedis.rpop("tel_list"); // 3.获取list所有的值 List<String> telList = jedis.lrange("tel_list",0,-1); for (String tel : telList) { System.out.println(tel); } }
注意:
使用set来保存uv值,为了方便计算,将用户名保存到uv中。
/** * 操作set类型的数据 */ @Test public void setTest(){ // 从redis连接池获取redis连接 Jedis jedis = jedisPool.getResource(); // 求UV就是求独立有多少个不重复 // 1.往一个set中添加页面page1的uv,用户user1访问一次该页面 jedis.sadd("uv","user1"); // 2.user2访问一次该页面 jedis.sadd("uv","user2"); // 3.user1访问一次该页面 jedis.sadd("uv","user1"); // 最后获取page1的uv值 System.out.println("uv:" + jedis.scard("uv")); jedis.close(); }
注意:
由于redis是一个内存数据库,所有的数据都是保存在内存当中的,内存当中的数据极易丢失,所以redis的数据持久化就显得尤为重要,在redis当中,提供了两种数据持久化的方式,分别为RDB以及AOF,且Redis默认开启的数据持久化方式为RDB方式。
Redis会定期保存数据快照至一个rdb文件中,并在启动时自动加载rdb文件,恢复之前保存的数据。可以在配置文件中配置Redis进行快照保存的时机:
save [seconds] [changes]
意为在seconds秒内如果发生了changes次数据修改,则进行一次RDB快照保存,例如
save 60 100
会让Redis每60秒检查一次数据变更情况,如果发生了100次或以上的数据变更,则进行RDB快照保存。可以配置多条save指令,让Redis执行多级的快照保存策略。Redis默认开启RDB快照。也可以通过SAVE或者BGSAVE命令手动触发RDB快照保存。 SAVE 和 BGSAVE 两个命令都会调用 rdbSave 函数,但它们调用的方式各有不同:
cd /opt/modules/redis-3.2.8
vim redis.conf
# 第202行
save 900 1
save 300 10
save 60 10000
save 5 1
这三个选项是redis的配置文件默认自带的存储机制。表示每隔多少秒,有多少个key发生变化就生成一份dump.rdb文件,作为redis的快照文件
例如:save 60 10000 表示在60秒内,有10000个key发生变化,就会生成一份redis的快照
ps -ef | grep redis
bin/redis-cli -h bigdata-pro-m07 shutdown
bin/redis-server redis.conf
采用AOF持久方式时,Redis会把每一个写请求都记录在一个日志文件里。在Redis重启时,会把AOF文件中记录的所有写操作顺序执行一遍,确保数据恢复到最新。
AOF默认是关闭的,如要开启,进行如下配置:
# 第594行
appendonly yes
AOF提供了三种fsync配置:always/everysec/no,通过配置项[appendfsync]指定:
随着AOF不断地记录写操作日志,因为所有的写操作都会记录,所以必定会出现一些无用的日志。大量无用的日志会让AOF文件过大,也会让数据恢复的时间过长。不过Redis提供了AOF rewrite功能,可以重写AOF文件,只保留能够把数据恢复到最新状态的最小写操作集。
AOF rewrite可以通过BGREWRITEAOF命令触发,也可以配置Redis定期自动进行:
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
Redis的数据持久化工作本身就会带来延迟,需要根据数据的安全级别和性能要求制定合理的持久化策略:
每一次RDB快照和AOF Rewrite都需要Redis主进程进行fork操作。fork操作本身可能会产生较高的耗时,与CPU和Redis占用的内存大小有关。根据具体的情况合理配置RDB快照和AOF Rewrite时机,避免过于频繁的fork带来的延迟
Redis在fork子进程时需要将内存分页表拷贝至子进程,以占用了24GB内存的Redis实例为例,共需要拷贝48MB的数据。在使用单Xeon 2.27Ghz的物理机上,这一fork操作耗时216ms。
Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
总结说:Redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令
Redis事务没有隔离级别的概念:
Redis不保证原子性:
Redis事务相关命令:
bigdata-pro-m07:6379> set k1 v1 OK bigdata-pro-m07:6379> set k2 v2 OK bigdata-pro-m07:6379> multi OK bigdata-pro-m07:6379> set k1 11 QUEUED bigdata-pro-m07:6379> set k2 22 QUEUED bigdata-pro-m07:6379> exec 1) OK 2) OK bigdata-pro-m07:6379> get k1 "11" bigdata-pro-m07:6379> get k2 "22"
bigdata-pro-m07:6379> flushdb OK bigdata-pro-m07:6379> keys * (empty list or set) bigdata-pro-m07:6379> set k1 v1 OK bigdata-pro-m07:6379> set k2 v2 OK bigdata-pro-m07:6379> multi OK bigdata-pro-m07:6379> set k1 11 QUEUED bigdata-pro-m07:6379> sets k2 22 (error) ERR unknown command 'sets' bigdata-pro-m07:6379> exec (error) EXECABORT Transaction discarded because of previous errors. bigdata-pro-m07:6379> get k1 "v1" bigdata-pro-m07:6379> get k2 "v2"
Redis类型错误(运行时错误),在开启事务后,修改k1值为11,k2值为22,但将k2的类型作为List,在运行时检测类型错误,最终导致事务提交失败,此时事务并没有回滚,而是跳过错误命令继续执行, 结果k1值改变、k2保留原值。
bigdata-pro-m07:6379> flushdb OK bigdata-pro-m07:6379> keys * (empty list or set) bigdata-pro-m07:6379> set k1 v1 OK bigdata-pro-m07:6379> set k2 v2 OK bigdata-pro-m07:6379> multi OK bigdata-pro-m07:6379> set k1 11 QUEUED bigdata-pro-m07:6379> lpush k2 22 QUEUED bigdata-pro-m07:6379> exec 1) OK 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value bigdata-pro-m07:6379> get k1 "11" bigdata-pro-m07:6379> get k2 "v2"
DISCARD取消事务
bigdata-pro-m07:6379> multi
OK
bigdata-pro-m07:6379> set k6 v6
QUEUED
bigdata-pro-m07:6379> set k7 v7
QUEUED
bigdata-pro-m07:6379> discard
OK
bigdata-pro-m07:6379> get k6
(nil)
bigdata-pro-m07:6379> get k7
(nil)
多数事务失败是由语法错误或者数据结构类型错误导致的,语法错误说明在命令入队前就进行检测的,而类型错误是在执行时检测的,Redis为提升性能而采用这种简单的事务,这是不同于关系型数据库的,特别要注意区分。Redis之所以保持这样简易的事务,完全是为了保证高并发下的核心问题——性能。
Redis是key-value数据库,可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理。
过期策略通常有以下三种:
Redis的内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据,在Redis的配置文件中描述如下:
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory # is reached. You can select among five behaviors: #最大内存策略:当到达最大使用内存时,你可以在下面5种行为中选择,Redis如何选择淘汰数据库键 #当内存不足以容纳新写入数据时 # volatile-lru -> remove the key with an expire set using an LRU algorithm # volatile-lru :在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把 redis 既当缓存,又做持久化存储的时候才用。 # allkeys-lru -> remove any key according to the LRU algorithm # allkeys-lru : 移除最近最少使用的key (推荐) # volatile-random -> remove a random key with an expire set # volatile-random : 在设置了过期时间的键空间中,随机移除一个键,不推荐 # allkeys-random -> remove a random key, any key # allkeys-random : 直接在键空间中随机移除一个键,弄啥叻 # volatile-ttl -> remove the key with the nearest expire time (minor TTL) # volatile-ttl : 在设置了过期时间的键空间中,有更早过期时间的key优先移除 不推荐 # noeviction -> don't expire at all, just return an error on write operations # noeviction : 不做过键处理,只返回一个写操作错误。 不推荐 # Note: with any of the above policies, Redis will return an error on write # operations, when there are no suitable keys for eviction. # 上面所有的策略下,在没有合适的淘汰删除的键时,执行写操作时,Redis 会返回一个错误。下面是写入命令: # At the date of writing these commands are: set setnx setex append # incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd # sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby # zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby # getset mset msetnx exec sort # 过期策略默认是: # The default is: # maxmemory-policy noeviction
实际项目中设置内存淘汰策略:maxmemory-policy allkeys-lru,移除最近最少使用的key。
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。
默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。
如下图所示左边是Master节点,右边是slave节点,即主节点和从节点。从节点也是可以对外提供服务的,主节点是有数据的,从节点可以通过复制操作将主节点的数据同步过来,并且随着主节点数据不断写入,从节点数据也会做同步的更新。
从节点起到的就是数据备份的效果。
除了一主一从模型之外,Redis还提供了一主多从的模型,也就是一个master可以有多个slave,也就相当于有了多份的数据副本。
可以做一个更加高可用的选择,例如一个master和一个slave挂掉了,还能有其他的slave数据备份。
在bigdata-pro-m08和bigdata-pro-m09执行以下命令安装依赖环境
yum -y install gcc-c++
在bigdata-pro-m08和bigdata-pro-m09服务器上面上传Redis压缩包,然后进行解压,并将安装包上传到/opt/software路径下
cd /opt/software
tar -zxvf redis-3.2.8.tar.gz -C /opt/softwares/
在bigdata-pro-m08和bigdata-pro-m09服务器执行以下命令在线装TCL
yum -y install tcl
bigdata-pro-m08和bigdata-pro-m09执行以下命令进行编译Redis
执行以下命令进行编译:
cd /opt/software/redis-3.2.8/
#或者使用命令 make 进行编译
make MALLOC=libc
make test && make install PREFIX=/opt/software/redis-3.2.8/
执行以下命令修改Redis配置文件
cd /opt/software/redis-3.2.8/
mkdir data
mkdir log
vim redis.conf
# 修改第61行
bind bigdata-pro-m08
# 修改第128行
daemonize yes
# 修改第163行
logfile "/opt/software/redis-3.2.8/log/redis.log"
# 修改第247行
dir /opt/software/redis-3.2.8/data
# 修改第266行,配置bigdata-pro-m08为第一台服务器的slave节点
slaveof bigdata-pro-m07 6379
执行以下命令修改Redis配置文件
cd /opt/software/redis-3.2.8/
mkdir data
mkdir log
vim redis.conf
# 修改第61行
bind bigdata-pro-m08
# 修改第128行
daemonize yes
# 修改第163行
logfile "/opt/software/redis-3.2.8/log/redis.log"
# 修改第247行
dir /opt/software/redis-3.2.8/data
# 修改第266行,配置bigdata-pro-m09为第二台服务器的slave节点
slaveof bigdata-pro-m07 6379
bigdata-pro-m08和bigdata-pro-m089执行以下命令启动Redis服务
bin/redis-server redis.conf
启动成功便可以实现redis的主从复制,bigdata-pro-m07可以读写操作,bigdata-pro-m08与bigdata-pro-m09只支持读取操作。
在Server1 掉线后:
升级Server2 为新的主服务器:
三台机器执行以下命令修改redis的哨兵配置文件
vim sentinel.conf
配置监听的主服务器
#修改第15行, bind配置,每台机器修改为自己对应的主机名
bind bigdata-pro-m07
# 在下方添加配置,让sentinel服务后台运行
daemonize yes
#修改第71行,三台机器监控的主节点,现在主节点是bigdata-pro-m07服务器
sentinel monitor mymaster bigdata-pro-m07 6379 2
参数说明
如果Redis是有密码的,需要指定密码
# sentinel author-pass定义服务的密码,mymaster是服务名称,123456是Redis服务器密码
# sentinel auth-pass <master-name> <password>
scp sentinel.conf bigdata-pro-m08:$PWD
scp sentinel.conf bigdata-pro-m09:$PWD
bigdata-pro-m08
# 修改第18行
bind bigdata-pro-m08
bigdata-pro-m09
# 修改第18行
bind bigdata-pro-m09
bin/redis-sentinel sentinel.conf
三台服务器的进程信息:
[caizhengjie@bigdata-pro-m07 redis-3.2.8]$ ps -ef | grep redis
caizhen+ 16605 1 0 00:37 ? 00:00:03 bin/redis-sentinel bigdata-pro-m07:26379 [sentinel]
root 16681 1 0 00:53 ? 00:00:00 bin/redis-server bigdata-pro-m07:6379
caizhen+ 16685 1233 0 00:53 pts/0 00:00:00 grep --color=auto redis
查看Sentinel master的状态
bin/redis-cli -h bigdata-pro-m08 -p 26379
使用ping命令检查哨兵是否工作,如果正常会返回PONG
bigdata-pro-m08:26379> ping
PONG
bigdata-pro-m08:26379> info
... ... ...
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=10.211.55.9:6379,slaves=2,sentinels=3
这时的主节点是bigdata-pro-m07
使用kill -9命令杀死redis服务进程,模拟redis故障宕机情况
过一段时间之后,就会在bigdata-pro-m08与bigdata-pro-m09服务器选择一台服务器来切换为主节点
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=10.211.55.11:6379,slaves=2,sentinels=3
这时的主节点是bigdata-pro-m09
通过哨兵连接,要指定哨兵的地址,并使用JedisSentinelPool来创建连接池。
实现步骤:
package cn.itcast.redis.api_test; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.JedisSentinelPool; import java.util.HashSet; import java.util.Set; /** * @author :caizhengjie * @description: * 1. 在 cn.itcast.redis.api_test 包下创建一个新的类 RedisSentinelTest * 2. 构建JedisPoolConfig配置对象 * 3. 创建一个HashSet,用来保存哨兵节点配置信息(记得一定要写端口号) * 4. 构建JedisSentinelPool连接池 * 5. 使用sentinelPool连接池获取连接 * @date :2021/1/28 4:07 下午 */ public class RedisSentinelTest { private JedisSentinelPool jedisSentinelPool; @BeforeTest public void redisConnectionPool(){ // 创建JedisPoolConfig配置对象 JedisPoolConfig config = new JedisPoolConfig(); // 指定最大空闲连接为10个 config.setMaxIdle(10); // 最小空闲连接5个 config.setMinIdle(5); // 最大等待时间为3000毫秒 config.setMaxWaitMillis(3000); // 最大连接数为50 config.setMaxTotal(50); HashSet<String> sentinelSet = new HashSet<>(); sentinelSet.add("bigdata-pro-m07:26379"); sentinelSet.add("bigdata-pro-m08:26379"); sentinelSet.add("bigdata-pro-m09:26379"); jedisSentinelPool = new JedisSentinelPool("mymaster",sentinelSet,config); } @Test public void keysTest(){ // 1.要操作redis,先要获取redis连接。现在通过哨兵连接池来获取连接 Jedis jedis = jedisSentinelPool.getResource(); // 2.执行keys操作 Set<String> keySet = jedis.keys("*"); // 3.遍历所有key for (String key : keySet) { System.out.println(key); } // 4.再将连接返回到连接池 jedis.close(); } @AfterTest public void afterTest(){ // 关闭连接池 jedisSentinelPool.close(); } }
运行结果:
k1
k2
===============================================
Default Suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================
Redis最开始使用主从模式做集群,若master宕机需要手动配置slave转为master;后来为了高可用提出来哨兵模式,该模式下有一个哨兵监视master和slave,若master宕机可自动将slave转为master,但它也有一个问题,就是不能动态扩充;所以在Redis 3.x提出cluster集群模式。
Redis Cluster是Redis官方提供的Redis集群功能,为什么要实现Redis Cluster?
在存储引擎框架(MySQL、HDFS、HBase、Redis、Elasticsearch等)中,只要数据量很大时,单机无法承受压力,最好的方式就是:数据分布进行存储管理。
对Redis 内存数据库来说:全量数据,单机Redis节点无法满足要求,按照分区规则把数据分到若干个子集当中。
一致性哈希分区图解:
虚拟槽分区图解:
Redis Cluster是分布式架构,有多个节点,每个节点都负责进行数据读写操作,每个节点之间会进行通信。Redis Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。
结构特点:
Redis 集群的优势:
Redis 3.0以后,节点之间通过去中心化的方式提供了完整的sharding(数据分片)、replication(复制机制、Cluster具备感知准备的能力)、failover解决方案。
Redis3.0及以上版本实现,集群中至少应该有奇数个节点,所以至少有三个节点,官方推荐三主三从的配置方式。Redis 3.x和Redis4.x 搭建集群是需要手动安装ruby组件的,比较麻烦。
2018年十月 Redis 发布了稳定版本的 5.0 版本,推出了各种新特性,其中一点是放弃 Ruby的集群方式,改为 使用 C语言编写的redis-cli的方式,是集群的构建方式复杂度大大降低。Redis cluster tutorial:https://redis.io/topics/cluster-tutorial
基于Redis-5.0.8版本,在三台机器上搭建6个节点的Redis集群:三主三从架构。
关闭以前Redis主从复制和哨兵模式监控的所有服务,备注:如果以前没有安装过Redis服务,不用执行此步骤操作。
# ============= node1.itcast.cn、node2.itcast.cn和node3.itcast.cn =============
# 关闭哨兵服务SentinelServer
ps -ef | grep redis
kill -9 哨兵的进程ID
# 关闭Redis服务
redis-cli -h bigdata-pro-m07 -p 6379 SHUTDOWN
redis-cli -h bigdata-pro-m08 -p 6379 SHUTDOWN
redis-cli -h bigdata-pro-m09 -p 6379 SHUTDOWN
安装Redis编译环境:GCC和TCL。
yum -y install gcc-c++ tcl
将Redis-5.0.8软件安装包上传至 /opt/software 目录,并解压与安装。
chmod u+x redis-5.0.8.tar.gz
tar -zxvf redis-5.0.8.tar.gz -C /opt/modules/
编译Redis 源码,并安装至【/opt/modules/redis-5.0.8-bin】目录。
# bigdata-pro-m07, 编译、安装、创建软连接
# 进入源码目录
cd /opt/modules/redis-5.0.8
# 编译
make
# 安装至指定目录
make PREFIX=/opt/modules/redis-5.0.8-bin install
# 创建安装目录软连接
ln -s redis-5.0.8-bin redis
配置环境变量(如果以前安装过Redis,配置过环境变量,就不用配置)。
vim /etc/profile
# ======================== 添加如下内容 ========================
# REDIS HOME
export REDIS_HOME=/opt/modules/redis
export PATH=:$PATH:$REDIS_HOME/bin
# 执行生效
source /etc/profile
从Redis-5.0.8源码目录下拷贝配置文件:redis.conf至Redis 安装目录。
# ====================== bigdata-pro-m07 上操作 ======================
# 拷贝配置文件
cd /opt/modules/redis-5.0.8
cp redis.conf /opt/modules/redis
每台机器上启动2个Redis服务,一个主节点服务:7001,一个从节点服务:7002,如下图所示:
在Redis安装目录下创建7001和7002目录,分别存储Redis服务配置文件、日志及数据文件。
# 创建目录:7001和7002
cd /opt/modules/redis
mkdir -p 7001 7002
拷贝配置文件:redis.conf至7001目录,并重命名为redis_7001.conf。
cd /opt/modules/redis
cp redis.conf 7001/redis_7001.conf
编辑配置文件:redis_7001.conf,内容如下:
cd /opt/modules/redis/7001 vim redis_7001.conf ## =========================== 修改内容说明如下 =========================== ## 69行,配置redis服务器接受链接的网卡 bind 0.0.0.0 ## 88行,关闭保护模式 protected-mode no ## 92行,设置端口号 port 7001 ## 136行,redis后台运行 daemonize yes ## 158行,Redis服务进程PID存储文件名称 pidfile /var/run/redis_7001.pid ## 171行,设置redis服务日志存储路径 logfile "/opt/modules/redis-5.0.8-bin/7001/log/redis.log" ## 263行,设置redis持久化数据存储目录 dir /opt/modules/redis-5.0.8-bin/7001/data/ ## 699行,启动AOF方式持久化 appendonly yes ## 832行,启动Redis Cluster cluster-enabled yes ## 840行,Redis服务配置保存文件名称 cluster-config-file nodes-7001.conf ## 847行,超时时间 cluster-node-timeout 15000
创建日志目录和数据目录:
mkdir log
mkdir data
配置7002端口号启动Redis服务,操作命令如下:
## 拷贝配置文件
cd /opt/modules/redis
cp 7001/redis_7001.conf 7002/redis_7002.conf
## 修改配置文件:redis_7002.conf
cd /opt/modules/redis/7002
vim redis_7002.conf
# 进入vim编辑之后,执行以下代码将7001全部替换成7002
:%s/7001/7002/g # 表示:%s/old/new/g g表示全部替换
# 创建目录
mkdir log
mkdir data
将bigdata-pro-m07上配置好的Redis安装包,发送至bigdata-pro-m08和bigdata-pro-m09,每台机器运行2个Redis服务,端口号分别为7001和7002,具体命令如下:
# 发送安装包 cd /opt/modules/ scp -r redis-5.0.8-bin bigdata-pro-m08:$PWD scp -r redis-5.0.8-bin bigdata-pro-m09:$PWD # 创建软连接 ln -s redis-5.0.8-bin redis # 配置环境变量 vim /etc/profile # ======================== 添加如下内容 ======================== # REDIS HOME export REDIS_HOME=/opt/modules/redis export PATH=:$PATH:$REDIS_HOME/bin # 执行生效 source /etc/profile
在三台机器,分别启动6个Redis服务,命令如下:
# 启动7001端口Redis服务
cd /opt/modules/redis
bin/redis-server 7001/redis_7001.conf
# 启动7002端口Redis服务
bin/redis-server 7002/redis_7002.conf
Redis服务启动完成以后,查看如下:
[root@bigdata-pro-m07 redis]# ps -ef | grep redis
root 25362 1 0 03:37 ? 00:00:00 bin/redis-server 0.0.0.0:7001 [cluster]
root 25367 1 0 03:37 ? 00:00:00 bin/redis-server 0.0.0.0:7002 [cluster]
root 25372 20674 0 03:37 pts/0 00:00:00 grep --color=auto redis
Redis5.x版本之后,通过redis-cli客户端命令来进行创建集群,注意:Redis对主机名解析不友好,使用IP地址。
# 任意选择一台机器执行如下命令,创建集群
bin/redis-cli --cluster create 10.211.55.9:7001 10.211.55.9:7002 10.211.55.10:7001 10.211.55.10:7002 10.211.55.11:7001 10.211.55.11:7002 --cluster-replicas
启动集群日志信息如下:
>>> Performing hash slots allocation on 6 nodes... Master[0] -> Slots 0 - 5460 Master[1] -> Slots 5461 - 10922 Master[2] -> Slots 10923 - 16383 Adding replica 10.211.55.10:7002 to 10.211.55.9:7001 Adding replica 10.211.55.11:7002 to 10.211.55.10:7001 Adding replica 10.211.55.9:7002 to 10.211.55.11:7001 M: 85967aece18ad0a0dbba0bd8ab5dc231daa37211 10.211.55.9:7001 slots:[0-5460] (5461 slots) master S: 0e819904c77c695451f50b83c6a65fd83d1e4760 10.211.55.9:7002 replicates b870f6c001ba485caffa2ade9a152d163909a548 M: 29b88a543dd91233f5c0b7f25b8fa05495799f9f 10.211.55.10:7001 slots:[5461-10922] (5462 slots) master S: 644fe21de168518e8ece15785a96078fd8926498 10.211.55.10:7002 replicates 85967aece18ad0a0dbba0bd8ab5dc231daa37211 M: b870f6c001ba485caffa2ade9a152d163909a548 10.211.55.11:7001 slots:[10923-16383] (5461 slots) master S: aed488ce6dcbde4fc23e8b160a0ea8583ec783ab 10.211.55.11:7002 replicates 29b88a543dd91233f5c0b7f25b8fa05495799f9f Can I set the above configuration? (type 'yes' to accept): yes >>> Nodes configuration updated >>> Assign a different config epoch to each node >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join .... >>> Performing Cluster Check (using node 10.211.55.9:7001) M: 85967aece18ad0a0dbba0bd8ab5dc231daa37211 10.211.55.9:7001 slots:[0-5460] (5461 slots) master 1 additional replica(s) M: 29b88a543dd91233f5c0b7f25b8fa05495799f9f 10.211.55.10:7001 slots:[5461-10922] (5462 slots) master 1 additional replica(s) S: 0e819904c77c695451f50b83c6a65fd83d1e4760 10.211.55.9:7002 slots: (0 slots) slave replicates b870f6c001ba485caffa2ade9a152d163909a548 M: b870f6c001ba485caffa2ade9a152d163909a548 10.211.55.11:7001 slots:[10923-16383] (5461 slots) master 1 additional replica(s) S: 644fe21de168518e8ece15785a96078fd8926498 10.211.55.10:7002 slots: (0 slots) slave replicates 85967aece18ad0a0dbba0bd8ab5dc231daa37211 S: aed488ce6dcbde4fc23e8b160a0ea8583ec783ab 10.211.55.11:7002 slots: (0 slots) slave replicates 29b88a543dd91233f5c0b7f25b8fa05495799f9f [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.
在任意一台机器,使用redis-cli客户端命令连接Redis服务:
redis-cli -c -p 7001
输入命令:cluster nodes(查看集群信息)和info replication(主从信息):
127.0.0.1:7001> cluster nodes 29b88a543dd91233f5c0b7f25b8fa05495799f9f 10.211.55.10:7001@17001 master - 0 1611909783813 3 connected 5461-10922 0e819904c77c695451f50b83c6a65fd83d1e4760 10.211.55.9:7002@17002 slave b870f6c001ba485caffa2ade9a152d163909a548 0 1611909784865 5 connected 85967aece18ad0a0dbba0bd8ab5dc231daa37211 10.211.55.9:7001@17001 myself,master - 0 1611909779000 1 connected 0-5460 b870f6c001ba485caffa2ade9a152d163909a548 10.211.55.11:7001@17001 master - 0 1611909786961 5 connected 10923-16383 644fe21de168518e8ece15785a96078fd8926498 10.211.55.10:7002@17002 slave 85967aece18ad0a0dbba0bd8ab5dc231daa37211 0 1611909788005 4 connected aed488ce6dcbde4fc23e8b160a0ea8583ec783ab 10.211.55.11:7002@17002 slave 29b88a543dd91233f5c0b7f25b8fa05495799f9f 0 1611909785916 6 connected 127.0.0.1:7001> info replication # Replication role:master connected_slaves:1 slave0:ip=10.211.55.10,port=7002,state=online,offset=280,lag=0 master_replid:f67ea78cd33268a6c217b99b249858c8372837ae master_replid2:0000000000000000000000000000000000000000 master_repl_offset:280 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:280
测试数据,设置Key值和查询Key的值。
127.0.0.1:7001> keys * (empty list or set) 127.0.0.1:7001> set k1 v1 -> Redirected to slot [12706] located at 10.211.55.11:7001 OK 10.211.55.11:7001> set k2 v2 -> Redirected to slot [449] located at 10.211.55.9:7001 OK 10.211.55.9:7001> set k3 v3 OK 10.211.55.9:7001> get k1 -> Redirected to slot [12706] located at 10.211.55.11:7001 "v1" 10.211.55.11:7001> get k2 -> Redirected to slot [449] located at 10.211.55.9:7001 "v2" 10.211.55.9:7001> get k3 "v3" 10.211.55.9:7001> KEYS * 1) "k3" 2) "k2"
编写脚本,方便启动和关闭Redis集群:redis-cluster-start.sh和redis-cluster-stop.sh。
# 进入bigdata-pro-m07
cd /opt/modules/redis/bin
touch redis-cluster-start.sh
touch redis-cluster-stop.sh
# 给以执行权限
chmod u+x redis-cluster-start.sh
chmod u+x redis-cluster-stop.sh
#!/bin/bash
REDIS_HOME=/opt/modules/redis
# Start Server
## bigdata-pro-m07
ssh bigdata-pro-m07 "${REDIS_HOME}/bin/redis-server /opt/modules/redis/7001/redis_7001.conf"
ssh bigdata-pro-m07 "${REDIS_HOME}/bin/redis-server /opt/modules/redis/7002/redis_7002.conf"
## bigdata-pro-m08
ssh bigdata-pro-m08 "${REDIS_HOME}/bin/redis-server /opt/modules/redis/7001/redis_7001.conf"
ssh bigdata-pro-m08 "${REDIS_HOME}/bin/redis-server /opt/modules/redis/7002/redis_7002.conf"
## bigdata-pro-m09
ssh bigdata-pro-m09 "${REDIS_HOME}/bin/redis-server /opt/modules/redis/7001/redis_7001.conf"
ssh bigdata-pro-m09 "${REDIS_HOME}/bin/redis-server /opt/modules/redis/7002/redis_7002.conf"
#!/bin/bash
REDIS_HOME=/opt/modules/redis
# Stop Server
## bigdata-pro-m07
${REDIS_HOME}/bin/redis-cli -h bigdata-pro-m07 -p 7001 SHUTDOWN
${REDIS_HOME}/bin/redis-cli -h bigdata-pro-m07 -p 7002 SHUTDOWN
## bigdata-pro-m08
${REDIS_HOME}/bin/redis-cli -h bigdata-pro-m08 -p 7001 SHUTDOWN
${REDIS_HOME}/bin/redis-cli -h bigdata-pro-m08 -p 7002 SHUTDOWN
## bigdata-pro-m09
${REDIS_HOME}/bin/redis-cli -h bigdata-pro-m09 -p 7001 SHUTDOWN
${REDIS_HOME}/bin/redis-cli -h bigdata-pro-m09 -p 7002 SHUTDOWN
启动脚本的时候注意root权限和无密钥登陆。
测试Redis Cluster中主从服务切换,首先查看集群各个服务状态:
在bigdata-pro-m09上将7001端口Redis 服务关掉:SHUTDOWN
redis-cli -h bigdata-pro-m09 -p 7001 SHUTDOWN
重新启动bigdata-pro-m09上7001端口Redis服务,再次查看集群状态信息:
bin/redis-server 7001/redis_7001.conf
# 连接Redis集群集群
redis-cli -c -p 7001
cluster nodes
redis-cli集群命令帮助:
redis-cli --cluster help
在实际项目中可能由于Redis Cluster中节点宕机或者增加新节点,需要操作命令管理,主要操作如下。
连接Redis集群,需要使用JedisCluster来获取Redis连接。
实现步骤:
注意事项:
package cn.itcast.redis.api_test; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPoolConfig; import java.io.IOException; import java.util.HashSet; /** * @author :caizhengjie * @description:TODO * @date :2021/1/29 8:54 下午 */ public class RedisClusterTest { private JedisPoolConfig jedisPoolConfig; private JedisCluster jedisCluster; @BeforeTest public void beforeTest(){ // 1. 创建一个HashSet<HostAndPort>,用于保存集群中所有节点的机器名和端口号 HashSet<HostAndPort> hostAndPortSet = new HashSet<>(); hostAndPortSet.add(new HostAndPort("bigdata-pro-m07", 7001)); hostAndPortSet.add(new HostAndPort("bigdata-pro-m07", 7002)); hostAndPortSet.add(new HostAndPort("bigdata-pro-m08", 7001)); hostAndPortSet.add(new HostAndPort("bigdata-pro-m08", 7002)); hostAndPortSet.add(new HostAndPort("bigdata-pro-m09", 7001)); hostAndPortSet.add(new HostAndPort("bigdata-pro-m09", 7002)); // 2. 创建JedisPoolConfig对象,用于配置Redis连接池配置 JedisPoolConfig config = new JedisPoolConfig(); // 指定最大空闲连接为10个 config.setMaxIdle(10); // 最小空闲连接5个 config.setMinIdle(5); // 最大等待时间为3000毫秒 config.setMaxWaitMillis(3000); // 最大连接数为50 config.setMaxTotal(50); // 3. 创建JedisCluster对象 jedisCluster = new JedisCluster(hostAndPortSet); } @Test public void clusterOpTest(){ // 设置一个key jedisCluster.set("pv", "1"); // 获取key System.out.println(jedisCluster.get("pv")); } @AfterTest public void afterTest(){ try { jedisCluster.close(); } catch (IOException e) { System.out.println("关闭Cluster集群连接失败!"); e.printStackTrace(); } } }
从Redis 3.0发布提供Redis Cluster以后,经历Redis 4.x、Redis5.x和Redis 6.x一系列版本,Redis Cluster更加成熟、稳定,推荐企业使用此种架构,通常公司也是使用此种架构。如果使用Redis Cluster集群,面试中碰到的问题有一些坑,还望注意。
在应用程序和MySQL数据库中建立一个中间层:Redis缓存,通过Redis缓存可以有效减少查询数据库的时间消耗,但是引入redis又有可能出现缓存穿透、缓存击穿、缓存雪崩等问题。
缓存穿透: key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。
一言以蔽之:查询Key,缓存和数据源都没有,频繁查询数据源
比如用一个不存在的用户id获取用户信息,无论论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
解决缓存穿透的方案主要有两种:
缓存击穿: key对应的数据库存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
一言以蔽之:查询Key,缓存过期,大量并发,频繁查询数据源
业界比较常用的做法:使用互斥锁。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db(查询数据库),而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,就是只让一个线程构建缓存,其他线程等待构建缓存的线程执行完,重新从缓存获取数据。
String get(String key) { String value = redis.get(key); if (value == null) { // 如果key不存在,则设置为1 if (redis.setnx(key_mutex, "1")) { // 设置key的过期时间为3分钟 redis.expire(key_mutex, 3 * 60) // 从db中加载数据,但注意:只有一个线程能进入到这里,其他线程访问的时候已有课key_mutex value = db.get(key); // 从数据库中加载成功,则设置对应的数据 redis.set(key, value); redis.delete(key_mutex); } else { //其他线程休息50毫秒后重试 Thread.sleep(50); get(key); } } }
通过sexnx(“互斥锁”, 1)
缓存雪崩: 当缓存服务器重启或者大量缓存集中在某一个时间段失效,会出现大量并发去直接访问数据库,导致数据库的压力过大,系统崩溃。
一言以蔽之:缓存不可用(服务器重启或缓存失效),频繁查询数据源
与缓存击穿的区别在于这里针对很多key缓存,前者则是某一个key。缓存正常从Redis中获取,示意图如下:
缓存失效瞬间示意图如下:
缓存失效时的雪崩效应对底层系统的冲击非常可怕!大多数系统设计者考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时将缓存失效时间分散开,比如可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
解决办法:
以上内容仅供参考学习,如有侵权请联系我删除!
如果这篇文章对您有帮助,左下角的大拇指就是对博主最大的鼓励。
您的鼓励就是博主最大的动力!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。