Redis学习笔记

Nodql概述


大数据时代;
一般的数据库无法进行分析处理了,2006年Hadoop发布 spring2004年
spingBoot springCloud

为什么要用Nosql?

1.单机mysql的时代
90年代,一个基本的网站访问量一般不会太大,单个数据库完全足够。那个时候更多的是使用静态网页Html,服务器根本没有太大的压力。
思考一下,这种情况下:整个网站的瓶颈是什么?
1.数据量如果太大、一个机器放不下
2.数据的索引(B+ Tree)一个机器内存页放不下
3.访问量(读写混合),一个服务器承受不了
只要出现以上的三种情况之一,那么就必须要晋级


2.Memcached(缓存) +mysql + 垂直拆分(读写分离)
网站80%的情况下都是在读,每次都去查询数据库的话就十分的麻烦!所以所我们要想减轻数据库的压力,就使用缓存来保证效率。
发展过程: 优化数据结构和索引 --> 文件缓存(IO) --> Memcached(当时最热门的技术)
加入缓存机制后,读的问题被妥善的解决了,但是写的问题呢?
在这里插入图片描述

3.分库分表+水平拆分+mysql集群
在这里插入图片描述
技术与业务发展的同时,对人的要求也越来越高。
本质:数据库(读,写)
早些年MyISAM: 表锁,查询的某一行的时候会把该表锁定,十分的影响效率!高并发下就会出现严重的锁问题
转战Innodb: 行锁
慢慢的就开始使用分库分表来解决写的压力,mysql在那个年代推出了表分区,并没有多少公司使用。
mysql的集群很好的解决了那个年代的所有需求!

4.如今的年代
2010-2020十年间,发生了翻天覆地的变化。(如 定位,音乐,热榜)
mysql这种关系型数据库就不够用了!数据量发生变化大,而且变化很快。
mysql有的使用它来存储一些比较大的文件,博客,图片。数据库图标很大,效率就会低下!如果有一种数据库专门用来处理这些数据,那么mysql压力就会变得十分小(研究如何处理这些问题!) 大数据的IO压力下,表几乎没法扩大,如某表一亿条,加一列就相当于重新加了一亿条null数据。
目前一个互联网的基本项目
在这里插入图片描述

###为什么要用Nosql!
用户的个人信息、社交网络、地理位置、用户自己产生的数据,用户日志等等爆发式的增长!
这时候已经到达了关系型数据库的瓶颈,就需要使用Nosql数据库

NoSQL
not only sql
关系型数据库:表格,行,列 (POI操作excel表)
泛指非关系型数据库,随着web2.0互联网的诞生,传统的关系型数据库很难对付web2.0时代!尤其超大规模的高并发的社区,暴露出来很多难以解决的问题,redis是发展最快的,也是当下最需要掌握的技术!存储并不需要一个固定的格式,不需要多余的操作就可以横向扩展! Map<String,Object>

NoSQL的特点

1.方便扩展(数据之间没有关系,很好扩展)
2.大数据量高性能(Redis一秒可以写8万次,读取11万次)
3.数据类型是多样型的,(不需要事先设计数据库!随取随用!如果数据库量十分大的表,很多人就无法设计了。)
4.传统RDBMS和NoSQL

传统的RDBMS
-结构化组织
-SQL
-数据和关系都存在单独的表中
-操作语言、数据定义语言。
-严格的一致性
-基础的事务性操作

NoSQL
不仅仅是数据
没有固定的查询语言
键值对存储、列存储、文档存储、图形数据库(社交关系)
最终一致性。
CAP定理和 BASE(异地多活)
高性能、高可用、高可扩。

了解3V+3高

大数据时代的3V:主要是描述问题的

  1. 海量Volume
  2. 多样Variety
  3. 实时Velocity

大数据时代的3高:主要是对程序的要求

  1. 高并发
  2. 高可扩(随时水平拆分、如加机器)
  3. 高性能(提高用户的体验)

真正在公司中的实践:NoSQL + RDBMS结合使用,才是最强的, 阿里巴巴的架构演进。


阿里巴巴演进分析

思考问题:这么多的东西难道都是在一个数据库中的吗?数据,图片,评论,有的需要经常改,有的不需要经常改。

在这里插入图片描述
了解 敏捷开发、极限编程。
如果未来想当一个架构师,没有什么是加一层解决不了的。

