Redis学习

为什么要用Nosql?

1、单机MySQL的年代

Redis学习_第1张图片90年代,一个基本的网站访问量不会太大,单个数据库完全足够
呢个时候服务器更多的是使用静态的html,服务器根本没有太大压力
思考一下,这种情况下:整个网站的瓶颈是什么?
1、数据量如果太大,一个机器放不下
2、数据的索引(B+tree),一个机器内存也放不下, 超过三百万就必须建立索引了
3、访问量(读写混合),一个服务器承受不了。

只要你出现以上三种情况之一,那么你就必须要晋级。

2、Memcached(缓存)+MySQL+垂直拆分(读写分离)

网站80%的情况都是在读,每次都去查询数据库的画话就十分麻烦!所以说我们希望减轻数据的压力,我们可以用缓存来保证效率!
发展过程:优化数据结构和索引–>文件缓存(IO)–>Memcached(当时最热门的技术!)
Redis学习_第2张图片

3、分库分表+水平拆分+MySQL集群

本质:数据库(读,写)
早些年MyISAM:表锁,十分影响效率!高并发下就会出现严重的锁问题。
转战Innodb:行锁
慢慢的就开始使用分库分表来解决写的压力!MySQL在那个年代推出了表分区!这个并没有多少公司使用!
MySQL的集群,很好的满足了那个年代的所有需求!
Redis学习_第3张图片

4、如今最近的年代

2010-2020十年之间,世界已经发生了翻天覆地的变化;(定位,也是一种数据,音乐热榜!)
MySQL等关系型数据库就不够用了!数据量很多,变化很快!
MySQL有的使用它来存储一些比较大的文件,博客,图片!数据表很大,效率就低了!如果有一种数据库来专门处理这种数据,MySQL压力就变得十分小(研究如何处理这些问题!)大数据的IO压力下,表几乎没法更改。

目前一个基本的互联网项目

Redis学习_第4张图片

为什么要用NoSQL!

用户的个人信息,社交网络,地理位置。用户自己产生的数据,用户日志等等爆发式增长!
这个时候我们就需要NoSQL数据库,NoSQL可以很好的解决这个问题

什么是NoSQL

NoSQL = Not Only SQL

关系型数据库:表格,行,列
泛指非关系型数据库,随着web2.0互联网诞生!传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社区!暴露出来很多难以克服的问题,NoSQL在当今大数据环境下发展十分迅速,redis是发展最快的,而且是我们当下必须要掌握的一个技术。
很多的数据类型用户的个人信息,社交网络,地理位置。这些数据类型的存储不需要一个固定的格式!不需要的多余的操作就可以进行横向扩展!
Map使用键值对来控制。

NoSQL特点

1、方便扩展(数据之间没有关系,很好扩展)
2、大数据量高性能(Redis一秒可以写八万次,一秒可以读十一万,NoSQL缓存记录级,是一种细粒度的缓存,性能会比较高!)
3、数据类型是多样型的(不需要事先设计数据库!随取随用!如果是数据量比较大的表,很多人就无法设计了)
4、传统的RDBMS和NoSQL

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

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

真正的在公司实践:NoSQL+RDBMS一起使用才是最强的.

NoSQL四大分类

键值对存储

  • 新浪:redis
  • 美团:redis+Tair
  • 阿里,百度:redis+memcache

文档型数据库(bson格式和json一样)

  • MongoDB(一般必须要掌握)
    MongoDB是一个基于分布式文件存储的数据库,c++编写,主要用来处理大量的文档
    MongoDB是一个介于关系型数据库和非关系型数据库中间品!MongoDB是非关系型数据库中功能最丰富,最像关系型数据库的。

  • CouchDB(国外的一款数据库)

列存储数据库

  • HBase
  • 分布式文件系统

图关系数据库

  • 他不是存图形,而是存关系,比如朋友圈,社交网络,广告推荐等等。
  • Neo4j InfoGrid
    Redis学习_第5张图片

Redis是什么

Redis (Remote Dictionary Server)远程字典服务
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

Redis能干什么

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

Redis特性

1、多样的数据
2、持久化
3、集群
4、事务
5、…

测试性能

redis-benchmark是一个压力测试工具!
官方自带的性能测试工具!
redis-benchmark命令参数:
Redis学习_第6张图片

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

