狂神Reids学习笔记二

三大特殊数据类型

geospatial地理位置

附近的人,地理查询,打车距离计算等等

Redis的Geo在Redis3.2就推出了!推算地理位置,方圆几里的人

Redis规定有效的纬度经度值

经度值-180 到 180

纬度值-85.05112878 到 85.05112878

超出范围会爆出错误

GEOADD

############################################################
###	GEOADD添加地理信息	两级无法直接添加  我们一般通过Java程序导入
#  参数  key(纬度  经度  名称)
127.0.0.1:6379> GEOADD china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> GEOADD china:city 121.47 31.23 sahnghai
(integer) 1
127.0.0.1:6379> GEOADD china:city 106.50 29.53 chongqing 114.05 22.54 shenzhen
(integer) 2
127.0.0.1:6379> GEOADD china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2
############################################################

GEOPOS

获得当前定位:坐标值

############################################################
###		GEOPOS 获取指定的经度纬度  可获取多个
127.0.0.1:6379> keys *
1) "china:city"
127.0.0.1:6379> GEOPOS china:city beijing
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
127.0.0.1:6379> GEOPOS china:city beijing chongqing
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
2) 1) "106.49999767541885376"
   2) "29.52999957900659211"

############################################################

GEODIST

两人之间的距离

单位:

​ ●m表示单位为米。
​ ●km表示单位为千米。
​ ●mi表示单位为英里。
​ ●ft表示单位为英尺。

############################################################
####		GEODIST  两地之间的距离
####		参数  地点1   地点二  单位
#不带单位
127.0.0.1:6379> GEODIST china:city beijing sahnghai
"1067378.7564"
#带单位
127.0.0.1:6379> GEODIST china:city beijing sahnghai km
"1067.3788"
127.0.0.1:6379> GEODIST china:city beijing chongqing km
"1464.0708"

############################################################

GEORADIUS

以给定的经纬度为中心,找出某半径内的元素

附近的人(获得所有附近的人的定位!!!)通过半径搜索周围几千米

############################################################
####   GEORADIUS  以给定的经纬度为中心,找出某半径内的元素
####  为了精确   所有的数据应该都要存储在city里面
####  以经纬度110  30  的地方搜索1000km以内的城市(都在city里)
####  参数下面例子详解
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km
1) "chongqing"
2) "xian"
#####	withdist	显示距离中心的直线距离
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist
1) 1) "chongqing"
   2) "341.9374"
2) 1) "xian"
   2) "483.8340"
#####  withcoord	显示地点的经纬度
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord
1) 1) "chongqing"
   2) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "xian"
   2) 1) "108.96000176668167114"
      2) "34.25999964418929977"
#####	withdist withcoord混合使用,即会显示距离中心的直线距离,又会显示地点的经纬度
######	count	限制查询出来的城市个数
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist withcoord count 1
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist withcoord count 2
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "xian"
   2) "483.8340"
   3) 1) "108.96000176668167114"
      2) "34.25999964418929977"
############################################################

GEORADIUSBYMEMBER

找出位于指定范围内的元素,中心点是由给定的位置元素决定的

附近的人朋友

############################################################
####	GEORADIUSBYMEMBER 找出指定元素周围的元素,附近的人朋友
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km
1) "beijing"
2) "xian"
###我这里上面存储的时候上海拼错了  
127.0.0.1:6379> GEORADIUSBYMEMBER china:city sahnghai 400 km
1) "hangzhou"
2) "sahnghai"
############################################################

GEOHASH

返回一个或多个位置元素的Geohash表示

该命令将返回11个字符的Geohash字符串,所以没有精度Geohash,损失相比,使用内部52位表示(缩短字符,丢失精度)

############################################################
####   GEOHASH  将二维的经纬度转换为一维字符串,即降维打击
####	如果两个字符串越接近,就说明位置越接近
127.0.0.1:6379> GEOHASH china:city beijing chongqing
1) "wx4fbxxfke0"
2) "wm5xzrybty0"
############################################################

GEO底层实现原理其实就是Zset!!

所以我们可以使用Zset来操作GEO

############################################################
##查看地图中所有元素
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "sahnghai"
6) "beijing"
####移除地图中的某个元素
127.0.0.1:6379> ZREM china:city sahnghai
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "beijing"

Hyperloglog

什么是基数

A{1,3,5,7,8,9,7} B{1,3,5,7,8}

基数: 不重复的元素

什么是Hyperloglog

Redis2.8.9版本就更新了Hyperloglog数据结构

Redis Hyperloglog 基数统计的算法

网页的UV(一个人访问一个网站多次,但是还是所做一个人)

传统的方式:set保存用户id,然后就可以统计set中的元素数量作为标准判断

但是有的网站采用UUID或者分布式ID来辨识用户,这样网站会存储大量的用户id,会比较麻烦!我们的目的是为了计数,而不是保存用户id!!!