1、商品的基本信息
名称、价格、商家信息
关系型数据库就恶意解决, Mysql / oracle(淘宝早年就去IOE了 王坚:推荐文章 阿里云的这群疯子

2.、商品的描述、评论(文字比较多)
文档性数据库中,MongoDB

3、 图片
分布式文件系统FastDFS
淘宝自己的 TFS
Google的 GFS
Hadoop的 HDFS
阿里云的 oss

4、商品的关键字 (搜索)
-搜索引擎 solr elasticsearch
-ISearach 多隆(多去了解一下这些技术大佬。)

所有牛逼的人都会有一段苦逼的岁月,但是只要你像傻逼一样的去坚持,终将牛逼。

5、商品热门的波段信息
-内存数据库
-Redis 、 Tair、 Memache 。。。

6、商品的交易、外部的支付接口
-三方应用。

要知道,一个简单的网页背后的技术一定不是大家想的那么简单。
大型互联网应用问题:

  • 数据类型太多了
  • 数据源繁多,经常重构
  • 数据要改造,大面积改造比较麻烦。

阿里做的是在中间加一层
在这里插入图片描述


NoSQL的四大分类

kv键值对:
  • 新浪 : Redis
  • 美团 :Redis + Tair
  • 阿里 百度 : Redis + memecache
文档型数据库(bson格式和 Json一样)
  • MongoDB(一般必须要掌握)
    是一个基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档。
    MongoDB是一个介于关系型数据库和非关系型数据库中中间的产品 相当于两者的交集区域
    也是非关系型数据库中功能最丰富,最像关系型数据库的。

  • CouchDB 国外的

列存储数据库 (改行和改列是不一样的)
  • Hbase
  • 分布式文件系统
图形关系型数据库在这里插入图片描述不是存图形的,放的是关系。如:朋友圈社交网络、广告推荐!
  • Neo4j、 InfoGrid

四者NoSQL数据库的对比

在这里插入图片描述

敬畏之心可以使人进步。宇宙。


Redis入门

redis是什么?

Remote Dictionary Server 远程字典服务。

Redis能干吗?

redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

  1. 内存存储,持久化,内存中是断电即失的,所以说持久化很重要(rdb、aof)
  2. 效率高,可以用于高速缓存
  3. 发布订阅系统
  4. 地图信息分析
  5. 计时器、计数器(浏览量 )
  6. 。。。。

Redis的特性

  1. 多样的数据类型
  2. 持久化
  3. 集群
  4. 事务

Windows安装

Linux安装


1.下载安装包
2.解压文件
3.进入解压后的文件,可以看到redis的配置文件
在这里插入图片描述
4.基本的环境安装

yum install gcc-c++

make
make install

5.redis的默认安装路径 usr/local/bin
在这里插入图片描述
6.将redis配置文件复制到当前redis目录下
如将usr/local/redis.4.8.5目录下的redis.conf文件复制到 usr/local/redis/bin目录下 可以保证原生的配置文件不会改变。使用自定义复制的配置文件

7.redis默认不是后台启动的,修改配置文件。 vim redis.conf
修改 daemonize yes 设置后台启动

8.启动redis服务。
./redis.server redis.conf
./redis.cli -p 6379
在这里插入图片描述

9.查看redis是否连接

ps -ef | grep redis

10.如何关闭redis服务?
shutdown
然后exit

11.再次查看进程是否存在

ps -ef | grep redis

12.后面会使用单击多Redis启动集群测试!

Redis的性能测试

redis-benchmark是一个压力测试工具
官方自带的性能测试工具
可选的参数如下:
在这里插入图片描述

简单测试下:

测试: 100个并发连接 100000个请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000

在这里插入图片描述


Redis的基础知识

Redis默认有16个数据库

在这里插入图片描述

默认使用的是第0个
可以使用select进行切换数据库

select 3

查看当前数据库大小

DBSIZE

查看所有的key

keys *

清空库

flushdb #清空当前库
flushall #清空全部的库

思考:为什么redis的端口号是6379? (MERZ的九宫格简称。。。。)

Redis是单线程
明白Redis是很快的,官方表示,Redis是基于内存操作,CPU并不是Redis的性能瓶颈,Redis的瓶颈是根据机器内存和网络带宽,既然可以使用单线程实现,那么就使用了单线程。

Redis是C语言写的,官方提供的数据为100000+的QPS,完全不比同样是使用k-v的Memecache差!

为什么单线程还这么快?
1、误区1: 高性能的服务器一定是多线程的?
2、误区2: 多线程(CPU上下文会切换)一定比单线程效率高?

核心 : redis是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU会上下文切换:耗时的操作) ,对于内存系统而言,如果没有上下文切换效率就是最高的!多次读写都是在一个CPU上的,在内存情况下,这个就是最佳的方案。

五大数据类型

Redis是一个开源的,内存中的数据结构存储系统,可以做 数据库、 缓存、 消息中间件MQ 。支持多种类型的数据结构,如字符串、散列(hashes)、列表、集合、有续集合(sorted sets)与范围查询bitmaps,hyperloglogs和地理空间(geospatial)索引半径查询。

常用命令 :

EXISTS key 判断key是否存在
MOVE key 1移除
KEYS * 查看当前所有的key 值
EXPIRE key 10 设置key值10秒后过期 ttl key查看key的剩余时间 如设置用户的cookie 或者单点登陆
TYPE key 查看当前的key是什么类型 并不是当前key存储的value的类型

Redis-Key

String(字符串)


90%java程序员使用redis只会使用一个string类型!
在这里插入图片描述append追加了字符串 如果当前字符不存在就相当于set了一个key
STRLEN获取字符串的长度
INCR自动追加1 如浏览量
INCRBY 步长 INCRBY views 10
字符串范围

127.0.0.1:6379[2]> set key1 “hello,zhiqiang” OK getrange 获取范围字符串
127.0.0.1:6379[2]> get key1 “hello,zhiqiang”
127.0.0.1:6379[2]> getrange key1 1 4 “ello”
127.0.0.1:6379[2]> getrange key1 0 -1 “hello,zhiqiang”

127.0.0.1:6379[2]> set key2 abcdef OK #setrange 替换范围字符串
127.0.0.1:6379[2]> get key2 “abcdef”
127.0.0.1:6379[2]> setrange key2 1 xx (integer) 6
127.0.0.1:6379[2]> get key2 “axxdef”

设置set

setex (set with expire) #设置过期时间
setnx (set if not exist) #不存在再设置 (在分布式锁中会常常使用)
127.0.0.1:6379[2]> setex key3 30 “hello” OK
127.0.0.1:6379[2]> ttl key3 (integer) 26
127.0.0.1:6379[2]> ttl key3 (integer) 25
127.0.0.1:6379[2]> setnx mykey “redis” (integer) 1
127.0.0.1:6379[2]> keys * 1) “mykey” 2) “key1” 3) “key2”
127.0.0.1:6379[2]> ttl key3 (integer) -2
127.0.0.1:6379[2]> setnx mykey “MongDB” (integer) 0 如果mykey存在,创建失败!
127.0.0.1:6379[2]> keys * 1) “mykey” 2) “key1” 3) “key2”
127.0.0.1:6379[2]> get mykey “redis”