Redis学习_第7张图片
测试结果分析
Redis学习_第8张图片

Redis基础知识

  • Redis默认有16个数据库。
    Redis学习_第9张图片
  • 默认使用第0个,可以使用select进行切换
select 3

Redis学习_第10张图片

  • 查询所有key
keys *

在这里插入图片描述

  • 清除当前数据库
flushdb
  • 清除所有数据库
flushall
  • Redis是单线程

官方表示,Redis是基于内存操作的,CPU不是Redis性能的瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以用单线程实现,就用单线程了!所以就是用了单线程!
Redis是C语言编写的,官方提供数据为100000+的QPS,完全不比同样是使用key-value的MemCache差。

  • Redis为什么单线程还这么快?

1.redis是基于内存的,内存的读写速度非常快;
2.redis是单线程的,省去了很多上下文切换线程的时间;
3.redis使用多路复用技术,可以处理并发的连接;

目前,多路复用主要有三种技术:select,poll,epoll。它们出现的顺序是按时间先后的,越排后的技术改正了之前技术的缺点。epoll是最新的也是目前最好的多路复用技术。

  • select:
    1.会修改传入的参数,对于多个调用的函数来说非常不友好;
    2.要是sock(io流出现了数据),select只能轮询这去找数据,对于大量的sock来说开销很大;
    3.不是线程安全的,很恐怖;
    4.只能监视1024个连接;
  • poll:
    1.还不是线程安全的…
    2.去掉了1024个连接的限制;
    3.不修改传入的参数了;
  • epoll:
    1.线程安全了;
    2.epoll不仅能告诉你sock有数据,还能告诉你哪个sock有数据,不用轮询了;
    3.however,只支持linux系统;

1、误区一:高性能服务器一定是多线程的?
2、误区二:多线程(CPU会进行上下文切换)一定比单线程效率高?
速度:CPU > 内存 > 硬盘
核心:redis是将所有的数据全部放在内存中,所以说使用单线程效率是最高的,多线程(CPU会上下文切换:耗时的操作!!!),对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个CPU上的,在内存情况下,这个就是最佳方案。

五大数据类型

Redis-key

127.0.0.1:6379> FLUSHALL  #清空所有
OK
127.0.0.1:6379> keys * #查看所有key
(empty list or set)
127.0.0.1:6379> clear
127.0.0.1:6379> set name zhaoli #设置key
OK
127.0.0.1:6379> get name #获取key对应的value
"zhaoli"
127.0.0.1:6379> keys * 
1) "name"
127.0.0.1:6379> EXISTS name #判断当前key是否存在 存在返回1,不存在返回0
(integer) 1
127.0.0.1:6379> EXISTS name1
(integer) 0
127.0.0.1:6379> move name 1 #将当前数据移动到指定库中
(integer) 1
127.0.0.1:6379> select 1 #切换到1号库
OK
127.0.0.1:6379[1]> keys *
1) "name"
127.0.0.1:6379[1]> get name
"zhaoli"
127.0.0.1:6379[1]> del name #删除指定的key
(integer) 1
127.0.0.1:6379[1]> keys *
(empty list or set)
127.0.0.1:6379[1]> set name zhaoli
OK
127.0.0.1:6379[1]> EXPIRE name 10 #设置过期时间
(integer) 1
127.0.0.1:6379[1]> ttl name #查看当前key剩余时间
(integer) 6
127.0.0.1:6379[1]> ttl name
(integer) 4
127.0.0.1:6379[1]> ttl name
(integer) 3
127.0.0.1:6379[1]> ttl name
(integer) 3
127.0.0.1:6379[1]> ttl name
(integer) 1
127.0.0.1:6379[1]> ttl name
(integer) -2
127.0.0.1:6379[1]> set name zhaoli
OK
127.0.0.1:6379[1]> set age 19
OK
127.0.0.1:6379[1]> type name #查看当前key类型
string
127.0.0.1:6379[1]> type age
string

不知道的命令可以去官网查看:https://redis.io/commands

  • String
