Redis学习记录-002
Redis中文链接:http://www.redis.cn/commands/geodist.html
Geospatial地理位置
GEOADD
将指定的地理空间位置(纬度、经度、名称)添加到指定的key
中。这些数据将会存储到sorted set
这样的目的是为了方便使用GEORADIUS或者GEORADIUSBYMEMBER命令对数据进行半径查询等操作。
该命令以采用标准格式的参数x,y,所以经度必须在纬度之前。这些坐标的限制是可以被编入索引的,区域面积可以很接近极点但是不能索引。具体的限制,由EPSG:900913 / EPSG:3785 / OSGEO:41001 规定如下:
- 有效的经度从-180度到180度。
- 有效的纬度从-85.05112878度到85.05112878度。
当坐标位置超出上述指定范围时,该命令将会返回一个错误。
redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania" (integer) 2 redis> GEODIST Sicily Palermo Catania "166274.15156960039" redis> GEORADIUS Sicily 15 37 100 km 1) "Catania" redis> GEORADIUS Sicily 15 37 200 km 1) "Palermo" 2) "Catania" redis>
GEODISK
返回两个给定位置之间的距离。 如果两个位置之间的其中一个不存在, 那么命令返回空值。 指定单位的参数 unit 必须是以下单位的其中一个:
m 表示单位为米。 km 表示单位为千米。 mi 表示单位为英里。 ft 表示单位为英尺。 如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位。 GEODIST 命令在计算距离时会假设地球为完美的球形, 在极限情况下, 这一假设最大会造成 0.5% 的误差。
redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania" (integer) 2 redis> GEODIST Sicily Palermo Catania "166274.15156960039" redis> GEODIST Sicily Palermo Catania km "166.27415156960038" redis> GEODIST Sicily Palermo Catania mi "103.31822459492736" redis> GEODIST Sicily Foo Bar (nil) redis>
GEOHASH
返回一个或多个位置元素的 Geohash 表示。 通常使用表示位置的元素使用不同的技术,使用Geohash位置52点整数编码。由于编码和解码过程中所使用的初始最小和最大坐标不同,编码的编码也不同于标准。此命令返回一个标准的Geohash,在维基百科和geohash.org网站都有相关描述 Geohash字符串属性 该命令将返回11个字符的Geohash字符串,所以没有精度Geohash,损失相比,使用内部52位表示。返回的geohashes具有以下特性: 他们可以缩短从右边的字符。它将失去精度,但仍将指向同一地区。 它可以在 geohash.org 网站使用,网址 http://geohash.org/<geohash-string>。查询例子:http://geohash.org/sqdtr74hyu0. 与类似的前缀字符串是附近,但相反的是不正确的,这是可能的,用不同的前缀字符串附近。 返回值 integer-reply, 具体的: 一个数组, 数组的每个项都是一个 geohash 。 命令返回的 geohash 的位置与用户给定的位置元素的位置一一对应。
redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania" (integer) 2 redis> GEOHASH Sicily Palermo Catania 1) "sqc8b49rny0" 2) "sqdtr74hyu0" redis>
GEOPOS:
从key里返回所有给定位置元素的位置(经度和纬度)。 给定一个sorted set表示的空间索引,密集使用 geoadd 命令,它以获得指定成员的坐标往往是有益的。当空间索引填充通过 geoadd 的坐标转换成一个52位Geohash,所以返回的坐标可能不完全以添加元素的,但小的错误可能会出台。 因为 GEOPOS 命令接受可变数量的位置元素作为输入, 所以即使用户只给定了一个位置元素, 命令也会返回数组回复。 返回值 array-reply, 具体的: GEOPOS 命令返回一个数组, 数组中的每个项都由两个元素组成: 第一个元素为给定位置元素的经度, 而第二个元素则为给定位置元素的纬度。 当给定的位置元素不存在时, 对应的数组项为空值。
redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania" (integer) 2 redis> GEOPOS Sicily Palermo Catania NonExisting 1) 1) "13.361389338970184" 2) "38.115556395496299" 2) 1) "15.087267458438873" 2) "37.50266842333162" 3) (nil) redis>
GEORADIUS:附近的人(使用经纬度坐标查找)
以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
范围可以使用以下其中一个单位:
m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
在给定以下可选项时, 命令会返回额外的信息:
WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
WITHCOORD: 将位置元素的经度和维度也一并返回。
WITHHASH: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
命令默认返回未排序的位置元素。 通过以下两个参数, 用户可以指定被返回位置元素的排序方式:
ASC: 根据中心的位置, 按照从近到远的方式返回位置元素。
DESC: 根据中心的位置, 按照从远到近的方式返回位置元素。
在默认情况下, GEORADIUS 命令会返回所有匹配的位置元素。 虽然用户可以使用 COUNT <count> 选项去获取前 N 个匹配元素, 但是因为命令在内部可能会需要对所有被匹配的元素进行处理, 所以在对一个非常大的区域进行搜索时, 即使只使用 COUNT 选项去获取少量元素,
命令的执行速度也可能会非常慢。 但是从另一方面来说, 使用 COUNT 选项去减少需要返回的元素数量, 对于减少带宽来说仍然是非常有用的。
返回值
bulk-string-reply, 具体的:
在没有给定任何 WITH 选项的情况下, 命令只会返回一个像 [“New York”,”Milan”,”Paris”] 这样的线性(linear)列表。
在指定了 WITHCOORD 、 WITHDIST 、 WITHHASH 等选项的情况下, 命令返回一个二层嵌套数组, 内层的每个子数组就表示一个元素。
在返回嵌套数组时, 子数组的第一个元素总是位置元素的名字。 至于额外的信息, 则会作为子数组的后续元素, 按照以下顺序被返回:
以浮点数格式返回的中心与位置元素之间的距离, 单位与用户指定范围时的单位一致。
geohash 整数。
由两个元素组成的坐标,分别为经度和纬度。
redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania" (integer) 2 redis> GEORADIUS Sicily 15 37 200 km WITHDIST 1) 1) "Palermo" 2) "190.4424" 2) 1) "Catania" 2) "56.4413" redis> GEORADIUS Sicily 15 37 200 km WITHCOORD 1) 1) "Palermo" 2) 1) "13.361389338970184" 2) "38.115556395496299" 2) 1) "Catania" 2) 1) "15.087267458438873" 2) "37.50266842333162" redis> GEORADIUS Sicily 15 37 200 km WITHDIST WITHCOORD 1) 1) "Palermo" 2) "190.4424" 3) 1) "13.361389338970184" 2) "38.115556395496299" 2) 1) "Catania" 2) "56.4413" 3) 1) "15.087267458438873" 2) "37.50266842333162" redis>
GEORADIUSBYMEMBER:附近的人(使用已经添加的位置)
这个命令和 GEORADIUS 命令一样, 都可以找出位于指定范围内的元素, 但是 GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的, 而不是像 GEORADIUS 那样, 使用输入的经度和纬度来决定中心点
指定成员的位置被用作查询的中心。
redis> GEOADD Sicily 13.583333 37.316667 "Agrigento" (integer) 1 redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania" (integer) 2 redis> GEORADIUSBYMEMBER Sicily Agrigento 100 km 1) "Agrigento" 2) "Palermo" redis>
Hyperloglog基数统计
Bitmap位图场景
Redis基本的事务操作
事务:本质一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行 一次性 顺序性 排他性 redis单条命令是保证原子性的,redis事务是不保证原子性的 redis事务没有隔离级别的概念 所有的命令在事务中,并没有被执行 只有发起执行命令的时候 才会执行exec 事务分为3个阶段 1.开启事务(multi) 2.命令入队(....) 3.执行事务(exec)
127.0.0.1:6379> MULTI #开启事务 OK 127.0.0.1:6379(TX)> set k1 v1 #命令入队 QUEUED 127.0.0.1:6379(TX)> set k2 v2 QUEUED 127.0.0.1:6379(TX)> get k2 QUEUED 127.0.0.1:6379(TX)> set k3 v3 QUEUED 127.0.0.1:6379(TX)> exec #执行事务 1) OK 2) OK 3) "v2" 4) OK
放弃事务:DISCARD 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> set k1 v1 QUEUED 127.0.0.1:6379(TX)> set k2 v2 QUEUED 127.0.0.1:6379(TX)> set k4 v4 QUEUED 127.0.0.1:6379(TX)> DISCARD OK 127.0.0.1:6379> get k4 #事务中的命令都不会被执行 (nil)
Redis实现乐观锁
监控 悲观锁:认为什么时候都会出问题,无论做什么都加锁 乐观锁:认为什么时候都不会出问题,不会加锁 更新数据时候取判断一下 再次期间是否有人修改过数据,version,获取version,更新的时候比较version 127.0.0.1:6379> set money 100 OK 127.0.0.1:6379> set out 0 OK 127.0.0.1:6379> watch money #监视money OK 127.0.0.1:6379> multi #事务正常结束 数据期间没有发生变动 这个时候正常执行成功 OK 127.0.0.1:6379(TX)> DECRBY money 20 //银行卡取出少了20元 QUEUED 127.0.0.1:6379(TX)> INCRBY out 20 //手中余额多了20元 QUEUED 127.0.0.1:6379(TX)> exec 1) (integer) 80 2) (integer) 20 测试多线程修改值 使用watch 可以当作redis 乐观锁操作 127.0.0.1:6379> watch money OK 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> DECRBY money 10 QUEUED 127.0.0.1:6379(TX)> INCRBY out 10 QUEUED 127.0.0.1:6379(TX)> exec #执行之前另外一个线程修改了值 这个时候导致事务执行失败 (nil) 如果发现事务执行失败 先解锁unwatch 再次监视watch
Redis订阅发布
Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。
Redis 客户端可以订阅任意数量的频道。
下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
redis 127.0.0.1:6379> SUBSCRIBE runoobChat Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "redisChat" 3) (integer) 1
redis 127.0.0.1:6379> PUBLISH runoobChat "Redis PUBLISH test" (integer) 1 redis 127.0.0.1:6379> PUBLISH runoobChat "Learn redis by runoob.com" (integer) 1 # 订阅者的客户端会显示如下消息 1) "message" 2) "runoobChat" 3) "Redis PUBLISH test" 1) "message" 2) "runoobChat" 3) "Learn redis by runoob.com"
PSUBSCRIBE:订阅一个或多个符合给定模式的频道。
PUBSUB:查看订阅与发布系统状态
PUBLISH:将信息发送到指定的频道
PUNSUBSCRIBE:退订所有给定模式的频道
SUBSCRIBE:订阅给定的一个或多个频道的信息
UNSEBSCRIBE:退订给定的频道
Redis配置文件
1、配置文件单位大小写不敏感
2、可以引入其他配置文件
3、配置网络
4、pidfile 守护进程的pid设置
5、默认16个数据库
6、快照设置 默认的配置 save 900 1 save 300 10 save 60 10000 #解释 900秒有一个键发生改变,300秒内有10个键发生改变,60秒内有10000个键发生改变 则进行备份
7、持久化出错,是否继续工作 默认开启,继续工作
8、压缩rdb文件,默认开启
9、安全设置,默认没有密码
10、默认10000个客户端
11、内存满了,处理策略进行配置
默认RDB持久化,生成dump.rdb文件,rdb文件在配置文件夹中即可,每次重启则读取rdb的文件进行还原。
AOF(Append Only Aof)默认不开启,当aof备份文件存在语法错误时,可以使用redis-check-aof.exe 进行修复。
在aof文件中保存的内容有自己的固定格式,当文件中存在不符合格式的内容,在启动Redis并且开始还原aof文件时,则无法启动。这时候只需要运行redis-check-aof 就可以进行修复。
redis-check-aof --fix aof文件
Redis集群环境搭建
测试实验在单机进行,只需要修改配置文件即可,需要注意的是需要修改的有四项
1、port 6379 端口
2、pid(后台运行守护进程的id)
3、logfile log文件名称
4、dbfilename dump.rdb dump.rdb文件名(rdb备份文件)
Redis在配置之前都是主机,想要搭建集群,就需要进行配置,将Redis配置成从机,临时配置可以使用以下命令
slaveof 主机IP 端口
可以通过以上命令临时配置多个从机,想要永久生效就需要在配置文件(redis.windows.conf)中进行设置,我这个是在window上进行配置的。
#主机ip #主机端口
slaveof <masterip> <masterport>
#如果需要密码则进行配置 主机密码
masterauth <master-password>
# 使得Redis服务器可以跨网络访问 bind 0.0.0.0 # 设置密码 requirepass "123456" # 指定主服务器,注意:有关slaveof的配置只是配置从服务器,主服务器不需要配置 slaveof 192.168.11.128 6379 # 主服务器密码,注意:有关slaveof的配置只是配置从服务器,主服务器不需要配置 masterauth 123456
查看主从及信息命令
127.0.0.1:6379> info replication # Replication role:master #主机 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 127.0.0.1:6379>
接下来就是主从机发生故障的情况,前提是主机已经设置完毕。举例:A为主机,B、C为从机
主机写,从机读,从机主要用来备份
从机强行进行写操作,就会报错提示
主机A故障发生宕机,B、C从机的主机依然是A
主机A恢复后,从机B、C的数据进行恢复,并且A仍然为主机。有两个从机B、C,就是说我是主机,暂时宕机了,恢复了我依然是主机,该是我的就是我的
主机A已经设置完毕,从机也已经绑定完毕,已经存在数据,添加一个新的从机D,从机D会发送一个Sync同步命令,从而进行全量复制(将绑定前主机A的数据全量进行复制)。绑定完成后,主机A进行写操作时,就会进行增量复制,同步进行备份。
宕机后手动配置主机,以下命令可以使自己变为主机
slaveof no noe #通俗解释 部署于任何一个主机 那么我就是主机了
哨兵模式
哨兵认识
在哨兵模式出来之前,以上主从机设置都是手动的,之后都是自动的,感觉像是手动挡向自动挡的进化。
跟一主多从一样,防止单个哨兵故障宕机,于是哨兵模式也就是多哨兵模式,再加上redis集群,所以哨兵模式加一主多从,起步就是6个进程。
故障切换(failover),加入主服务器宕机,哨兵1先检测,系统不会马上进行failover过程,仅仅哨兵1主观的认为主服务器不可用,这个现象就是主观下线。当后面其他哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵就会进行一次投票,投票的结果由一个哨兵发起,
进行failover操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换,这个过程称为客观下线。
我自己总结的图
配置哨兵模式
1、修改配置文件(sentinel.conf) 最简单的核心配置
sentinel monitor myredis IP 端口 1 #1代表主机挂了进行投票选举
2、启动哨兵
redis-sentinel kconfig/sentinel.conf
配置哨兵模式之后,当主机宕机,确认进行客观下线后,进行选举,选举新的主机,A主机发生宕机,所有的哨兵都已经检测确认,然后将A主机进行客观下线,选举B作为主机,当A主机恢复后,此时就不再是主机了,也会以B作为主机,从而变成从机,这是跟之前手动设置的不同
# 禁止保护模式 protected-mode no # 配置监听的主服务器,这里sentinel monitor代表监控,mymaster代表服务器的名称,可以自定义,192.168.11.128代表监控的主服务器,6379代表端口,2代表只有两个或两个以上的哨兵认为主服务器不可用的时候,才会进行failover操作。 sentinel monitor mymaster 192.168.11.128 6379 2 # sentinel author-pass定义服务的密码,mymaster是服务名称,123456是Redis服务器密码 # sentinel auth-pass <master-name> <password> sentinel auth-pass mymaster 123456
java中使用哨兵模式
/** * 测试Redis哨兵模式 * @author liu */ public class TestSentinels { @SuppressWarnings("resource") @Test public void testSentinel() { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(10); jedisPoolConfig.setMaxIdle(5); jedisPoolConfig.setMinIdle(5); // 哨兵信息 Set<String> sentinels = new HashSet<>(Arrays.asList("192.168.11.128:26379", "192.168.11.129:26379","192.168.11.130:26379")); // 创建连接池 JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels,jedisPoolConfig,"123456"); // 获取客户端 Jedis jedis = pool.getResource(); // 执行两个命令 jedis.set("mykey", "myvalue"); String value = jedis.get("mykey"); System.out.println(value); } }
哨兵模式的其他配置
缓存穿透、击穿、雪崩
缓存穿透:很多请求请求一个缓存中不存在的数据,大量请求直达数据库,造成崩溃。
缓存击穿:很多请求同时访问同一个热点数据,当这个数据失效时,所有请求涌入数据库,造成奔溃。
缓存雪崩:热点数据同时失效,从而导致所有请求涌入数据库,造成崩溃。
开发中使用redis,进行集成,自己定义一个template,并且进行各种类型的系列化,将底层的方法进行封装,封装成RedisUtil,使调用更加方便。
跟SpringBoot集成是不再直接使用Jedis,而是使用lettue。
jedis和lettue的区别
jedis客户端连接方式是基于tcp的阻塞式连接方式。 支持全面的Redis操作特性,使用阻塞的I/O,且方法调用都是同步的,程序流需要等到socket处理完I/O才能执行,不支持异步,不是线程安全的,需要通过连接池使用Jedis
lettuce客户端连接方式是基于netty的多路复用异步非阻塞的连接方案。 可扩展的线程安全的Redis客户端,支持异步模式,底层基于Netty,支持高级的Redis特性,比如哨兵、集群、管道、自动重新连接
Redisson,额外提供了分布式服务,使用者对Redis进行关注分离,可以类比Spring框架,提供了很多分布式相关操作服务,比如,分布式锁、分布式集合、可通过Redis支持延迟队列,对字符串的操作支持比较差
使用建议:lettuce+Redisson
lettuce支持基础的Redis操作,Redisson提供了很多开箱即用的Redis高级功能,相互配合则比较完善。
当主从机写入配置中是,哨兵模式切换主从机时,是否会修改配置文件呢?具体怎么工作的呢???如何切换的??
故障切换机制
1. 启动群集后,群集程序默认会在从库的redis文件中加入连接主的配置
# Generated by CONFIG REWRITE slaveof 192.168.137.40 6379
2.启动群集之后,群集程序默认会在主从的sentinel.conf文件中加入群集信息
主:
port 26379 dir "/usr/local/redis-6379" # 守护进程模式 daemonize yes # 指明日志文件名 logfile "./sentinel.log" sentinel monitor mymaster 192.168.137.40 6379 1 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 18000 sentinel auth-pass mymaster 123456 # Generated by CONFIG REWRITE sentinel config-epoch mymaster 0 sentinel leader-epoch mymaster 1 sentinel known-slave mymaster 192.168.137.40 6380 sentinel known-sentinel mymaster 192.168.137.40 26380 c77c5f64aaad0137a228875e531c7127ceeb5c3f sentinel current-epoch 1
从:
#sentinel端口 port 26380 #工作路径 dir "/usr/local/redis-6380" # 守护进程模式 daemonize yes # 指明日志文件名 logfile "./sentinel.log" #哨兵监控的master,主从配置一样,在进行主从切换时6379会变成当前的master端口, sentinel monitor mymaster 192.168.137.40 6379 1 # master或slave多长时间(默认30秒)不能使用后标记为s_down状态。 sentinel down-after-milliseconds mymaster 5000 #若sentinel在该配置值内未能完成failover操作(即故障时master/slave自动切换),则认为本次failover失败。 sentinel failover-timeout mymaster 18000 #设置master和slaves验证密码 sentinel auth-pass mymaster 123456 #哨兵程序自动添加的部分 # Generated by CONFIG REWRITE sentinel config-epoch mymaster 0 sentinel leader-epoch mymaster 1 ###指明了当前群集的从库的ip和端口,在主从切换时该值会改变 sentinel known-slave mymaster 192.168.137.40 6380 ###除了当前的哨兵还有哪些监控的哨兵 sentinel known-sentinel mymaster 192.168.137.40 26379 7a88891a6147e202a53601ca16a3d438e9d55c9d sentinel current-epoch 1
模拟主故障
[root@monitor redis-6380]# ps -ef|grep redis root 4171 1 0 14:20 ? 00:00:15 /usr/local/redis-6379/src/redis-server *:6379 root 4175 1 0 14:20 ? 00:00:15 /usr/local/redis-6380/src/redis-server *:6380 root 4305 1 0 15:28 ? 00:00:05 /usr/local/redis-6379/src/redis-sentinel *:26379 [sentinel] root 4306 1 0 15:28 ? 00:00:05 /usr/local/redis-6380/src/redis-sentinel *:26380 [sentinel] root 4337 4144 0 15:56 pts/1 00:00:00 grep redis [root@monitor redis-6380]# kill -9 4171 [root@monitor redis-6380]# ps -ef|grep redis root 4175 1 0 14:20 ? 00:00:15 /usr/local/redis-6380/src/redis-server *:6380 root 4305 1 0 15:28 ? 00:00:05 /usr/local/redis-6379/src/redis-sentinel *:26379 [sentinel] root 4306 1 0 15:28 ? 00:00:05 /usr/local/redis-6380/src/redis-sentinel *:26380 [sentinel] root 4339 4144 0 15:56 pts/1 00:00:00 grep redis [root@monitor redis-6380]#
从哨兵配置文件中可以看到当前的主库的已经发生了改变
从日志文件也可以看到当前的主已经从6379转换成了6380
待续......