批量设置

mset k1 v1 k2 v2 k3 v3 批量设置
mget k1 k2 k3 批量获取
msetnx 当不存在时设置,存在的话失败 msetnx是一个原子性的操作,一起成功,一起失败

进阶操作

set user:1 {name:zhangsan,age:4} 实质上是设置了key为 user:1
get user:1 “{name:zhangsan,age:4}”

getset

127.0.0.1:6379[2]> getset db redis (nil) 如果不存在值,则返回nil
127.0.0.1:6379[2]> get db “redis”
127.0.0.1:6379[2]> getset db mongodb “redis” 如果存在值,则返回原来的值, 设置新的值。
127.0.0.1:6379[2]> get db “mongodb”
127.0.0.1:6379[2]>
先get然后再set

List


在redis中, 可以把list玩成, 栈、队列、阻塞队列。

所有的list命令都是以l开头的

127.0.0.1:6379[2]> keys * (empty list or set)
127.0.0.1:6379[2]> lpush list one two three (integer) 3 将一个值或者多个值插入列表的头部。(左边)
127.0.0.1:6379[2]> lrange list 0 -1 1) “three” 2) “two” 3) “one”
127.0.0.1:6379[2]> lrange list 0 1 1) “three” 2) “two” 再设置0~1的时候并没有获取到one 相当于倒着拿了出来。
127.0.0.1:6379[2]> rpush list right (integer) 4
127.0.0.1:6379[2]> lrange list 0 -1 1) “three” 2) “two” 3) “one” 4) “right” 使用了RPUSH后让新加的值添加到了最右边。

在这里插入图片描述
移除最左最右值

127.0.0.1:6379[2]> lrange list 0 -1 1) “three” 2) “two” 3) “one” 4) “right”
127.0.0.1:6379[2]> lpop list “three” 最左边移出值 移除列表的第一个元素
127.0.0.1:6379[2]> rpop list “right” 最右边移出值 移除列表的最后一个元素

通过下标获取值

127.0.0.1:6379[2]> lindex list 0 “two” 通过下标获得list的某一个值!
127.0.0.1:6379[2]> lindex list 1 “one”

获取list的长度

127.0.0.1:6379[2]> llen list (integer) 2 通过llen获取
127.0.0.1:6379[2]> lrange list 0 -1 1) “two” 2) “one”

移除指定的值 如 取关uid操作 精确匹配

127.0.0.1:6379[2]> lrange list 0 -1 且list中允许有重复的值。

  1. “three” 2) “three” 3) “two” 4) “one”
    127.0.0.1:6379[2]> lrange list 0 -1
  2. “three” 2) “three” 3) “two” Lrem list 2 three lrem 移除 list 中的2个 three value值。
    127.0.0.1:6379[2]> lrem list 1 three (integer) 1
    127.0.0.1:6379[2]> lrange list 0 -1 1) “three” 2) “two”
    127.0.0.1:6379[2]> lpush list three (integer) 3
    127.0.0.1:6379[2]> lrange list 0 -1 1) “three” 2) “three” 3) “two”
    127.0.0.1:6379[2]> lrem list 2 three (integer) 2
    127.0.0.1:6379[2]> lrange list 0 -1 1) “two”

截取字段 ltrim

127.0.0.1:6379[2]> lrange mylist 0 -1 1) “hello” 2) “hell1” 3) “hello2” 4) “hello3”
127.0.0.1:6379[2]> ltrim mylist 1 2 OK 通过下标截取指定的长度
127.0.0.1:6379[2]> lrange mylist 0 -1 1) “hell1” 2) “hello2”

移除并重新push一个值

rpoplpush

127.0.0.1:6379[2]> lrange mylist 0 -1 1) “hell1” 2) “hello2” 3) “hello3” 4) “hello4”
127.0.0.1:6379[2]> rpoplpush mylist myotherlist “hello4”
移除列表最后一个元素,将他移动到新的列表中去。 将hello4在mylist中移除了,并将其移动到myotherlist中去了。

模拟的update更新操作
lset list 0 litem 将list的第0个元素设置为item值 相当于更新操作

127.0.0.1:6379[2]> EXISTS list (integer) 0
127.0.0.1:6379[2]> lset list 0 item (error) ERR no such key 如果不存在列表去更新就会报错 存在的话就会set该值
127.0.0.1:6379[2]> lpush list value1 (integer) 1
127.0.0.1:6379[2]> lrange list 0 0 1) “value1”
127.0.0.1:6379[2]> lset list 0 item OK
127.0.0.1:6379[2]> lrange list 0 -1 1) “item”

linsert 在某一个值的前面或者后面插入值

127.0.0.1:6379[2]> rpush list hello (integer) 1
127.0.0.1:6379[2]> rpush list world (integer) 2
127.0.0.1:6379[2]> LINSERT list before world other (integer) 3
127.0.0.1:6379[2]> lrange list 0 -1 1) “hello” 2) “other” 3) “world”


小结

  • 实际上是一个链表,before Node after , left ,right都可以插入值
  • 如果key不存在,创建新的链表。
  • 如果key存在,则增加新的内容。
  • 如果移除了所有的值,空链表,也代表不存在。
  • 在两边插入或者改动值,效率最高!中间元素,相对来说,效率会低一些。