###########################################################
127.0.0.1:6379[1]> set key1 v1
OK
127.0.0.1:6379[1]> APPEND key1 hello    #追加字符串
(integer) 7
127.0.0.1:6379[1]> get key1
"v1hello"
127.0.0.1:6379[1]> STRLEN key1        #获取字符串长度
(integer) 7
127.0.0.1:6379[1]> APPEND key zhaoli   #如果当前key不存在,则相当于set key
(integer) 6
127.0.0.1:6379[1]> keys *
1) "key1"
2) "key"
3) "age"
4) "name"
############################################################
127.0.0.1:6379[1]> set views 0
OK
127.0.0.1:6379[1]> INCR views  #自增一
(integer) 1
127.0.0.1:6379[1]> get views
"1"
127.0.0.1:6379[1]> DECR views   #自减一
(integer) 0
127.0.0.1:6379[1]> get views
"0"
127.0.0.1:6379[1]> INCRBY views 10  #自增  步长为10
(integer) 10
127.0.0.1:6379[1]> get views   
"10"
127.0.0.1:6379[1]> DECRBY views 5    #自减  步长为5
(integer) 5
127.0.0.1:6379[1]> get views
"5"
############################################################
字符串范围range
127.0.0.1:6379[1]> set key1 hello,redis
OK
127.0.0.1:6379[1]> STRLEN key1        #获取字符串长度
(integer) 11
127.0.0.1:6379[1]> get key1
"hello,redis"
127.0.0.1:6379[1]> GETRANGE key1 0 3    #截取字符串[0,3]
"hell"
127.0.0.1:6379[1]> GETRANGE key1 0 -1      #截取全部字符串
"hello,redis"
############################################################
替换
127.0.0.1:6379[1]> set key2 abcdefg
OK
127.0.0.1:6379[1]> SETRANGE key2 2 xxx
(integer) 7
127.0.0.1:6379[1]> get key2
"abxxxfg"
############################################################
setex(set with expire) #设置过期时间
setnx(set if not exist) #如果不存在再设置(在分布式锁中会常常使用)
127.0.0.1:6379[1]> setex key1 30 hello  #设置key1的值为hello,30秒后过期
OK
127.0.0.1:6379[1]> ttl key1
(integer) 22
127.0.0.1:6379[1]> set mykey zhaoli
OK
127.0.0.1:6379[1]> setnx mykey xiaocong   #如果mykey存在,创建失败
(integer) 0
127.0.0.1:6379[1]> keys *
1) "mykey"
############################################################
批量获取和设值
127.0.0.1:6379[1]> mset k1 v1 k2 v2 k3 v3        #同时设置多个值
OK
127.0.0.1:6379[1]> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379[1]> mget k1 k2 k3          #同时获取多个值	
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379[1]> msetnx k1 v2 k4 v4    #msetnx 是一个原子性的操作,要么一起成功,要么一起失败。
(integer) 0
############################################################
#对象 
set user:1 {name:zhangsan,age:3}   #设置一个user:1 对象,值为一个json字符串来保存一个对象!
这里的key是一个巧妙的设计,对象:{id}{field}
127.0.0.1:6379[1]> mset user:1:name zhangsan user:1:age 19
OK
127.0.0.1:6379[1]> mget user:1:name user:1:age
1) "zhangsan"
2) "19"
###############################################################
getset        #先get后set
127.0.0.1:6379[1]> getset key1 redis   #如果不存在值,则返回nil
(nil)
127.0.0.1:6379[1]> get key1
"redis"
127.0.0.1:6379[1]> getset key1 mongodb   #如果存在值,返回原来的值,将值设置为新值
"redis"
127.0.0.1:6379[1]> get key1
"mongodb"
  • List(redis中list可以玩成栈,队列,阻塞队列)
#############################################################
LPUSH
RPUSH
127.0.0.1:6379> LPUSH list one        #将一个值或多个值插入到列表左部
(integer) 1
127.0.0.1:6379> LPUSH list two
(integer) 2
127.0.0.1:6379> LPUSH list three
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1       #获取list'中的值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> LRANGE list 0 1       #获取list中指定范围的值
1) "three"
2) "two"
127.0.0.1:6379> RPUSH list right          #将一个值或多个值插入到列表右部
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"