Redis Hyperloglog

占用的内存是固定的,2^64不同的元素的技术(位计算),只需12KB内存!!如果要从内存角度比较,Redis Hyperloglog 是首选!!有一定的错误率

如果允许容错,那么一定可以使用Redis Hyperloglog

如果不允许容错,就是用set或者自己的数据类型

方法使用

############################################################
###		PFADD  添加元素
###		PFCOUNT	统计指定key中的基数
###		PFMERGE  合并(并集)  相同的不会重复插入
127.0.0.1:6379> PFADD mykey a b c d e f g h i j
(integer) 1
127.0.0.1:6379> PFCOUNT mykey
(integer) 10
127.0.0.1:6379> PFADD mykey2 i j z x c v b n m 
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 9
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 
OK
127.0.0.1:6379> PFCOUNT mykey3
(integer) 15
############################################################

Bitmaps

位存储

采用01来存储状态

统计疫情感染人数 使用01来存储 365天打卡设计,打卡/不打卡

统计用户信息!!活跃,不活跃 登录信息!!登录/不登录

两个状态的都可以用Bitmaps

Bitmaps位图,数据结构!操作二进制来进行记录,就只有0和1两个状态

拿 365天打卡设计来说 365天=365bit 1字节=8bit ======>46个字节我们就能解决

方法测试

############################################################
###		setbit存储数据
### 	sign表示一周内的打卡情况
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0 
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
#####		GETBIT				查看某一天是否打卡
127.0.0.1:6379> GETBIT sign 3		#查看周四
(integer) 1
127.0.0.1:6379> GETBIT sign 6		#查看周六
(integer) 0
######			BITCOUNT		统计打卡天数	扣全勤奖(xiao)
127.0.0.1:6379> BITCOUNT sign
(integer) 3

事务

MySQL:ACID原则,要么同时成功,要么同时失败,一个事务的所有的事务,都会被序列化,在事务的执行过程中,按照顺序执行排他性

Redis:单条命令是保证原子性的,但是Redis的事务是不保证原子性

Redis事务没有隔离级别的概念,所有的命令在事务中,并没有直接执行!!只有发起执行命令的时候才会执行 Exec

Redis事务:

  • 开启事务(MULTI)
  • 命令入队(…)
  • 执行事务(EXEC)

正常执行事务

############################################################
######		multi开启事务
#####		命令入队
####		exec命令执行
127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) "v2"
4) OK

放弃事务

因为是事务,上次开启执行之后就没了,我们得重新开启新的事务

############################################################
#####		DISCARD    放弃事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1 
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> GET key4
(nil)

编译型异常(代码有问题!命令有错),事务中所有的命令都不会被执行

############################################################
####
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> GETSET k3		####错误命令,报错
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4 
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> exec		###运行报错,所有都不执行
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1
(nil)

运行时异常(1/0)

事务队列中存在语法错误,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常

############################################################
127.0.0.1:6379> set k1 "v1"
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> INCR k1		#错误的计算
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec		##第一条报错,但是其他的依然成功
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3"

监控 Watch

悲观锁:

认为什么时候都出错,无论做什么都会加锁

乐观锁:

  • 认为什么时候都不出现问题,所以不会加锁,在更新数据的时候判断,再次期间,是否有人修改过这个数据**(mysql)version字段来处理**
  • 获取version
  • 更新的时候比较version

Redis的监视测试(面试常问)

正常执行成功

############################################################
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> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY out 20
QUEUED
127.0.0.1:6379> exec		#事务正常结束,数据期间没有发生变动,正								常执行
1) (integer) 80
2) (integer) 20

多线程测试

############################################################
##   	第一个线程	开启监视	进行事务操作	但是还没有执行
127.0.0.1:6379> WATCH money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
############################################################
####	第二个线程,修改了我们的money
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000
OK
############################################################
####	此时再去执行事务的命令  发现监视失败	相当于Redis的乐观锁操作
127.0.0.1:6379> WATCH money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
127.0.0.1:6379> exec
(nil)
############################################################
#####		解决办法
#####	发现事务失败先	UNWATCH解锁
####		再次监视(获取最新值,再次加锁)
####		比对监视的值是否发生变化,如果没有,则执行成功
#####				如果变了,则会执行失败
127.0.0.1:6379> UNWATCH
OK
127.0.0.1:6379> WATCH money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 1
QUEUED
127.0.0.1:6379> INCRBY out 1
QUEUED
127.0.0.1:6379> exec
1) (integer) 999
2) (integer) 21

Jedis

使用Java操作我们的Redis

什么是jedis

Redis是Redis官方推荐的Java连接的开发工具!!Java操作Reids的中间件

如果要使用Java操作Redis,一定要对Jedis十分熟悉

测试

1.导入对应的依赖

<!--导入jedis的包-->
    <dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.2.0</version>
        </dependency>
<!--        fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>