消息队列! 消息队列(Lpush Rpop) ,栈(Lpush Lpop)


Set(集合)

set中的值是不能重复的!

127.0.0.1:6379[2]> sadd myset hello (integer) 1
127.0.0.1:6379[2]> sadd myset world (integer) 1
127.0.0.1:6379[2]> sadd myset lovezhiqiang (integer) 1
127.0.0.1:6379[2]> smembers myset 1) “world” 2) “hello” 3) “lovezhiqiang” 查看set的所有值
127.0.0.1:6379[2]> SISMEMBER myset hello (integer) 1 判断是否在set集合中存在相关元素
Scard mysql 返回integer 3 查看set集合中的内容元素个数
SREM myset hello srem 移除set集合中某个元素

因为set中的元素是无序且唯一的
SRANDMEMBER myset 随机抽取其中的一个元素
SRANDMEMBER myset 2 随机抽取出其中元素 并指定个数

将一个指定的元素移动到另外一个set集合中

127.0.0.1:6379[2]> sadd myset hellp (integer) 1
127.0.0.1:6379[2]> sadd myset zhiqiang (integer) 1
127.0.0.1:6379[2]> sadd myset lovezhiqiang (integer) 1
127.0.0.1:6379[2]> sadd myset2 myset2 (integer) 1
127.0.0.1:6379[2]> SMOVE myset myset2 lovezhiqiang (integer) 1
127.0.0.1:6379[2]> SMEMBERS myset 1) “hellp” 2) “zhiqiang”
127.0.0.1:6379[2]> SMEMBERS myset2 1) “myset2” 2) “lovezhiqiang”

随机删除一个元素

127.0.0.1:6379[2]> spop myset “zhiqiang”
127.0.0.1:6379[2]> SMEMBERS myset 1) “hellp”

b站、微博 共同关注粉丝
数字集合类

  • 差集

127.0.0.1:6379[2]> sadd key1 a (integer) 1
127.0.0.1:6379[2]> sadd key1 b (integer) 1
127.0.0.1:6379[2]> sadd key1 c (integer) 1
127.0.0.1:6379[2]> sadd key2 c (integer) 1
127.0.0.1:6379[2]> sadd key2 d (integer) 1
127.0.0.1:6379[2]> sadd key2 e (integer) 1
127.0.0.1:6379[2]> SDIFF key1 key2 1) “a” 2) “b”
127.0.0.1:6379[2]> SDIFF key2 key1 1) “d” 2) “e”

  • 交集

127.0.0.1:6379[2]> SINTER key1 key2 1) “c” 共同好友就可以实现。

  • 并集

127.0.0.1:6379[2]> SUNION key1 key2 1) “d” 2) “c” 3) “e” 4) “b” 5) “a”

微博,A用户将所有他关注的人放在一个集合中(保证唯一),将他的粉丝也放在一个集合中。
可以设置共同关注、共同爱好、二度好友,推荐好友等


Hash(哈希)

可以想象成一个Map集合, key - <key - value> 相当于key对应的仍然是一个K V对。即 key - Map

127.0.0.1:6379[2]> hset myhash field1 zhiqiang (integer) 1 设置hash值
127.0.0.1:6379[2]> hmset myhash field1 hello field2 world field3 love OK 批量设置hash值
127.0.0.1:6379[2]> hmget myhash field1 field2 field3 1) “hello” 2) “world” 3) “love” 批量获取hash值
127.0.0.1:6379[2]> hgetall myhash 1) “field1” 2) “hello” 3) “field2” 4) “world” 5) “field3” 6) “love” 获取全部的hash值 展示形式为key - value
127.0.0.1:6379[2]> hdel myhash field1 (integer) 1 删除hash中某个字段值,对应的value也没有了
127.0.0.1:6379[2]> hgetall myhash 1) “field2” 2) “world” 3) “field3” 4) “love”
hlen 获取hash中有多少键值对
Hexists myhash field1 判断hash中的指定字段是否存在

只获取feild

127.0.0.1:6379[2]> hkeys myhash

  1. “field2”
  2. “field3”

只获取value

127.0.0.1:6379[2]> hvals myhash

  1. “world”
  2. “love”

hashi中的自增自减

127.0.0.1:6379[2]> hset myhash field3 5 (integer) 0
127.0.0.1:6379[2]> HINCRBY myhash field3 1 (integer) 6
127.0.0.1:6379[2]> HINCRBY myhash field3 -1 (integer) 5 设置增量为-1 就变成了递减
127.0.0.1:6379[2]> HINCRBY myhash field3 -1 (integer) 4

不存在则创建

127.0.0.1:6379[2]> hsetnx myhash field5 88
(integer) 1
127.0.0.1:6379[2]> hsetnx myhash field5 88
(integer) 0

hash的应用

127.0.0.1:6379[2]> hmset user:1 name zhiqiang age 26 height 180 OK
127.0.0.1:6379[2]> hget user:1 name “zhiqiang”
127.0.0.1:6379[2]> hmget user:1 (error) ERR wrong number of arguments for ‘hmget’ command
127.0.0.1:6379[2]> hgetall user:1 1) “name” 2) “zhiqiang” 3) “age” 4) “26” 5) “height” 6) “180”
127.0.0.1:6379[2]> hvals user:1 1) “zhiqiang” 2) “26” 3) “180” 使用hvals来获取user:1所对应的value键值对
直接使用user:1 来当key值 而value值对应的是多个键值对,如name:xxx age: 27
hash变更的数据 user name age,尤其是用户信息之类的,经常变动的信息! hash更适合于对象的存储,String更适合字符串的存储。

Zset(有序集合)

在set的基础上,增加了一个值 set k1 v1 zset k1 score1 v1