############################################################
LPOP
RPOP
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> LPOP list      #弹出左边第一个元素
"three"
127.0.0.1:6379> RPOP list      #弹出右边第一个元素
"right"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
############################################################
LINDEX
127.0.0.1:6379> LINDEX list 0        #通过下标获取list中某一个值
"two"
127.0.0.1:6379> LINDEX list 1
"one"
#############################################################
LLEN
127.0.0.1:6379> LLEN list          #返回list长度
(integer) 2
##############################################################
LREM          #移除指定值!
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "two"
3) "one"
4) "one"
127.0.0.1:6379> LREM list 2 two  #移除list集合中指定个数的value,精确匹配
(integer) 2
127.0.0.1:6379> LRANGE list 0 -1
1) "one"
2) "one"
127.0.0.1:6379> LREM list 1 one
(integer) 1
127.0.0.1:6379> LRANGE list 0 -1
1) "one"
############################################################
127.0.0.1:6379> RPUSH list hello
(integer) 1
127.0.0.1:6379> RPUSH list hello1
(integer) 2
127.0.0.1:6379> RPUSH list hello12
(integer) 3
127.0.0.1:6379> RPUSH list hello13
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "hello"
2) "hello1"
3) "hello12"
4) "hello13"
127.0.0.1:6379> LTRIM list 1 2       #截取list指定区间
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "hello1"
2) "hello12"
############################################################
RPOPLPUSH      #将最后一个元素弹出并添加进新的list
127.0.0.1:6379> LRANGE list 0 -1
1) "hello1"
2) "hello12"
127.0.0.1:6379> RPOPLPUSH list list
"hello12"
127.0.0.1:6379> LRANGE list 0 -1
1) "hello12"
2) "hello1"
############################################################
LSET       #将列表中指定下标元素替换为新的值,更新操作
127.0.0.1:6379> LPUSH list value1
(integer) 1
127.0.0.1:6379> LRANGE list 0 0
1) "value1"
127.0.0.1:6379> LSET list 0 items
OK
127.0.0.1:6379> LRANGE list 0 0
1) "items"
127.0.0.1:6379> LSET list 1 items
(error) ERR index out of range
############################################################
LINSERT       #在指定元素前面或后面插入元素
127.0.0.1:6379> RPUSH list hello
(integer) 1
127.0.0.1:6379> RPUSH list world
(integer) 2
127.0.0.1:6379> LINSERT list before world other
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1
1) "hello"
2) "other"
3) "world"
127.0.0.1:6379> LINSERT list after world new
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "hello"
2) "other"
3) "world"
4) "new"

栈 (LPUSH,LPOP) 队列(LPUSH,RPOP)

  • Set(set中值不能重复)
127.0.0.1:6379> SADD myset hello      # set集合中添加值
(integer) 1
127.0.0.1:6379> SADD myset world
(integer) 1
127.0.0.1:6379> SADD myset zhaoli
(integer) 1
127.0.0.1:6379> SMEMBERS myset      #查看值定set中的所有值
1) "world"
2) "zhaoli"
3) "hello"
127.0.0.1:6379> SISMEMBER myset hello  #判断set中是否存在某一个值
(integer) 1
############################################################	
127.0.0.1:6379> SCARD myset   #获取set中元素个数
(integer) 3
############################################################
127.0.0.1:6379> SMEMBERS myset
1) "world"
2) "zhaoli"
3) "hello"
127.0.0.1:6379> SREM myset world       #移除set中指定元素
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "zhaoli"
2) "hello"
############################################################
127.0.0.1:6379> SMEMBERS myset
1) "a2"
2) "zhaoli"
3) "hello"
4) "a4"
5) "a1"
6) "a3"
127.0.0.1:6379> SRANDMEMBER myset      #随机抽选出一个元素
"zhaoli"
127.0.0.1:6379> SRANDMEMBER myset
"hello"
127.0.0.1:6379> SRANDMEMBER myset 3   #随机抽选出指定个数元素
1) "a1"
2) "a2"
3) "hello"
127.0.0.1:6379> SRANDMEMBER myset 3
1) "a4"
2) "a1"
3) "hello"
127.0.0.1:6379> SRANDMEMBER myset 33   #超出总的元素个数则显示全部
1) "zhaoli"
2) "a4"
3) "a1"
4) "a3"
5) "a2"
6) "hello"
###########################################################
127.0.0.1:6379> SMEMBERS myset
1) "zhaoli"
2) "a4"
3) "a1"
4) "a3"
5) "hello"
6) "a2"
127.0.0.1:6379> SPOP myset   #随机删除一个元素
"a3"
127.0.0.1:6379> SPOP myset
"a1"
127.0.0.1:6379> SMEMBERS myset
1) "zhaoli"
2) "a4"
3) "hello"
4) "a2"
127.0.0.1:6379> SPOP myset 2  #随机删除指定个数的元素
1) "zhaoli"
2) "a2"
###############################################################
SMOVE  #将一个指定元素移动到另一个set
127.0.0.1:6379> SADD myset hello
(integer) 1
127.0.0.1:6379> SADD myset world
(integer) 1
127.0.0.1:6379> SADD myset zhaoli
(integer) 1
127.0.0.1:6379> SADD set2 set2
(integer) 1
127.0.0.1:6379> SMOVE myset set2 zhaoli
(integer) 1
127.0.0.1:6379> SMEMBERS myset 
1) "world"
2) "hello"
127.0.0.1:6379> SMEMBERS set2
1) "set2"
2) "zhaoli"
############################################################
127.0.0.1:6379> sadd key1 a
(integer) 1
127.0.0.1:6379> sadd key1 b
(integer) 1
127.0.0.1:6379> sadd key1 c
(integer) 1
127.0.0.1:6379> sadd key2 c
(integer) 1
127.0.0.1:6379> sadd key2 d
(integer) 1
127.0.0.1:6379> sadd key2 f
(integer) 1
127.0.0.1:6379> SDIFF key1 key2  #差集
1) "a"
2) "b"
127.0.0.1:6379> SINTER key1 key2  #交集 共同好友可以通过交集实现
1) "c"
127.0.0.1:6379> SUNION key1 key2  #并集
1) "c"
2) "a"
3) "b"
4) "f"
5) "d"