2.编码测试

  • 连接数据库
  • 操作命令
  • 断开连接

这个地方我们会出现很多问题

这里我总结一下

docker exec -it myredis redis-cli
Error response from daemon: Container 36e472dc2ac121942cd621d85c8d54c0e8d650de2e5e16ed94cc04b06f89dee5 is not running
这里说的是我们的这个容器没有启动

[root@bogon docker]# docker run -p 6379:6379 --privileged=true --name myredis -d redis redis-server /usr/local/docker/redis.conf
docker: Error response from daemon: Conflict. The container name "/myredis" is already in use by container "36e472dc2ac121942cd621d85c8d54c0e8d650de2e5e16ed94cc04b06f89dee5". You have to remove (or rename) that container to be able to reuse that name.
这里说的是我们已经有了这个容器,容器名字冲突了
然后我们真正启动后
[root@bogon docker]# docker exec -it redis redis-cli
127.0.0.1:6379> clear
127.0.0.1:6379> keys *
1) "out"
2) "money"
public class TestPing {
    public static void main(String[] args) {
        //1.new jedis对象
        Jedis jedis = new Jedis("192.168.1.99",6379);
        //dedis所有的命令跟linux一样
        System.out.println(jedis.ping());
    }
}
//如果ping失败我们要看看我们的配置文件是不是修改了
//1.bind 127.0.0.1 绑定IP 注释掉,默认只接收本机访问
//2.protected-mode no 守护进程设置no
//3.daemonize yes 作为守护进程运行yes
//地址要填成我们虚拟机CentOS的IP地址
运行结果
    PONG
	进程已结束,退出代码 0

java操作Redis API

public class TestPing {
    public static void main(String[] args) {
        //1.new jedis对象
        Jedis jedis = new Jedis("192.168.1.99",6379);
        //dedis所有的命令跟linux一样
        System.out.println(jedis.ping());
        System.out.println(jedis.keys("*"));
        System.out.println("清空数据"+jedis.flushDB());
        System.out.println("判断某个键是否存在:"+jedis.exists("username"));
        System.out.println("新增<'username','wang'>的键值对:"+jedis.set("username","wang"));
        System.out.println("新增<'password','wa'>的键值对:"+jedis.set("password","wa"));
        System.out.println("系统中所有的键:"+jedis.keys("*"));
    }
}
PONG
[money, out]
清空数据OK
判断某个键是否存在:false
新增<'username','wang'>的键值对:OK
新增<'password','wa'>的键值对:OK
系统中所有的键:[password, username]

其他的命令跟我们的命令一模一样,这里就不一一赘述了

通过jedis再次理解事务

1.正常执行

public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("192.168.1.99", 6379);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello","world");
        jsonObject.put("name","wanghuahua");
        //开启事务
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();
        try {
            multi.set("user1",result);
            multi.set("user2",result);
            multi.exec();
        } catch (Exception e) {
            multi.discard();
            e.printStackTrace();
        } finally {
            //输出数据
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user1"));
            jedis.close();
        }
    }
}
{"name":"wanghuahua","hello":"world"}
{"name":"wanghuahua","hello":"world"}

2.出现异常

public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("192.168.1.99", 6379);
        jedis.flushDB();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello","world");
        jsonObject.put("name","wanghuahua");
        //开启事务
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();
        try {
            multi.set("user1",result);
            multi.set("user2",result);
            int i=1/0;
            multi.exec();
        } catch (Exception e) {
            multi.discard();
            e.printStackTrace();
        } finally {
            //输出数据
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user1"));
            jedis.close();
        }
    }
}
java.lang.ArithmeticException: / by zero
	at com.wang.test.TestTX.main(TestTX.java:27)
null
null

3.监控对象

public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("192.168.1.99", 6379);
        jedis.flushDB();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello","world");
        jsonObject.put("name","wanghuahua");
        //开启事务
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();
       	multi.watch(result);
        //jedis.watch(result);
        try {
            multi.set("user1",result);
            multi.set("user2",result);
            multi.exec();
        } catch (Exception e) {
            multi.discard();
            e.printStackTrace();
        } finally {
            //输出数据
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user1"));
            jedis.close();
        }
    }
}
Exception in thread "main" redis.clients.jedis.exceptions.JedisDataException: Cannot use Jedis when in Multi. Please use Transaction or reset jedis state.
	at redis.clients.jedis.BinaryJedis.checkIsInMultiOrPipeline(BinaryJedis.java:1885)
	at redis.clients.jedis.Jedis.watch(Jedis.java:1709)
	at com.wang.test.TestTX.main(TestTX.java:24)
    原因是因为我们使用了事务监控,需要用事务的实例( Transaction实例 )来执行命令,最后调用exec执行事务模块中的命令。如果不小心使用了conn连接(Jedis实例)直接执行命令,则会抛出以上的异常。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会写代码的花城

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值