127.0.0.1:6379[2]> zset myset 1 one (error) ERR unknown command zset, with args beginning with: myset, 1, one,
127.0.0.1:6379[2]> zadd myset 1 one (integer) 1
127.0.0.1:6379[2]> zadd myset 2 two 3 three (integer) 2 添加多个值
127.0.0.1:6379[2]> zrange myset 0 -1 1) “one” 2) “two” 3) “three”

排序如何实现

127.0.0.1:6379[2]> zadd salary 8000 zhiqiang (integer) 1
127.0.0.1:6379[2]> zadd salary 7000 liuqian (integer) 1
127.0.0.1:6379[2]> zadd salary 5000 xiaoming (integer) 1
127.0.0.1:6379[2]> ZRANGEBYSCORE salary -inf +inf 1) “xiaoming” 2) “liuqian” 3) “zhiqiang” 正无穷到负无穷排序
可以附带value
127.0.0.1:6379[2]> ZRANGEBYSCORE salary -inf +inf withscores 1) “xiaoming” 2) “5000” 3) “liuqian” 4) “7000” 5) “zhiqiang” 6) “8000”

从负值到2500
127.0.0.1:6379[2]> ZRANGEBYSCORE salary -inf 6000 withscores 1) “xiaoming” 2) “5000”

从大到小进行排序
127.0.0.1:6379[2]> zrevrange salary 0 -1

  1. “zhiqiang”
  2. “liuqian”
    移除rem中的元素

127.0.0.1:6379[2]> zrange salary 0 -1 1) “xiaoming” 2) “liuqian” 3) “zhiqiang”
127.0.0.1:6379[2]> zrem salary xiaoming (integer) 1
127.0.0.1:6379[2]> zrange salary 0 -1 1) “liuqian” 2) “zhiqiang”

获取有序集合中的个数

zcard salary

获取有序集合中指定区间的数量

zcount salary 1 3

案例启示

set排序 存储班级成绩表 工资表
普通消息1 重要消息2 带权重进行判断!
排行榜应用实现 取TOP N 测试!

三种特殊数据类型

geospatial 地理位置

朋友的定位,附近的人,打车距离计算?

只有6个命令
GEOADD
GEODIST
GEOHASH
GEOPOS
GEORADIUS
GEORADIUSBYMEMBER

GEOADD 添加地理位置
两极无法添加,一般会下在城市数据,直接通过java程序一次性导入!
有效的经度从 -180 到 180度
有效的纬度从 -85.05112878到 85.05112878度
当坐标位置超过上述指定范围时,该命令会返回一个错误。
参数 key 值(经度、纬度、名称)

GEOPOS 获取当前定位:一定是一个坐标值
127.0.0.1:6379[2]> geopos china:city beijing chongqing 获取指定城市的经度和纬度

    1. “116.39999896287918091”
    2. “39.30000117660147652”
    1. “106.49999767541885376”
    2. “29.52999957900659211”

GEODIST 获取两人之间的位置
单位:
-m 表示 米
-km 千米
-mi 英里
-ft 英尺
127.0.0.1:6379[2]> GEODIST china:city beijing shanghai “1008342.6601” 北京到上海的直线距离
127.0.0.1:6379[2]> GEODIST china:city beijing shanghai km “1008.3427” 北京到上海的km距离

GEORADIUS以给定的经纬度为中心,查找某一半径内的元素
我附近的人?获得附近的人的地址,定位 ,通过半径来查询!

127.0.0.1:6379[2]> GEORADIUS china:city 110 30 500 km withcoord 查找以110 30为中心点 半径50km之内的元素

    1. “chongqing”
        1. “106.49999767541885376”
      1. “29.52999957900659211”
    1. “xian”
      1. “108.96000176668167114”
        1. “34.25999964418929977”

127.0.0.1:6379[2]> GEORADIUS china:city 110 30 500 km withcoord withdist 添加了withdist 显示了之间的距离

    1. “chongqing”
      1. “341.9374”
        1. “106.49999767541885376”
        2. “29.52999957900659211”
    1. “xian”
      1. “483.8340”
        1. “108.96000176668167114”
      2. “34.25999964418929977”

另外在参数中添加count 可以指定数量的元素

GEORADIUSBYMEMBER 找出位于指定范围内的元素,中心点是由给定的元素决定的
127.0.0.1:6379[2]> GEORADIUSBYMEMBER china:city beijing 1000 km 找出指定元素周围的元素

  1. “beijing”
  2. “xian”

GEOHASH 返回一个或者多个位置元素的GeoHash表示
该命令将返回11个字符的Geohash字符串 将二维的经纬度转换为一维的字符串,如果恋歌字符串越相似,则距离越近
127.0.0.1:6379[2]> GEOHASH china:city beijing chongqing 1) “wwfz8drghe0” 2) “wm5xzrybty0”

GEO的底层实现是zset 可以使用Zset命令来操作GEO
127.0.0.1:6379[2]> zrange china:city 0 -1 1) “chongqing” 2) “xian” 3) “hangzhou” 4) “shanghai” 5) “beijing” #查看地图中的全部元素
127.0.0.1:6379[2]> zrem china:city beijing (integer) 1 #移除某个指定的元素
127.0.0.1:6379[2]> zrange china:city 0 -1 1) “chongqing” 2) “xian” 3) “hangzhou” 4) “shanghai”

hyperloglogs

什么是基数?

A{ 1,3,5,7,8,9}
B{ 1,3,5,7,9}
基数(不重复元素) =5 ,可以接受误差

简介