微博,A用户将所有的关注的人放在一个set中!,将他的粉丝也放在一个集合中!
共同关注,共同爱好,二度好友,推荐好友(六度分割理论!!+)

  • Hash
    map集合,key-map这时候的值是一个map集合!本质和String没有太大区别,还是一个简单的key-value
127.0.0.1:6379> hset myhash k1 v1       #	set一个具体的key-value
(integer) 1
127.0.0.1:6379> hget myhash k1
"v1"
127.0.0.1:6379> hmset myhash k2 v2 k3 v3    #set多个key-vaue
OK
127.0.0.1:6379> hmget k1 k2 k3
1) (nil)
2) (nil)
127.0.0.1:6379> hmget myhash k1 k2 k3   #获取多个key-value
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> hgetall myhash   #获取全部数据
1) "k1"
2) "v1"
3) "k2"
4) "v2"
5) "k3"
6) "v3"
##########################################################
127.0.0.1:6379> hdel myhash k1 k2   #删除指定key的值
(integer) 2
127.0.0.1:6379> hgetall myhash
1) "k3"
2) "v3"
##########################################################
127.0.0.1:6379> hlen myhash     #获取hash表的字段数量
(integer) 1
##########################################################
127.0.0.1:6379> HEXISTS myhash k3   #判断hash表中指定字段是否存在
(integer) 1
127.0.0.1:6379> HEXISTS myhash k1
(integer) 0
127.0.0.1:6379> hkeys myhash   #获取全部key
1) "k3"
127.0.0.1:6379> hvals myhash  #获取全部值
1) "v3"
##########################################################
127.0.0.1:6379> hset myhash k1 1
(integer) 1
127.0.0.1:6379> HINCRBY myhash k1 2      #自增步长为2
(integer) 3
127.0.0.1:6379> HINCRBY myhash k1 -1
(integer) 2
127.0.0.1:6379> HSETNX myhash k2 hello   #如果不存在则可以设置
(integer) 1
127.0.0.1:6379> HSETNX myhash k2 world   #如果存在则不能设置
(integer) 0

hash更适合对象的存储,String更适合字符串的存储

  • Zset(有序集合)
    在set基础上,增加了一个值,set k1 v1 zset k1 score1 v1
