️Redis小白的复习梳理
官网: https://redis.io/ 中文地址: http://redis.cn/下载地址: https://redis.io/download
高并发、高可用、高性能、海量用户。
性能瓶颈问题:磁盘IO性能低下。
扩展瓶颈:数据关系复杂,扩展性差,不利于大规模集群。
内存存储,降低了磁盘IO次数
不存储关系,仅存储数据与数据之间关系,越简单越好
Redis (Remote DIctionary Server) 是用 C 语言开发的一个开源的高性能键值 对(key-value)数据库。
数据间没有必然的关联关系。
高性能。
官方提供测试数据,50 个并发执行 100000 个请求,读的速度是 110000 次/s,写的速 度是 81000 次
多种数据结构支持
持久化支持,可以进行数据灾难恢复。
Not-Only SQL( 泛指非关系型的数据库),作为关系型数据库的补充。
作用:应对在海量用户和海量数据的情况下,带来的数据处理问题。
常见的NoSQL数据库有Redis、memcache、HBase、MongoDB。
/etc/sysconfig/network-scripts/ifcfg-ens33,根据实际情况修改。
#IP地址
IPADDR=192.168.153.129
#网关
GATEWAY=192.168.153.2
#DNS
DNS1=8.8.8.8
BOOTPROTO="static"
安装gcc
yam install gcc
测试gcc版本
gcc --version
下载redis-6.26.tar.gc,并转存到 /opt
目录
解压redis-6.26.tar.gc,得到redis-6.26目录
tar -zxvf redis-6.26.tar.gc
make
make install
cd /usr/local/bin/
ls
显示结果
redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-sentinel redis-server
/usr/local/bin/redis-server /etc/redis.conf
ps -aux |grep redis
或者
netstat -anp | more
/usr/local/bin/redis-cli -p 6379
单实例关闭
/usr/local/bin/redis-cli shutdown
/usr/local/bin/redis-cli -p 6379 shutdown
指令文档: http://redis.cn/commands.html
参考中文文档: http://redisdoc.com/
Redis 命令十分丰富,包括的命令组有 Cluster、Connection、Geo、Hashes、HyperLogLog、Keys、Lists、Pub/Sub、Scripting、Server、Sets、Sorted Sets、Strings、Transactions 一共14个 redis 命令组两百多个 redis 命令。
# 设置key,value数据
set key value
# 根据key查询value,如果不存在,返回空(nil)
get keyh
# 退出客户端 (Redis 服务没有关闭)
quit/exit
# 清屏
clear
# 查看当前库所有key
keys *
#判断某个key是否存在
exists key
#查看key的类型
type key
#删除指定key
del key
#根据value选择非阻塞删除,仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作
unlink key
#未指定key设置过期时间,默认单位为秒
expire key time(s)
#查看key还有多少秒过期
ttl key
redis 安装后,默认有 16 个库, 0-15
# 命令切换数据库
select n
# 查看当前数据库的 key 的数量
dbsize
# 清空当前库(谨慎使用)
flushdb
# 清空全部库 (谨慎使用)
flushall
官方文档 : https://redis.io/commands
参考中文文档: http://redisdoc.com/
redis 自身是一个 Map,其中所有的数据都是采用key : value 的形式存储。key 是字符串,value 是数据,数据支持多种类型/结构。Redis 数据类型 5 种常用 string 、 hash 、list 、set 、sorted_set。
# 添加键值对
set key value
# 只在键 key 不存在的情况下, 将键 key 的值设置为 value 。
setnx key value
# 查询key对应的value
get key
# 将给定的value追加到原值的末尾
append key value
# 获得值的长度
strlen key
# 设置key值(只有在key不存在时)
setnx key value
# 将value存储的数字值(字符串)增1,只能对数字值操作,如果为空,新增值为1。
incr key
# 将value存储的数字值(字符串)减1,只能对数字值操作,如果为空,新增值为-1。
decr key
# 将value储存的数字值增。自定义步长。
incrby key step
# 将value储存的数字值减。自定义步长。
decrby key step
# 同时设置一个或多个value,如果某个给定键已经存在, 那么 MSET 将使用新值去覆盖旧值
# 是一个原子性(atomic)操作。
# 所有给定键都会在同一时间内被设置, 不会出现某些键被设置了但是另一些键没有被设置的情况。
mset key value key value
# 当且仅当所有给定键都不存在时, 为所有给定键设置值。
msetnx key value key
# 同时获取多个value值
mget key key
# 获取值范围(闭区间),索引从0开始。
getrange key start end
# 覆盖存储字符串的值,从star位置开始,索引从0开始。
setrange key offset value
# 设置value,同时设置过期时间(秒)
setex key seconds
#返回旧值,同时设置一个新值
getset key value
# 从右边插入一个或多个值
rpush key value value
# 从左边插入一个或多个值
lpush key value value
# 移除并返回列表 key 的头元素。(从左边吐出一个值) count:删除的数量
lpop key [count]
#从右边吐出一个值
rpop key
# 从key1列表右边吐出一个值,插到key2列表左边。
rpoplplush key1 key2
# 按照索引下标获得元素(从左到右,0左边第一个,-1右边第一个)
lrange key start stop
# 按照索引下标获得元素
lindex key index
# 获取列表长度
llen key
# 在value的前面或后面插入newvalue
linsert key before|after value newvalue
# 将列表 key 下标为 index 的值替换成 value
lset key index value
# 从左边删除 n 个 value(从左到右)
# count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。
# count < 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。
# count = 0 : 移除表中所有与 value 相等的值。
lrem key count value
set 提供的功能与 list 类似是一个列表的功能,特殊之处在于set 是可以自动排重的, 即值是不允许重复。
# 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。
sadd key membe member
# 取出该集合的所有值。
smembers key
# 判断集合key是否为含有该value值,有 1,没有0。
sismember key value
# 返回集合 key 的基数(集合中元素的数量)。
scard key
# 删除集合中的某个元素。
srem key value1 value2
# 随机从该集合中吐出一个值。
spop key
# 随机从该集合中取出 n 个值。不会从集合中删除。
srandmember key n
# 把集合中一个值从一个集合移动到另一个集合
smove source destination value
# 返回两个集合的交集元素。
sinter key1 key2
# 返回两个集合的并集元素。
sunion key1 key2
# 返回两个集合的差集元素(key1 中的,不包含 key2 中的)
sdiff key1 key2
hash 是一个键值对集合,hash 适合用于存储对象, 类似 Java 里面的 Map
# 给key集合中的 field键赋值value
hset key field value
# 将key 中的域 field 的值设置为 value ,当且仅当域field不存在
hset key field value
# 从key集合field取出 value
hget key1 field
# 批量设置 hash 的值
hmset key1 field1 value1 field2 value2
# 批量取出 hash 的 filed 值
hmget key1 field1 field2
# 查看哈希表 key 中,给定域 field 是否存在
hexists key1 field
# 列出该hash集合中的所有field
hkeys key
# 列出该hash集合中的所有value
hvals key
# 返回哈希表 key 中,所有的域和值。
hgetall key
# 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
hdel key field field
# 返回哈希表 key 中域的数量。
hlen key
# 返回哈希表 key 中, 与给定域 field 相关联的值的字符串长度
hstrlen key field
# 为哈希表 key 中的域 field 的值加上增量 increment 。 increment为负数则执行减法操作
hincrby key filed increment
Zset(sorted set)
# 将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
# score 值可以是整数值或双精度浮点数。
zadd key score member score member
# 返回有序集 key 中,成员 member 的 score 值。 如果 member 元素不是有序集 key 的成员,或 key 不存在,返回 nil
zscore key member
# 为有序集 key 的成员 member 的 score 值加上增量increment 传递一个负数值 increment ,让 score 减去相应的值
zincrby key increment member
# 返回序集key的基数
zcard key
#返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量。
zcount key min max
# 返回有序集 key 中,指定区间内的成员,其中成员的位置按 score 值递增(从小到大)来排序。
zrange key start stop [withscores]
# 返回有序集 key 中,指定区间内的成员,其中成员的位置按 score 值递增(从大到小)来排序
zrevrange key start stop [withscores]
# 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。
# (从小到大)
zrangebyscore key min max [withscores] [limit offset count]
# 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。
# (从大到小)
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
#返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。
#score 值最小的成员排名为 0 。
ZRANK key member
#返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递减(从大到小)排序。
#score 值最大的成员排名为 0
ZREVRANK key member
# 删除该集合的一个或多个元素
zrem key member [member ...]
# 移除有序集 key 中,指定排名(rank)区间内的所有成员。包含 start 和 stop 在内。
ZREMRANGEBYRANK key start stop
# 移除有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。
ZREMRANGEBYSCORE key min max
# 计算给定的一个或多个有序集的并集,其中给定 key 的数量必须以 numkeys 参数指定,并将该并集(结果集)储存到 destination 。
# 默认情况下,结果集中某个成员的 score 值是所有给定集下该成员 score 值之和 。
# --
# 使用 WEIGHTS 选项,你可以为 每个 给定有序集 分别 指定一个乘法因子(multiplication factor),每个给定有序集的所有成员的 score 值在传递给聚合函数(aggregation function)之前都要先乘以该有序集的因子。
# 如果没有指定 WEIGHTS 选项,乘法因子默认设置为 1 。
# 使用 AGGREGATE 选项,你可以指定并集的结果集sorce的聚合方式。
ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]
# 计算给定的一个或多个有序集的交集,其中给定 key 的数量必须以 numkeys 参数指定,并将该交集(结果集)储存到 destination 。
ZINTERSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]
列举几个常用设置
参考文档 : https://www.cnblogs.com/nhdlb/p/14048083.html#_label0
默认情况 bind=127.0.0.1 只能接受本机的访问请求。
如果服务器是需要远程访问的,需要将其注释掉。
默认是保护模式
如果服务器是需要远程访问的, 需要将 yes 设置为 no
服务默认端口 6379
一个空闲的客户端维持多少秒会关闭,0 表示关闭该功能, 即永不超时。默认为0。
是对访问客户端的一种心跳检测,每隔 n 秒检测一次, 单位为秒。
如果设置为 0,则不会进行 Keepalive 检测,建议设置成60。
TCP 协议中有长连接和短连接之分。短连接环境下,数据交互完毕后,主动释放连接。
长连接的环境下,进行一次数据交互后,很长一段时间内无数据交互时,客户端可能意外断开,这些 TCP 连接并未来得及正常释放,那么,连接的另一方并不知道对端的情况,它会一直维护这个连接,长时间的积累会导致非常多的半打开连接,造成端系统资源的消耗和浪费,且有可能导致在一个无效的数据链路层面发送业务数据,结果就是发送失败。所以服务器端要做到快速感知失败,减少无效链接操作,这就有了TCP 的Keepalive(保活探测)机制。
是否为后台进程,设置为 yes
设置为 yes 后, 表示守护进程, 后台启动
存放 pid 文件的位置,每个实例会产生一个不同的 pid 文件, 记录redis 的进程号。
redis 日志分为 4 个级别,默认的设置为 notice, 开发测试阶段可以用debug(日志内容较多,不建议生产环境使用),生产模式一般选用 notice。
ogfile “” 就是说,默认为控制台打印,并没有日志文件生成
databases 16
设定库的数量 默认 16,默认数据库为 0 号
永久设置密码
命令行设置临时密码 config set requirepass foobared
设置 redis 同时可以与多少个客户端进行连接。默认情况下为 10000 个客户端。
设置redis占用内存最大值
设置样本数量,LRU 算法和最小 TTL 算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小,redis 默认会检查这么多个 key 并选择其中LRU 的那个。
一般设置 3 到 7 的数字,数值越小样本越不准确,但性能消耗越小。
- Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者(pub) 发送消息,订阅者 (sub) 接收消息。Redis 客户端可以订阅任意数量的频道。当给频道发布消息后,消息就会发送给订阅的客户端。
- Redis的发布和订阅:发布的消息没有持久化。订阅的客户端, 只能收到订阅后发布的消息
任务队列
主要应用:通知、公告 ,可以作为消息队列或者消息管道。
各应用程序作为 Publisher 向 Channel 中发送消息,Subscriber 端收到消息后执行相应的业务逻辑,比如写数据库。
主要应用:排行榜、投票、计数。
可以向不同的 Channel 中发送消息,由不同的 Subscriber 接收。
主要应用:群聊、聊天
发送信息,将信息 message 发送到指定的频道 channel
订阅频道,可以同时订阅多个频道。
取消订阅指定的频道, 如果不指定频道,则会取消订阅所有频道。
订阅指定模式,订阅一个或多个符合给定模式的频道。
退订指定的规则, 如果没有参数则会退订所有规则。
Jedis是Redis官方推荐的Java连接开发工具。在线文档
Linux——防火墙开放端口
firewall-cmd --permanent --add-port=6379/tcp
firewall-cmd --reload
相关指令
设置 /etc/redis.conf
1、bind注销
2、requirepass查看密码
protected-mode no 关闭保护模式
3、查看 netstat -anp |more
maven 依赖
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.2.0version>
dependency>
如果 redis 你设置了密码, 需要执行 jedis.auth(“密码”);进行权限验证。
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.6.6version>
<relativePath/>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.13.2.2version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
也可以用application.yml 文件
# 完成 redis 的基本配置
#Redis 服务器地址
spring.redis.host=192.168.153.129
#Redis 服务器连接端口
spring.redis.port=6379
#Redis 如果有密码,需要配置, 没有密码就不要写
spring.redis.password=foobared
#Redis 数据库索引(默认为 0)
spring.redis.database=0
#连接超时时间(毫秒)
spring.redis.timeout=1800000
#连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=20
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-wait=-1
#连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=5
#连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=0
是对要使用的 RedisTemplate bean 对象的配置, 可以理解成是一个常规配置.
与JdbcTemplate设计理念类似
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template =
new RedisTemplate<>();
System.out.println("template=>" + template);//这里可以验证..
RedisSerializer<String> redisSerializer =
new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =
new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.WRAPPER_ARRAY);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setConnectionFactory(factory);
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
//value hashmap序列化
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer =
new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.WRAPPER_ARRAY);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
RDB(Redis DataBase) 在指定的时间间隔内将内存中的数据集快照写入磁盘,也就Snapshot 快照,恢复时将快照文件读到内存。
如果你是正常关闭 Redis , 仍然会进行持久化, 不会造成数据丢失-如果是 Redis 异常终止/宕机, 就可能造成数据丢失。
Fork&Copy-On-Write
1、在 redis.conf 中配置文件名称, 默认为 dump.rdb
dbfilename dump.rdb
2、文件位置默认为 Redis 启动时命令行所在的目录下,可以修改到/root/下或者其他目录。
默认为 dir ./
修改为 dir “/root/”
save 传入空字符串, 可禁止save
当 Redis 无法写入磁盘的话(比如磁盘满了), 直接关掉 Redis 的写操作。推荐yes
对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis 会采用LZF 算法进行压缩。
如果你不想消耗 CPU 来进行压缩的话,可以设置为关闭此功能, 默认yes
在存储快照后, 还可以让 redis 使用 CRC64 算法来进行数据校验,保证文件是完整的
但是这样做会增加大约 10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能, 推荐 yes
save VS bgsave
flushall
执行 flushall 命令,也会产生 dump.rdb 文件, 数据为空.Redis Flushall 命令用于清空整个 Redis 服务器的数据(删除所有数据库的所有key)。
lastsave 命令获取最后一次成功执行快照的时间(unix 时间戳 https://tool.lu/timestamp/)
AOF(Append Only File):以日志的形式来记录每个写操作(增量保存),将 Redis 执行过的所有写指令记录下来(比如 set/del 操作会记录, 读操作 get 不记录)。
文档
选择RDB还是AOF
在redis.conf中
AOF的文件的名称默认为appendonly.aof。
appendfilename '‘appendonly.aof’
AOF保存路径于RDB文件保存路径一致 dir,AOF 和 RDB 同时开启,系统默认取 AOF 的数据。
是否开启AOF,yes 开启。
appendonly yes
1、如遇到 AOF 文件损坏,通过/usr/local/bin/redis-check-aof --fix appendonly.aof 进行恢复。
2、建议先: 备份被写坏的 AOF 文件。
3、恢复:重启 redis,然后重新加载。
可以修复但是可能会造成数据丢失。
appendfsync always
始终同步,每次 Redis 的写入都会立刻记入日志;性能较差但数据完整性比较好。
appendfsync everysec
每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。
appendfsync no
redis不主动进行同步,把同步时机交给操作系统(30s)
- AOF 文件越来越大,需要定期对 AOF 文件进行重写达到压缩。
- 旧的 AOF 文件含有无效命令会被忽略,保留最新的数据命令, 比如set a a1 ; set a b1; set a c1; 保留最后一条指令就可以了。
- AOF 重写降低了文件占用空间,更小的 AOF 文件可以更快的被 redis 加载。
直接调用 bgrewriteaof 命令
auto-aof-rewrite-min-size: AOF 文件最小重写大小, 只有当 AOF 文件大小大于该值时候才能重写, 默认配置 64MB 。
auto-aof-rewrite-percentage: 当前 AOF 文件大小和最后一次重写后的大小之间的比率等于或者大于指定的增长百分比,如 100 代表当前 AOF 文件是上次重写的两倍时候才重写。
系统载入时或者上次重写完毕时,Redis 会记录此时 AOF 大小,设为base_size,如果 Redis 的 AOF 当前大小>= base_size +base_size*100% (默认)且当前大小>=64mb(默认)的情况下,Redis 会对 AOF 进行重写。
复习 MySQL事务的三特性
名字 | 解释 |
---|---|
原子性(Atomicity) | 原子性是指事务是一个不可分割的工作单位,事务的操作要么都发生,要么什么都不发生。 |
一致性(Consistency) | 事务必须使数据库从一个一致性变换到另一个一致性的状态。 |
隔离性(Isolation) | 事务的隔离性使多个用户访问数据库时,数据库为每一个用户(开启事务), 不被其他事务操作的数据所干扰。多个并发事务之间相互隔离。 |
持久性(Durability) | 持久性是指一个事务一旦被提交,它对数据库中数据的改变是永久性的。 |
事务中的所有命令都会序列化、按顺序地执行 。
事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
队列中的命令(指令), 在没有提交前都不会实际被执行。
事务执行过程中, 如果有指令执行失败,其它的指令仍然会被执行, 没有回滚。
在组队时,命令语法出错,具有原子性。如果组队成功,有些指令执行失败,那么不具有原子性。
案例购票
如果没有控制, 会造成超卖现象 。 如果 3 个指令, 都得到执行, 最后剩余的票数是 -2
乐观锁在版本修改时具有原子性。
取消 watch 命令对所有 key 的监视。
如果在执行 watch 命令后,exec 命令或 discard 命令先被执行了的话,那么就不 需要再执行 unwatch 。
yum install httpd-tools
ab -n 1000 -c 100 -p ~/postfile -T application/x-www-form-urlencoded http://192.168.153.1:8080/sr/secKillServlet
解读指令
ab 是并发工具程序
-n 1000 表示一共发出 1000 次 http 请求
-c 100 表示并发时 100 次, 你可以理解 1000 次请求, 会在10 次发送完毕
-p ~/postfile 表示发送请求时, 携带的参数从当前目录的postfile 文件读取(文件需要事先创建)
-T application/x-www-form-urlencoded 就是发送数据的编码是基于表单的url 编码
节省每次连接 redis 服务带来的消耗,把连接好的实例反复利用。
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* @author xiaoyu
* @version 1.0
* 使用连接池的方式来获取Redis连接
*/
public class JedisPoolUtil {
//解读volatile作用
//1. 线程的可见性: 当一个线程去修改一个共享变量时, 另外一个线程可以读取这个修改的值
//2. 顺序的一致性: 禁止指令重排
private static volatile JedisPool jedisPool = null;
private JedisPoolUtil() {}
//保证每次调用返回的 jedisPool是单例-这里使用了双重校验
public static JedisPool getJedisPoolInstance() {
if (null == jedisPool) {
synchronized (JedisPoolUtil.class) {
if (null == jedisPool) {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//对连接池进行配置
jedisPoolConfig.setMaxTotal(200);
jedisPoolConfig.setMaxIdle(32);
jedisPoolConfig.setMaxWaitMillis(60 * 1000);
jedisPoolConfig.setBlockWhenExhausted(true);
jedisPoolConfig.setTestOnBorrow(true);
jedisPool = new JedisPool(jedisPoolConfig, "192.168.153.129", 6379, 60000,"foobared");
}
}
}
return jedisPool;
}
//释放连接资源
public static void release(Jedis jedis) {
if(null != jedis) {
jedis.close();//如果这个jedis是从连接池获取的,这里jedis.close(),就是将jedis对象/连接,释放到连接池
}
}
}
EVAL script numkeys key [key ...] arg [arg ...]
推荐阅读:Redis LUA API 、Lua语法教程
例子
127.0.0.1:6379> eval "return 'Hello World!'" 0
"Hello World!"
127.0.0.1:6379> eval "return ARGV[1]" 0 "Hello World !"
"Hello World !"
127.0.0.1:6379[2]> eval "return {ARGV[1],KEYS[1]}" 1 "jinyu" "Hello World !"
1) "Hello World !"
2) "jinyu"
调用 redis.call()
函数引发的错误将直接返回给执行它的客户端。相反,调用 redis.pcall()
函数时遇到的错误将返回到脚本的执行上下文,以便进行可能的处理。
例子
127.0.0.1:6379[2]> keys *
(empty array)
127.0.0.1:6379[2]> EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 test1 Hello
OK
127.0.0.1:6379[2]> keys *
1) "test1"
127.0.0.1:6379[2]> get test1
"Hello"
通过调用
SCRIPT LOAD
命令并提供其源代码,将脚本加载到服务器的缓存中。服务器不执行脚本,而只是编译并将其加载到服务器的缓存中。加载后,可以使用从服务器返回的 SHA1 摘要执行缓存的脚本。Redis 脚本缓存始终是易失性的。它不被视为数据库的一部分,也不会持久保存。缓存可以在服务器重新启动时清除,在故障切换期间当复制副本担任主角色时清除,或者由 显式
SCRIPT FLUSH
清除。这意味着缓存的脚本是临时的,缓存的内容可能随时丢失。
常用命令
SCRIPT LOAD script
:
此命令在 Redis 脚本缓存中注册指定的脚本。在我们想要确保不会 EVALSHA
失败的所有上下文中,它是一个有用的命令(例如,在管道中或从 [ / 事务](/topics MULTI
/ EXEC
transactions)调用时),而无需执行脚本。
SCRIPT EXISTS
:
给定一个或多个 SHA1 摘要作为参数,此命令返回一个包含 1 和 0 的数组。1 表示特定 SHA1 被识别为脚本缓存中已存在的脚本。0 的意思是具有此 SHA1 的脚本之前未加载(或者至少自最近一次调用 以来 SCRIPT FLUSH
从未加载过)。
EVALSHA script numkeys key [key ...] arg [arg ...]
127.0.0.1:6379[2]> script load "return 'Hello World !'"
"7b39be95cdcf24151596a4f42203ab6f6bf09a0b"
127.0.0.1:6379[2]> EVALSHA 7b39be95cdcf24151596a4f42203ab6f6bf09a0b 0
"Hello World !"
127.0.0.1:6379[2]> script flush
OK
127.0.0.1:6379[2]> EVALSHA 7b39be95cdcf24151596a4f42203ab6f6bf09a0b 0
(error) NOSCRIPT No matching script. Please use EVAL.
- 好处: 读写分离, 提升效率 (理解: 读写分离后, 将读和写操作分布到不同的Reids, 减少单个 Redis 的压力, 提升效率)
- 好处: 容灾快速恢复。 (理解: 如果某个 slaver , 不能正常工作, 可以切换到另一个slaver)
- 主从复制, 要求是 1 主多从, 不能有多个 Master。( 理解: 如果有多个主服务器Master, 那么 slaver 不能确定和哪个 Master 进行同步, 出现数据紊乱)
- 要解决主服务器的高可用性, 可以使用 Redis 集群。
1、创建目录/jyuRedis, 并拷贝 redis.conf 到 /jyuRedis/redis.conf。
mkdir /jyuRedis
cp /etc/redis.conf /jyuRedis/redis.conf
2、设置以守护线程的方式启动 /jyuRedis/redis.conf,关闭AOF备份只开启RDB备份 。
daemonize yes
appendonly no
3、在/jyuRedis目录下创建三个配置文件(redis6379.conf, redis6380.conf, redis6381.conf)。
include /jyuRedis/redis.conf
port 6380
pidfile /var/run/redis_6380.pid
dbfilename dump6380.rdb
3、连接redis ,命令info replication
查看主从复制相关信息。
redis-server /jyuRedis/redis6379.conf
redis-server /jyuRedis/redis6380.conf
redis-server /jyuRedis/redis6381.conf
4、使用salveof 设置 主机 (保证主机于从机网络连接)salveof master_ip master_port
slaveof 127.0.0.1 6379
上一个 Slave 可以是下一个 slave 的 Master,Slave 同样可以接收其他 slaves 的连接和同步请求,那么该 slave 作为了链条中下一个的 master, 可以有效减轻 master 的写压力,去中心化降低风险。
slaveof
风险是一旦某个 slave 宕机,后面的 slave 都没法同步。
主机挂了,从机还是从机,无法写数据了。
1、在薪火相传的结构下, 当一个 master 宕机后, 指向 Master 的 slave 可以升为 master, 其后面的 slave 不用做任何修改.
2、用 slaveof no one 将从机变为主机 (说明: 后面可以使用哨兵模式, 自动完成切换.)
slaveof no one
sentinel monitor redis_master 127.0.0.1 6380 1
sentinel monitor 是关键字, redis_master 是对监控的服务所起的名字(自由起名)。127.0.0.1 是IP地址(参考实际情况更改),6380是监听的端口。1是投票数量。
/usr/local/bin/redis-sentinel /jyuRedis/sentinel.conf
容量不够,redis 如何进行扩容?
并发写操作, redis 如何分摊?
主从模式,薪火相传模式,主机宕机,会导致 ip 地址发生变化,应用程序中配置需要修改对应的主机地址、端口等信息。
回顾linux 关于vim 文本替换指令
:[addr]s/源字符串/目的字符串/[option]
[addr]: 表示检索范围,省略时表示当前行。
[option] :表示操作类型
省略option时仅对每行第一个匹配串进行替换;
如果在源字符串和目的字符串中出现特殊字符,需要用”\”转义
include /jyuRedis/redis.conf
port 6380
pidfile /var/run/redis_6380.pid
dbfilename dump6380.rdb
# 打开集群模式
cluster-enabled yes
# 设定节点配置文件名
cluster-config-file nodes-6379.conf
# 设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换
cluster-node-timeout 15000
redis-server redis6379.conf
进入安装redis时的源文件目录,/opt/redis-6.2.6/src/。根据实际情况修改。主要是使用redis-cli,同时还需要一个ruby环境,redis6有ruby环境。
redis-cli --cluster create --cluster-replicas 1 192.168.153.129:6379 192.168.153.129:6380 192.168.153.129:6381 192.168.153.129:6389 192.168.153.129:6390 192.168.153.129:6391
1、一个集群至少要有三个主节点
2、选项 --cluster-replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。
3、分配原则:尽量保证主服务器和从服务器各自运行在不同的IP 地址(机器), 防止机器故障导致主从机制失效, 高可用性得不到保障
redis-cli -c -p 6379
# 查看集群中节点主从关系
cluster nodes
一个 Redis 集群包含 16384 个插槽(hash slot),编号从0-16383, Reids 中的每个键都属于这 16384 个插槽的其中一个
集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽,其中CRC16(key) 语句用于计算键 key 的 CRC16 校验和
集群中的每个节点负责处理一部分插槽。 举个例子, 如果一个集群可以有主节点,其中:例如
节点 A 负责处理 0 号至 5460 号插槽。
节点 B 负责处理 5461 号至 10922 号插槽。
节点 C 负责处理 10923 号至 16383 号插槽
在 redis 每次录入、查询键值,redis 都会计算出该 key 应该送往的插槽,如果不是该客户端对应服务器的插槽,redis 会告知应前往的 redis 实例地址和端口。
redis-cli 客户端提供了 –c 参数实现自动重定向。 如 redis-cli -c –p 6379 登入后,再录入、查询键值对可以自动重定向。
不在一个 slot 下的键值,是不能使用 mget,mset 等多键操作
因为k1,k2,k3可能被分不到不同的slot,因此会报错,所以可以通过{}来定义组的概念,从而使 key 中{}内相同内容的键值对放到一个slot 中去。mset k1{name} v1 k2{name} v2 k3{name} v3
不在一个 slot 下的键值,是不能使用 mget,mset。
如果主节点下线,从节点自动升为主节点。原主节点恢复后,原主节点变为从机。
如果某一段插槽的所有主从节点都宕掉,Redis 服务是否还能继续, 要根据不同的配置而言
1、实现扩容 2、分摊压力 3、无中心配置相对简单
1、多键操作是不被支持的 2、多键的 Redis 事务是不被支持的。lua 脚本不被支持 3、由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而其它方案想要迁移至 redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。
Set<HostAndPort> hostAndPorts = new HashSet<>();
hostAndPorts.add(new HostAndPort("192.168.153.129",6379));
JedisCluster jedisCluster = new JedisCluster(hostAndPorts);