redis 2.8.9版本就更新了Hyperloglog数据结构
基数统计的算法!
优点:占用的内存是固定的,2^64不同的元素的基数,只需要花费12kb的内存,如果要从内存角度来考虑的话Hyperloglog首选!
	网页的UV  (一个人访问一个网站多次,但还是算作一个人)
传统的方式是:使用set保存用户的id,然后就可以统计出set元素值作为标准判断!
这个方式如果保存大量的用户id的话,会比较麻烦,我们的目的是计数,而不是去保存用户id  舍近求远。
0.81%的容错率  统计UV任务,可以忽略不计!

测试使用
127.0.0.1:6379[2]> PFADD mykey a b c d e f g h i j (integer) 1
127.0.0.1:6379[2]> PFCOUNT mykey (integer) 10
127.0.0.1:6379[2]> PFADD mykey2 i j z x c v b n (integer) 1
127.0.0.1:6379[2]> PFCOUNT mykey2 (integer) 8
127.0.0.1:6379[2]> PFMERGE mykey3 mykey1 mykey2 OK #合并,交集
127.0.0.1:6379[2]> pfcount mykey3 (integer) 8
127.0.0.1:6379[2]> PFMERGE mykey3 mykey mykey2 OK
127.0.0.1:6379[2]> pfcount mykey3 (integer) 14 #计数

如果允许容错 就使用Hyperloglog !
如果不允许容错 就使用set或者自己的数据类型即可 !

bitmaps(位存储)

位存储

统计用户信息,活跃,不活跃! 登陆,未登陆!打卡,365打卡!两个状态的,都可以使用!
bitmaps位图 ,数据结构,都是操作二进制位来进行记录,就只有0 1两个状态
365天 = 365bit  1字节=8bit    46个字节左右

使用bitmap来记录 周一到周日的打卡
周一: 1 周二: 0 周三: 1 周四: 。。。。。
在这里插入图片描述
查看某一天是否有打卡!
getbit sign 3

127.0.0.1:6379[2]> getbit sign 3 查看周三有没有打卡
(integer) 1

统计打卡的天数

127.0.0.1:6379[2]> bitcount sign (integer) 4

还可以统计区间范围内的打卡天数

127.0.0.1:6379[2]> bitcount sign start end 不过bitmap是八进制需要*8
https://blog.csdn.net/ma_jiang/article/details/61421888 解释说明


Redis的事务

事务

Redis事务本质: 一组命令的集合! 一个事务中所有命令都会被序列化,在事务执行过程中,会按照顺序执行!
一次性,顺序性、排他性,执行一些列的命令!
------------队列 set set set 执行-------------------------------

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

Redis单条命令是保持原子性的,但是事务并不能保证原子性。

Redis的事务:

  • 开启事务(multi)
  • 执行操作(。。。。)
  • 执行事务(EXEC)

正常执行事务

127.0.0.1:6379[2]> multi OK
127.0.0.1:6379[2]> set k1 v1 QUEUED
127.0.0.1:6379[2]> set k2 v2 QUEUED
127.0.0.1:6379[2]> set k3 v3 QUEUED
127.0.0.1:6379[2]> get k2 QUEUED
127.0.0.1:6379[2]> exec 1) OK 2) OK 3) OK 4) “v2”

放弃事务

127.0.0.1:6379[2]> multi OK
127.0.0.1:6379[2]> set k1 v1 QUEUED
127.0.0.1:6379[2]> set k2 v2 QUEUED
127.0.0.1:6379[2]> set k3 v4 QUEUED
127.0.0.1:6379[2]> DISCARD OK

编译型异常(代码有问题!命令有错),事务中所有的命令都不会被执行!
如读取了一个不存在的值

运行时异常( 1/0),如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以被执行的,该错误命令会抛出异常。
如对单条语句中str当成int

Redis乐观锁

监控!

悲观锁

  • 很悲观,认为什么时候都会出现错误,无论做什么都会加锁!

乐观锁

  • 很乐观,认为什么时候都不会出现问题,所以不会上锁,更新数据的时候去判断一下,根据version来查看当前的数据有没有被改动过,
  • 获取Version
  • 更新的时候比较Version

Redis测监视测试

正常执行成功!

127.0.0.1:6379[2]> set money 100 OK
127.0.0.1:6379[2]> set out 0 OK
127.0.0.1:6379[2]> watch money OK
127.0.0.1:6379[2]> multi OK
127.0.0.1:6379[2]> decrby money 20 QUEUED
127.0.0.1:6379[2]> incrby out 20 QUEUED
127.0.0.1:6379[2]> exec 1) (integer) 80 2) (integer) 20

测试多线程修改值,使用watch、可以当做redis的乐观锁操作

watch money 监控money变量
multi 开启事务
DECRBY money 10
INCRBY out 10
exec 在执行此操作之前,开启另外一个线程,修改了money的值,这时候就会导致exec错误,事务执行失败!

如果事务执行失败,先UNWATCH 解锁,然后 watch money 重新监听
秒杀操作 ,都可以使用Redis的Version来实现。

Jedis

使用java来操作redis

什么是jedis? 是Redis官方推荐的java连接开发工具!使用java操作redis的中间件!如果要使用java操作redis,一定要对jedis十分的熟悉。

  1. 导入pom依赖
    <!--导入jedis的包-->
    <dependencies>
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <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>

常用的API


测试、get、set