127.0.0.1:6379> ZADD myset 1 one      #zset 添加一个值
(integer) 1
127.0.0.1:6379> ZADD myset 2 two 3 three      #zset添加多个值
(integer) 2
127.0.0.1:6379> ZRANGE myset 0 -1
1) "one"
2) "two"
3) "three"
############################################################
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf     #显示全部用户 从小到大
1) "zahngsan"
2) "xiaocong"
3) "zhaoli"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores  #显示全部用户并且附带分数
1) "zahngsan"
2) "500"
3) "xiaocong"
4) "1500"
5) "zhaoli"
6) "2500"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 1500 withscores #显示工资小于1500元的用户的生序排列
1) "zahngsan"
2) "500"
3) "xiaocong"
4) "1500"
127.0.0.1:6379> ZREVRANGE salary 0 -1 withscores     #显示全部用户并且附带分数降序排列
1) "zhaoli"
2) "2500"
3) "xiaocong"
4) "1500"
5) "zahngsan"
6) "500"
##########################################################################
127.0.0.1:6379> ZREM salary zhaoli     #移除指定元素
(integer) 1
##########################################################################
127.0.0.1:6379> zcard salary       #获取有序集合元素个数
(integer) 3
#############################################################
127.0.0.1:6379> ZCOUNT myset 1 2       #获取指定区间成员数量
(integer) 2
##############################################################

应用案例:set排序 存储班级成绩排序表,工资排序表
普通消息 0 ,重要消息 1 带权重
网站热搜排行榜,取Top N

三种特殊数据类型

  • geospatial(地理位置)
    Redis的Geo在Redis3.2版本就推出了!这个功能可以推算地理位置信息,两地之间的距离,方圆几里的人。
    朋友的定位,附近的人,打车位置?
    在线经纬度查询:http://www.jsons.cn/lngcode/
    Redis学习_第11张图片

有效的经度从-180度到180度。
有效的纬度从-85.05112878度到85.05112878度。

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 shanghai
(integer) 1
127.0.0.1:6379> GEOADD china:city 106.50 29.53 chongqing
(integer) 1
127.0.0.1:6379> GEOADD china:city 120.15 30.28 hangzhou
(integer) 1
127.0.0.1:6379> GEOADD china:city 125.14 42.92 xian
(integer) 
##############################################################
#获取指定位置经纬度  GEOPOS key members
127.0.0.1:6379> GEOPOS china:city beijing chongqing
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
2) 1) "106.49999767541885376"
   2) "29.52999957900659211"
###########################################################
#查看两地之间的距离
127.0.0.1:6379> GEODIST china:city beijing shanghai
"1067378.7564"
127.0.0.1:6379> GEODIST china:city beijing shanghai km
"1067.3788"
############################################################
#获取以给定的经纬度为中心,找出某一半径范围内的元素
#显示以110 30 为中心半径1000km的元素以及他到中心点的距离
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist
1) 1) "chongqing"
   2) "341.9374"
2) 1) "hangzhou"
   2) "976.4868"
#显示以110 30 为中心半径1000km的元素以及他的经纬度
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withcoord
1) 1) "chongqing"
   2) 1) "106.49999767541885376"
      2) "29.52999957900659211"
#显示以110 30 为中心半径1000km的元素以及他到中心点的距离,显示数量为1
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord count 1
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
#显示以110 30 为中心半径1000km的元素以及他到中心点的距离,显示数量为2
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord count 2
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "hangzhou"
   2) "976.4868"
   3) 1) "120.15000075101852417"
      2) "30.2800007575645509"
#显示以110 30 为中心半径1000km的元素以及他到中心点的距离,显示数量为3
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord count 3
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "hangzhou"
   2) "976.4868"
   3) 1) "120.15000075101852417"
      2) "30.2800007575645509"
#################################################################
#查询指定位置周围固定距离的元素
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km 
1) "beijing"
2) "xian"
############################################################
#Geohash该命令将返回11个字符的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) "hangzhou"
3) "shanghai"
4) "beijing"
5) "xian"
127.0.0.1:6379> ZREM china:city beijing
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqing"
2) "hangzhou"
3) "shanghai"
4) "xian"
  • hyperloglog

什么是基数?

A{1,3.4,5,4}
B{1,3,4,5}
基数(不重复元素个数) = 4 可以接受误差!

简介