//1、  new Jedis对象即可
      Jedis jedis = new Jedis("127.0.0.1", 6379);

      /*  //jedis 所有的命令就是之前在命令行的所有指令
        System.out.println(jedis.ping());   //测试连通
        System.out.println("选择数据库"+jedis.select(2));
        System.out.println("清空数据:" + jedis.flushDB());
        System.out.println("判断某个键是否存在:"+ jedis.exists("username"));
        System.out.println("新增<'username','zhiqiang'>键值对:"+ jedis.set("username","zhiqiang"));
        System.out.println("新增<'password,'password'>键值对 "+ jedis.set("password","password"));
        System.out.println("系统中所有的键如下:");
        Set<String> keys = jedis.keys("*");
        System.out.println(keys);
        System.out.println("删除password的键"+ jedis.del("password"));
        System.out.println("判断键是否存在:"+ jedis.exists("password"));
        System.out.println("查看键所存储的值的类型"+ jedis.type("username"));
        System.out.println("随机返回key空间的一个"+ jedis.randomKey());
        System.out.println("重命名key"+ jedis.rename("username","name"));
        System.out.println("取出修改后的name"+ jedis.get("name"));
        System.out.println("按照索引查询"+ jedis.select(0));
        System.out.println("删除当前数据库中的所有key"+ jedis.flushDB());
        System.out.println("返回当前数据库中key的数目"+ jedis.dbSize());
        System.out.println("删除所有数据库中的key");*/

String

jedis.flushDB();
        System.out.println(jedis.set("key1","value1"));
        System.out.println(jedis.set("key2","value2"));
        System.out.println(jedis.set("key3","value3"));
        System.out.println("删除键key2: "+ jedis.del("key2"));
        System.out.println("获取键key2: "+ jedis.get("key2"));
        System.out.println("修改key1"+ jedis.set("key1","value1Changed"));
        System.out.println("获取key1的值"+ jedis.get("key1"));
        System.out.println("在key3后面追加值"+jedis.append("key3","End"));
        System.out.println("获取key3的值"+ jedis.get("key3"));

        System.out.println("增加多组键值对"+ jedis.mset("key01","value01","key02","value02"));
        System.out.println("获取多个键值对"+ jedis.mget("key01","key02"));
        System.out.println("删除多个键值对"+ jedis.del("key01","key02"));
        System.out.println("获取多个键值对"+ jedis.mget("key01","key02"));

        jedis.flushDB();
        System.out.println("===================新增键值对防止覆盖原先值======================");
        System.out.println(jedis.setnx("key1","value1"));
        System.out.println(jedis.setnx("key2","value2"));
        System.out.println(jedis.setnx("key2","value2-new"));
        System.out.println(jedis.get("key1"));
        System.out.println(jedis.get("key2"));

        System.out.println("==================新增键值对并设置有效时间=======================");
        System.out.println(jedis.setex("key3",2,"testValue"));
        System.out.println(jedis.get("key3"));
        try{
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(jedis.get("key3"));


        System.out.println("=================获取原值,更新为新值===========================");
        System.out.println(jedis.getSet("key2","key2Getset"));
        System.out.println(jedis.get("key2"));


        System.out.println("获取key2值的字串"+ jedis.getrange("key2",2,4));

List

jedis.flushDB();
      System.out.println("===================添加一个list=================");
      jedis.lpush("collection", "ArrayList", "Vector", "Stack", "HashMap", "WeakHashMap");
      jedis.lpush("collection", "HashSet");
      jedis.lpush("collection", "TreeSet");


      System.out.println("=================collection的内容============" + jedis.lrange("collections", 0, -1));
      System.out.println("=================collection区间0-3的元素" + jedis.lrange("collection", 0, 3));

      //删除列表指定的值,第二个参数为删除的个数(有重复时),后add进去的值先被删除,类似于出栈
      System.out.println("删除指定的元素" + jedis.lrem("collection", 2, "HashSet"));
      System.out.println("collection的内容" + jedis.lrange("collection", 0, -1));
      System.out.println("删除下表0-3区间之外的元素:" + jedis.ltrim("collection", 0, 3));
      System.out.println("collection的内容" + jedis.lrange("collection", 0, -1));
      System.out.println("collection的列表出栈(左端)" + jedis.lpop("collection"));
      System.out.println("collection的内容" + jedis.lrange("collection", 0, -1));
      System.out.println("collection添加元素,从列表右端,与lpush相对应:" + jedis.rpush("collection", "hellozhiqiang"));
      System.out.println("collection的内容" + jedis.lrange("collection", 0, -1));
      System.out.println("collection的列表出栈(右端)" + jedis.rpop("collection"));
      System.out.println("collection的内容" + jedis.lrange("collection", 0, -1));
      System.out.println("修改collection指定下标1的内容: " + jedis.lset("collection", 1, "linbiao"));
      System.out.println("collection的内容" + jedis.lrange("collection", 0, -1));
      System.out.println("=============================");
      System.out.println("获取collection的长度" + jedis.llen("collection"));
      System.out.println("获取下标为2的元素" + jedis.lindex("collection", 2));
      System.out.println("==================排序===========================");
      System.out.println("添加元素" + jedis.lpush("sortedList", "3", "4", "1", "7", "5"));
      System.out.println("collection排序前的元素" + jedis.lrange("collection", 0, -1));
      System.out.println(jedis.sort("sortedList"));*/

Set

 jedis.select(2);
      jedis.flushDB();
      System.out.println("向集合中添加元素(不重复)");
      System.out.println(jedis.sadd("eleset","a1","a2","a3","a4","a5","a6"));
      System.out.println(jedis.sadd("eleset","a7"));
      System.out.println(jedis.sadd("eleset","a7"));
      System.out.println("eleset的所有元素为"+ jedis.smembers("eleset"));
      System.out.println("删除一个元素a1"+ jedis.srem("eleset","a1"));
      System.out.println("所有的元素为"+ jedis.smembers("eleset"));
      System.out.println("删除两个元素"+ jedis.srem("eleset","a2","a3"));
      System.out.println("所有的元素为"+ jedis.smembers("eleset"));
      System.out.println("随机的移除集合中的一个元素"+ jedis.spop("eleset"));
      System.out.println("随机的移除集合中的一个元素"+ jedis.spop("eleset"));
      System.out.println("所有的元素为"+ jedis.smembers("eleset"));
      System.out.println("集合中包含元素的个数"+ jedis.scard("eleset"));

      System.out.println("判断a3是否在集合中"+jedis.sismember("eleset","a3"));
      System.out.println("判断a2是否在集合中"+jedis.sismember("eleset","a2"));
      System.out.println("判断a4是否在集合中"+jedis.sismember("eleset","a4"));
      System.out.println("====================================");

      System.out.println(jedis.sadd("eleset1","b1","b2","b3","b4","b5","b6","b7","b8"));
      System.out.println(jedis.sadd("eleset2","b1","b2","b3","b4","b5","b6"));

      System.out.println("将eleset1中删除b1并存入eleset3中"+jedis.smove("eleset1","eleset3","b1"));
      System.out.println("将eleset2中删除b2并存入eleset3中"+jedis.smove("eleset2","eleset3","b2"));

      System.out.println("eleSet1中的元素"+ jedis.smembers("eleset1"));
      System.out.println("eleSet3中的元素"+ jedis.smembers("eleset3"));


      System.out.println("=================集合运算=========================");
      System.out.println("eleset1中的元素"+ jedis.smembers("eleset1"));
      System.out.println("eleset2中的元素"+ jedis.smembers("eleset2"));
      System.out.println("eleset1和eleset2的交集"+jedis.sinter("eleset1","eleset2"));
      System.out.println("eleset1和eleset2的并集"+jedis.sunion("eleset1","eleset2"));
      System.out.println("eleset1和eleset2的差集"+ jedis.sdiff("eleset1","eleset2"));

      System.out.println("求eleset1和eleset2的交集,并将交集结果保存到dstkey中");
      System.out.println(jedis.sinterstore("eleset4","eleset1","eleset2"));

      System.out.println("查看eleset4中的元素"+ jedis.smembers("eleset4"));

Hash

    jedis.select(2);
        jedis.flushDB();
      HashMap<String,String> map= new HashMap<>();
      map.put("key1","value1");
      map.put("key2","value2");
      map.put("key3","value3");
      map.put("key4","value4");
      //添加名称为hash(key)的hash元素  hash实质上是key - (key- value)
      jedis.hmset("hash",map);
      jedis.hset("hash","key5","value5");

        System.out.println("散列hash的所有键值对:"+ jedis.hgetAll("hash"));
        System.out.println("hash的所有键"+jedis.hkeys("hash"));
        System.out.println("hash的所有值"+ jedis.hvals("hash"));
        System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6"+ jedis.hincrBy("hash","key6",1));
        System.out.println("散列hash的所有键值对:"+ jedis.hgetAll("hash"));
        System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6"+ jedis.hincrBy("hash","key6",1));
        System.out.println("散列hash的所有键值对:"+ jedis.hgetAll("hash"));
        System.out.println("删除一个或者多个键值对"+ jedis.hdel("hash","key2"));
        System.out.println("散列hash的所有键值对:"+ jedis.hgetAll("hash"));
        System.out.println("散列hash中键值对的个数"+ jedis.hlen("hash"));
        System.out.println("判断hash中是否存在key2: "+ jedis.hexists("hash","key2"));
        System.out.println("判断hash中是否存在key3: "+ jedis.hexists("hash","key3"));
        System.out.println("获取hash中的值"+ jedis.hget("hash","key3"));
        System.out.println("获取hash中的值"+ jedis.hmget("hash","key3","key4"));

Zset

Jedis再次理解事务

package com.zryy;

import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class testTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1",6379);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello","world");
        jsonObject.put("name","zhiqiang");

        //开启事务
        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("user2"));
            jedis.close();//关闭连接
        }
    }
}

执行过程中出现异常,执行失败
package com.zryy;

import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class testTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1",6379);
        jedis.flushDB();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello","world");
        jsonObject.put("name","zhiqiang");

        //开启事务
        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("user2"));
            jedis.close();//关闭连接
        }
    }
}

java.lang.ArithmeticException: / by zero
at com.zryy.testTX.main(testTX.java:21)
null
null #两个user都是null值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
尚硅谷是一个教育机构,他们提供了一份关于Redis学习笔记。根据提供的引用内容,我们可以了解到他们提到了一些关于Redis配置和使用的内容。 首先,在引用中提到了通过执行命令"vi /redis-6.2.6/redis.conf"来编辑Redis配置文件。这个命令可以让你进入只读模式来查询"daemonize"配置项的位置。 在引用中提到了Redis会根据键值计算出应该送往的插槽,并且如果不是该客户端对应服务器的插槽,Redis会报错并告知应该前往的Redis实例的地址和端口。 在引用中提到了通过修改Redis的配置文件来指定Redis的日志文件位置。可以使用命令"sudo vim /etc/redis.conf"来编辑Redis的配置文件,并且在文件中指定日志文件的位置。 通过这些引用内容,我们可以得出结论,尚硅谷的Redis学习笔记涵盖了关于Redis的配置和使用的内容,并提供了一些相关的命令和操作示例。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Redis学习笔记--尚硅谷](https://blog.csdn.net/HHCS231/article/details/123637379)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Redis学习笔记——尚硅谷](https://blog.csdn.net/qq_48092631/article/details/129662119)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值