Redis2.8.9版本更新了Hyperloglog数据结构
Redis Hyperloglog 基数统计的算法!
优点:占用的内存是固定的,2^64不同元素的基数,只需要费12KB的内存。如果从内存角度比较的话,Hyperloglog首选!
网页的UV(一个人访问一个网站多次,但还算作一个人)
传统的方式,set保存用户id,然后统计set中元素数量作为标准判断
这个方式如果存在大量用户,就会占用大量内存,就会比较麻烦!我们的目的是基数,而不是保存用户id。

0.81%错误率,UV统计是可以不记的

127.0.0.1:6379> PFADD mykey a b c d e      #创建第一组元素mykey
(integer) 1
127.0.0.1:6379> PFADD mykey2 e f j l n l    #创建第二组元素mykey2 
(integer) 1
127.0.0.1:6379> PFCOUNT mykey      #统计mykey元素数量
(integer) 5
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2  #合并两组mykey mykey2 ==》mykey3 并集
OK
127.0.0.1:6379> PFCOUNT mykey3 #查看并集数量
(integer) 9

如果允许容错,那么一定可以使用Hyperloglog
如何不允许容错,使用set或自己的数据类型。

  • bitmap

位存储

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

测试

使用Bitmaps来记录一周的打卡记录

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 1
(integer) 0
127.0.0.1:6379> SETBIT sign 3 0
(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 1
(integer) 0

获取打卡记录

127.0.0.1:6379> GETBIT sign 3
(integer) 0
127.0.0.1:6379> GETBIT sign 4
(integer) 1

获取打卡次数

127.0.0.1:6379> BITCOUNT sign
(integer) 4

事务

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

-----队列 set set set 执行-------

Redis事务没有隔离级别的概念
所有命令在事务中,并没有直接执行,只有发起执行命令才会执行!exec
Redis单条命令保证原子性,但是事务不保证原子性
Redis事务:

  • 开启事务(multi)
  • 命令入队
  • 执行事务(exec)

正常事务执行

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

取消执行

127.0.0.1:6379> MULTI  #开启事务
OK
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> DISCARD  #取消事务
OK
127.0.0.1:6379> get k4
(nil)

编译时异常,命令有错!事务中所有的命令都不会被执行

127.0.0.1:6379> MULTI 
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> GETSET k2
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.

运行时异常,如果对列中某条语句存在运行时异常,那么这条语句会执行失败,抛出异常,其他语句会执行失败。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> INCR k1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
4) "v2"

监控!Watch

悲观锁

  • 认为什么时候都会出现问题,无论做什么都会加锁

乐观锁

  • 认为什么时候都不会出错,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据,

  • 获取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

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

127.0.0.1:6379> watch money   #监视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)

线程二(在线程一事务执行之前执行)

127.0.0.1:6379> set money 1000
OK

如果失败,获取最新值再次执行

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 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
127.0.0.1:6379> EXEC     #比较获取的值与当前值是否相同
1) (integer) 990
2) (integer) 30

Jedis

我们要使用java来操作redis

Jedis是官方推荐的java连接开发工具!使用java来操作redis中间件

测试

  • 导入对应的依赖

        <dependency>
            <groupId>redis.clientsgroupId>
            <artifactId>jedisartifactId>
            <version>3.3.0version>
        dependency>
        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.68version>
        dependency>
package cn.smallcosmos;

import redis.clients.jedis.Jedis;

/**
 * @Date 2020/5/14 下午3:02
 * @Created by zhaoli
 */
public class test {
    public static void main(String[] args) {
        //连接redis
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //测试连接
        System.out.println(jedis.ping());

    }
}

常用API跟上面相同
事务操作

package cn.smallcosmos;

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

/**
 * @Date 2020/5/14 下午3:02
 * @Created by zhaoli
 */
public class test {
    public static void main(String[] args) {
        //连接redis
        Jedis jedis = new Jedis("127.0.0.1",6379);

        JSONObject jsonObject = new JSONObject();

        jsonObject.put("hello","world");
        jsonObject.put("name","zhaoli");

        String jsonString = jsonObject.toJSONString();
        jedis.flushDB();
        //开启事务
        Transaction multi = jedis.multi();
        try {
            multi.set("user1",jsonString);
            int i  = 1/0;
            multi.set("user2",jsonString);
            multi.exec();  //执行事务
        } catch (Exception e) {
            multi.discard();  //放弃事务
            e.printStackTrace();
        } finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close();
        }

    }
}

你可能感兴趣的:(数据库)