赞
踩
2020年之后,大数据时代
一般的数据库无法进行分析管理了!
90年代,一个基本的网站访问量一般不会太大,单个数据库完全足够!
使用静态网页Html,服务器没有太大的压力。
整个网站的瓶颈
1、数据量太大、一个机器放不了
2、数据的索引(B + Tree),一个机器内存也放不下
3、访问量(读写混合),一个服务器承受不了
网站80%的情况都是在读、每次都有去查询数据库的话就十分的麻烦,所以希望减轻数据库的压力,可以使用缓存来保证效率
发展过程:优化数据结构和索引 - - - > 文件缓存(IO) - - - > Memcached(当时最热门的技术! )
本质:数据(读、写)
早些年MyISAM:表锁,十分影响效率,高并发下就会出现严重的锁问题
早些年Innodb:行锁
慢慢的就开始分库分表来解决写的压力,MySQL推出了表分区,这个并没有多少公司使用MySQL的集群,满足了所有的需求
MySQL等关系数据库就不够用了,数据量多,变化很快!
MySQL有的使用它来存储比较大的文件,博客,图片!数据库表很大,效率就低了!如果有一种数据库来专门处理这种数据,MySQL的压力就会变得十分小(研究如何处理这些问题!)大数据的IO压力下,表几乎没办法更改。
用户的个人信息,社交网络,地理位置,用户自己产生的数据,用户日志等等就会爆发式增长!
这时候就要用NoSQL数据库了,NoSQL可以很多好的处理以上的情况!
NoSQL = Not Only SQL(不仅仅是SQL)
关系数据库:表格,行,列(POI)
泛指非关系型数据库,随着web2.0互联网的诞生!传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社区!暴露出来很多难以克服的问题,NoSQL在当今大数据环境下发展的十分迅速,Redis是发展最快的。
很多的数据类型用户的个人信息,社交网络,地理位置。这些数据类型的存储不需要一个固定的格式!不需要多余的操作就可以横向拓展的!Map<String,Object> 使用键值对来控制!
方便扩展(数据之间没有关系,很好扩展!)
大数据量高性能(Redis一秒写8万次,可以读取11万次,NoSQL的缓存记录级,是一种细粒度的缓存,性能比较高)
数据类型是多样型的!(不需要事先设计数据库!随取随用!如果是数据库量十分大的表,很多人就无法设计了!)
传统的RDBMS和NoSQL
传统的RDBMS - 结构化组织 - SQL - 数据和关系都存在单独的表中 row col - 操作操作,数据定义语言 - 严格的一致性 - 基础的事务 - ...... NoSQL - 不仅仅是数据 - 没有固定的查询语言 - 键值对存储,列存储,文档存储,图形数据库(社交关系) - 最终一致性, - CAP定理和BASE(异地多活!) - 高性能,高可用,高可扩展 - ......
了解:3v + 3高
大数据时代的3V:主要是描述问题的
大数据时代的3高:主要是对程序的要求
真正在公司中的实践:NoSQL + RDBMS 一起使用才是最强的。
KV键值对:
文档型数据库(bson格式和json一样):
列存储的数据库:
图关系数据库:
四者对比:
分类 | Examples举例 | 典型应用场景 | 数据模型 | 优点 | 缺点 |
---|---|---|---|---|---|
键值(key-value) | Tokyo Cabinet/Tyrant,Redis,Voldemort,Oracle BDB | 内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等 | key指向Value的键值对,通常采用Hash table来实现 | 查找速度快 | 数据无结构化,通常只能被当作字符和二级制数据 |
列存储数据库 | Cassandra,HBase,Riak | 分布式的文件系统 | 以列簇式存储,将同一列数据存在一起 | 查找速度快,可扩展性强,更容易进行分布式扩展 | 功能相对局限 |
文档型数据库 | CouchDB,MongoDB | Web应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解value的内容) | key-value对应的键值对,value为结构化数据 | 数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构 | 查询性能不高,而且缺乏统一的查询语法 |
图形(Graph)数据库 | Neo4j,InfoGrid,Infinite gRAPH | 社交网络,推荐系统等,专注于构建关系图谱 | 图结构 | 利用图结构相关算法。比如最短路径寻址,N度关系查找等 | 很多时候需要对整个图做计算才能d得出需要的信息,而且这种结构不太好做分布式集群方案 |
Redis(R
emote D
ictionary S
erver ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
免费和开源,是最热门的NoSQL技术之一,也被人们称之为结构化数据库!
1、下载安装包 redis-6.2.6.tar.gz
2、解压Redis的安装包!程序/opt tar -zxvf redis-6.2.6.tar.gz
3、进入解压后的文件 cd redis-6.2.6.tar.gz
可以看到Redis的配置文件
4、基本的环境安装
yum install -y gcc-c++
make
make install
5、redis的默认安装路径 usr/local/bin
6、将redis配置文件复制到当前目录下
7、默认不是后台启动的,修改配置文件
vim redis.conf
i进行编辑 修改no为yes
按Esc退出编辑
按:输入wq保存并退出
8、启动Redis服务
进入安装目录
redis-server kconfig/redis.conf
9、使用 redis-cli进行连接测试
redis-cli -p 6379
10、查看redis的进程是否开启
11、关闭redis服务 shutdown
exit
12、再次查看进程是否存在
redis-benchmark是一个压力测试工具!
官方自带的性能测试工具!
redis-benchmark命令参数
简单测试:
# 测试: 100个并发连接 100000请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
如何查看这些分析:
redis默认有16个数据库
默认使用的是第0个
可以使用select 进行切换
127.0.0.1:6379[3]> keys * # 查看数据库所有的key
1) "name"
清除当前数据库: flushdb
清除全部数据库内容: flushdb
127.0.0.1:6379[3]> flushdb
OK
127.0.0.1:6379[3]> keys *
(empty array)
Redis是单线程的!
Redis是很快的,官方表示Redis是基于内存操作,CPU不是Reids的性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,即然可以使用单线程来实现,就使用单线程!所有就使用了单线程!
Redis是C语言写的,官方提供的数据位 10万 + QPS,这个不比同样使用key-value的Memcache差!
为什么Redis单线程还这么快
1、误区1:高性能的服务器一定是多线程的
2、误区2:多线程一定比单线程效率高
CPU、内存、硬盘的速度要有所了解
核心:redis是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文切换:耗时的操作),对于内存系统来说,如果没有上下文切换效率是最高的!多次读写都是在一个CPU上的,在内存情况下,这个就是最佳方案!
Redis是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作
数据库、缓存
和消息中间件MQ
。它支持多种类型的数据结构,如 字符串(strings),散列(hashes),列表(lists),集合(set),有序集合(sorted sets)与范围查询,bitmaps,hyperloglogs和地理空间(geospatial)索引半径查询。Redis内置了复制(replication),LUA脚本(Lua scripting),LRU驱动事件(LRU eviction),事务(transactions)和不同级别的磁盘持久化(persistence),并通过Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(high availability)。
127.0.0.1:6379> set age 1 # set key OK 127.0.0.1:6379> keys * # 查看所有的key 1) "age" 2) "name" 127.0.0.1:6379> exists name # 判断当前的key是否存在 (integer) 1 127.0.0.1:6379> move name 1 # 移除当前的key (integer) 1 127.0.0.1:6379> expire name 10 # 设置key的过期时间单位是秒 (integer) 1 127.0.0.1:6379> ttl name # 查看当前key的剩余时间 (integer) 4 127.0.0.1:6379> get name (nil) 127.0.0.1:6379> type age # 查看当前key的一个类型 string
127.0.0.1:6379> set key1 v1 # 设置值 OK 127.0.0.1:6379> get key1 # 获得值 "v1" 127.0.0.1:6379> keys * # 获得所有的key 1) "key1" 127.0.0.1:6379> EXISTS key1 # 判断某一个key是否存在 (integer) 1 127.0.0.1:6379> append key1 "hello" # 追加字符串,如果当前key不存在就相当于set key (integer) 7 127.0.0.1:6379> get key1 "v1hello" 127.0.0.1:6379> strlen key1 # 获取字符串的长度 (integer) 7 127.0.0.1:6379> APPEND key1 ",kuang" (integer) 13 127.0.0.1:6379> get key1 "v1hello,kuang" 127.0.0.1:6379>
127.0.0.1:6379> set views 0 # 初始浏览量为0 OK 127.0.0.1:6379> get views "0" 127.0.0.1:6379> incr views # 自增1 浏览量为1 (integer) 1 127.0.0.1:6379> incr views (integer) 2 127.0.0.1:6379> get views "2" 127.0.0.1:6379> decr views # 自减1 浏览量-1 (integer) 1 127.0.0.1:6379> decr views (integer) 0 127.0.0.1:6379> get views "0" 127.0.0.1:6379> INCRBY views 10 # 设置步长 指定增量 (integer) 10 127.0.0.1:6379> DECRBY views 5 # (integer) 5
# 字符串范围 range 127.0.0.1:6379> set key1 "hello,kuangshen" # 设置key1的值 OK 127.0.0.1:6379> get key1 "hello,kuangshen" 127.0.0.1:6379> GETRANGE key1 0 3 # 截取字符串 [0,3] "hell" 127.0.0.1:6379> GETRANGE key1 0 -1 # 获得全部的字符串和get key是一样的 "hello,kuangshen" 127.0.0.1:6379> # 替换 127.0.0.1:6379> set key2 abcdefg OK 127.0.0.1:6379> get key2 "abcdefg" 127.0.0.1:6379> SETRANGE key2 1 xx # 替换指定位置开始的字符串 (integer) 7 127.0.0.1:6379> get key2 "axxdefg"
# setex (set with expire) # 设置过期时间 # setnx (set if not expire) # 不存在设置(在分布式锁中会常常使用) 127.0.0.1:6379> setex key3 30 "hello" # 设置 key3的值为hello,30秒后过期 OK 127.0.0.1:6379> get key3 "hello" 127.0.0.1:6379> ttl key3 (integer) 5 127.0.0.1:6379> setnx mykey "redis" # 如果mykey不存在,创建mykey (integer) 1 127.0.0.1:6379> keys * 1) "key2" 2) "key1" 3) "mykey" 127.0.0.1:6379> ttl key3 (integer) -2 127.0.0.1:6379> setnx mykey :"mongodeb" # 如果mykey存在,创建失败 (integer) 0 127.0.0.1:6379> get mykey "redis"
# mset # mget 127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 # 同时设置多个值 OK 127.0.0.1:6379> keys * 1) "k2" 2) "k1" 3) "k3" 127.0.0.1:6379> mget k1 k2 k3 # 同时获取多个值 1) "v1" 2) "v2" 3) "v3" 127.0.0.1:6379> msetnx k1 v1 k4 v4 # msetnx是一个原子性的操作,要么一起成功要么一起失败 (integer) 0 127.0.0.1:6379> get key4 (nil) 127.0.0.1:6379>
# 对象
set user:1 {name:zhangsan,age:3} # 设置一个user:1 对象值 为json字符串来保存一个对象!
# 这里的key是一个巧妙的设计:user:{id}:{filed},如此设计在Redis中是完全可以的
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "2"
# getset # 先get然后在set
127.0.0.1:6379> getset db redis # 如果不存在值返回 nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongodb # 如果存在值,获取原来值,并设置新的值
"redis"
127.0.0.1:6379> get db
"mongodb"
String类似的使用场景:value除了是我们的字符串还可以是数字
基本的数据类型,列表
在redis立马,可以把list当成,栈,队列,阻塞队列
所有的list命令都是用 l 开头的,Redis不区分大小写命令
# 插入 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 # 通过区间获取具体的值 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> Lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> lindex list 1 # 通过下标获得list中的某一个值
"one"
127.0.0.1:6379> lindex list 0
"two"
# Llen
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> llen list # 返回列表的长度
(integer) 3
# 移除指定的值 Lrem 127.0.0.1:6379> lrange list 0 -1 1) "three" 2) "three" 3) "two" 4) "one" 127.0.0.1:6379> lrem list 1 one # 移除list集合中指定个数的value,精确匹配 (integer) 1 127.0.0.1:6379> lrange list 0 -1 1) "three" 2) "three" 3) "two" 127.0.0.1:6379> lrem list 1 three (integer) 1 127.0.0.1:6379> lrange list 0 -1 1) "three" 2) "two" 127.0.0.1:6379> lpush list three (integer) 3 127.0.0.1:6379> lrem list 2 three (integer) 2 127.0.0.1:6379> lrange list 0 -1 1) "two"
# trim 修剪
127.0.0.1:6379> rpush mylist "hello"
(integer) 1
127.0.0.1:6379> rpush mylist "hello1"
(integer) 2
127.0.0.1:6379> rpush mylist "hello2"
(integer) 3
127.0.0.1:6379> rpush mylist "hello3"
(integer) 4
127.0.0.1:6379> ltrim mylist 1 2 # 通过下标截取指定的长度,这个list已经被改变了 只剩下截取的元素了
OK
127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello1"
2) "hello2"
rpoplpush # 移除列表最后一个元素,将它移动到新的列表中
127.0.0.1:6379> rpush mylist "hello"
(integer) 1
127.0.0.1:6379> rpush mylist "hello1"
(integer) 2
127.0.0.1:6379> rpush mylist "hello2"
(integer) 3
127.0.0.1:6379> rpoplpush mylist myotherlist # 移除列表的最后一个元素,将他移动到新的列表中
"hello2"
127.0.0.1:6379> lrange mylist 0 -1 # 查看原来的列表
1) "hello"
2) "hello1"
127.0.0.1:6379> lrange myotherlist 0 -1 # 查看目标列表中确实存在该值
1) "hello2"
lset 将列表指定下标的值 替换为另外一个值 ,更新操作 127.0.0.1:6379> EXISTS list # 判断这个列表是否存在 (integer) 0 127.0.0.1:6379> lset list 0 item # 如果不存在列表我们去更新就会报错 (error) ERR no such key 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 item # 如果存在列表,更新当前下标的值 OK 127.0.0.1:6379> lrange list 0 0 1) "item" 127.0.0.1:6379> lset list 1 other # 如果不存在,则会报错 (error) ERR index out of range
linsert # 将某一个具体的值插入到列表中某个元素的前面或者后面 127.0.0.1:6379> Rpush mylist "heelo" (integer) 1 127.0.0.1:6379> Rpush mylist "world" (integer) 2 127.0.0.1:6379> linsert mylist before "world" "other" (integer) 3 127.0.0.1:6379> lrange mylist 0 -1 1) "heelo" 2) "other" 3) "world" 127.0.0.1:6379> linsert mylist after "world" new (integer) 4 127.0.0.1:6379> lrange mylist 0 -1 1) "heelo" 2) "other" 3) "world" 4) "new"
小结
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 java
(integer) 1
127.0.0.1:6379> smembers myset # 查看指定set中的所有值
1) "hello"
2) "world"
3) "java"
127.0.0.1:6379> sismember myset hello # 判断某一个值是不是在set集合中
(integer) 1
127.0.0.1:6379> sismember myset ku
(integer) 0
127.0.0.1:6379> scard myset # 获取set集合中的内容元素个数
(integer) 4
127.0.0.1:6379> srem myset hello # 移除set集合中的指定元素
(integer) 1
127.0.0.1:6379> scard myset
(integer) 3
127.0.0.1:6379> smembers myset
1) "hello2"
2) "world"
3) "java"
set 无序不重复集合。抽随机
127.0.0.1:6379> smembers myset
1) "hello2"
2) "world"
3) "java"
127.0.0.1:6379> srandmember myset # 随机抽选出一个元素
"java"
127.0.0.1:6379> srandmember myset
"world"
127.0.0.1:6379> srandmember myset
"hello2"
127.0.0.1:6379> srandmember myset 2 # 随机抽选出指定个数的元素
1) "hello2"
2) "java"
删除指定的key,随机删除key
127.0.0.1:6379> smembers myset
1) "hello2"
2) "world"
3) "java"
127.0.0.1:6379> spop myset # 随机删除一些set集合中的元素
"world"
127.0.0.1:6379> spop myset
"java"
127.0.0.1:6379> smembers myset
1) "hello2"
# 将一个指定的值移动到另外一个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 java
(integer) 1
127.0.0.1:6379> sadd myset2 set2
(integer) 1
127.0.0.1:6379> smove myset myset2 java # 将一个指定的值移动到另外一个set集合中
(integer) 1
127.0.0.1:6379> smembers myset2
1) "set2"
2) "java"
共同关注(并集) 数字集合类: - 差集 sdiff - 交集 sinter - 并集 sunion 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 key1 d (integer) 1 127.0.0.1:6379> sadd key2 c (integer) 1 127.0.0.1:6379> sadd key2 e (integer) 1 127.0.0.1:6379> sdiff key1 key2 # 差集 1) "d" 2) "a" 3) "b" 127.0.0.1:6379> sinter key1 key2 # 交集 共同好友就这样实现的 1) "c" 127.0.0.1:6379> sunion key1 key2 # 并集 1) "a" 2) "b" 3) "c" 4) "e" 5) "d"
微博,A用户将所有的关注的人放在一个set集合中,将它的粉丝也放到一个集合中!
共同关注,共同爱好,二度好友
Map集合,key- Map (< key-value >) 本质和string类型没有太大区别,还是一个简单的key-value
127.0.0.1:6379> hset myhash filed1 hello # set一个具体的key-value (integer) 1 127.0.0.1:6379> hget myhash filed1 # 获取一个字段值 "hello" 127.0.0.1:6379> hmset myhash filed1 hello filed2 world # set多个key -value OK 127.0.0.1:6379> hmget myhash filed1 filed2 # 获取多个字段值 1) "hello" 2) "world" 127.0.0.1:6379> hgetall myhash # 获取全部的数据 1) "filed1" 2) "hello" 3) "filed2" 4) "world" 127.0.0.1:6379> hdel myhash filed1 # 删除hash指定的key字段,对应的value值也就消息了 (integer) 1 127.0.0.1:6379> hgetall myhash 1) "filed2" 2) "world"
hlen
127.0.0.1:6379> hmset myhash filed1 hello filed2 world
OK
127.0.0.1:6379> hgetall myhash
1) "filed2"
2) "world"
3) "filed1"
4) "hello"
127.0.0.1:6379> hlen myhash # 获取hash表的字段数量
(integer) 2
127.0.0.1:6379> hexists myhash filed1 # 判断hash中指定字段是否存在
(integer) 1
127.0.0.1:6379> hexists myhash filed3
(integer) 0
127.0.0.1:6379> hkeys myhash # 只获取所有filed
1) "filed2"
2) "filed1"
127.0.0.1:6379> hvals myhash # 只获取所有value
1) "world"
2) "hello"
incr decr
127.0.0.1:6379> hset myhash filed3 5 # 指定增量
(integer) 1
127.0.0.1:6379> hincrby myhash filed3 1
(integer) 6
127.0.0.1:6379> hincrby myhash filed3 -1
(integer) 5
127.0.0.1:6379> hsetnx myhash filed4 hello # 如果不存在则可以设置
(integer) 1
127.0.0.1:6379> hsetnx myhash filed4 world # 如果存在则不可以设置
(integer) 0
hash可以存储变更的数据 user name age 尤其是用户信息之类的,经常变动的信息!hash更加适合对象的存储,而string更加适合字符串的存储
在set的基础上增加了一个值
127.0.0.1:6379> zadd myset 1 one # 添加一个值
(integer) 1
127.0.0.1:6379> zadd myset 2 two 3 three # 添加多个值
(integer) 2
127.0.0.1:6379> zrange myset 0 -1
1) "one"
2) "two"
3) "three"
排序怎么实现 127.0.0.1:6379> zadd salary 2500 xiaohong # 添加三个用户 (integer) 1 127.0.0.1:6379> zadd salary 5000 zhangsan (integer) 1 127.0.0.1:6379> zadd salary 500 qigai (integer) 1 # zrangebyscore key min max 127.0.0.1:6379> zrangebyscore salary -inf +inf # 显示全部的用户 从小到大排序 1) "qigai" 2) "xiaohong" 3) "zhangsan" 127.0.0.1:6379> zrevrange salary 0 -1 # 从大到小进行排序 1) "zhangsan" 2) "xiaohong" 3) "qigai" 127.0.0.1:6379> zrangebyscore salary -inf +inf withscores #显示全部的用户并且附带成绩 1) "qigai" 2) "500" 3) "xiaohong" 4) "2500" 5) "zhangsan" 6) "5000" 127.0.0.1:6379> zrangebyscore salary -inf 2500 withscores #显示工资小于2500员工的升序排列 1) "qigai" 2) "500" 3) "xiaohong" 4) "2500"
zrem 移除元素
127.0.0.1:6379> zrange salary 0 -1
1) "qigai"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> zrem salary xiaohong # 移除有序集合中的指定元素
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "qigai"
2) "zhangsan"
127.0.0.1:6379> zcard salary # 获取有序集合中的个数
(integer) 2
127.0.0.1:6379> zadd myset 1 hello
(integer) 1
127.0.0.1:6379> zadd myset 2 world
(integer) 1
127.0.0.1:6379> zadd myset 3 java
(integer) 1
127.0.0.1:6379> zcount myset 1 3 # 获取指定区间的成员数量
(integer) 3
127.0.0.1:63
朋友的定位,附近的人,打车,距离计算
Redis 的Geo可以推算地理位置的信息,两地之间的距离,方圆几里的人
官方文档
# getadd 添加地理位置 # 规则:两极无法直接添加,我们一般会下载城市数据,直接通过java程序来一次性导入 #有效的经度从-180度到180度 #有效的纬度从-85.05112878度到85.05112878度。 #当坐标位置超出上述指定范围时,该命令将会返回一个错误。 #127.0.0.1:6379> geoadd china:city 39.90 116.40 beijing (error) ERR invalid longitude,latitude pair 39.900000,116.400000 # 参数 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 shanghai (integer) 1 127.0.0.1:6379> geoadd china:city 106.5 29.6 chongqing (integer) 1 127.0.0.1:6379> geoadd china:city 114.05 22.52 shenzhen (integer) 1 127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou (integer) 1 127.0.0.1:6379> geoadd china:city 108.96 34.26 xian (integer) 1
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 shanghai chongqing
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
2) 1) "121.47000163793563843"
2) "31.22999903975783553"
3) 1) "106.49999767541885376"
2) "29.60000097326405211"
127.0.0.1:6379> geodist china:city beijing shanghai km # 查看上海到北京的直线距离
"1067.3788"
127.0.0.1:6379> geodist china:city beijing chongqing km # 查看北京到重庆的直线距离
"1457.7307"
127.0.0.1:6379> georadius china:city 110 30 1000 km # 以 110 30这个 经纬度为中心,寻找方圆1000km内的城市 1) "chongqing" 2) "xian" 3) "shenzhen" 4) "hangzhou" 127.0.0.1:6379> georadius china:city 110 30 500 km 1) "chongqing" 2) "xian" 127.0.0.1:6379> georadius china:city 110 30 500 km withdist # 显示到中心距离的位置 1) 1) "chongqing" 2) "340.7173" 2) 1) "xian" 2) "483.8340" 127.0.0.1:6379> georadius china:city 110 30 500 km withcoord # 显示他人的定位信息 1) 1) "chongqing" 2) 1) "106.49999767541885376" 2) "29.60000097326405211" 2) 1) "xian" 2) 1) "108.96000176668167114" 2) "34.25999964418929977" 127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 1 # 筛选出指定的结果 1) 1) "chongqing" 2) "340.7173" 3) 1) "106.49999767541885376" 2) "29.60000097326405211" 127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 2 1) 1) "chongqing" 2) "340.7173" 3) 1) "106.49999767541885376" 2) "29.60000097326405211" 2) 1) "xian" 2) "483.8340" 3) 1) "108.96000176668167114" 2) "34.25999964418929977" 127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 3 1) 1) "chongqing" 2) "340.7173" 3) 1) "106.49999767541885376" 2) "29.60000097326405211" 2) 1) "xian" 2) "483.8340" 3) 1) "108.96000176668167114" 2) "34.25999964418929977"
# 找出位于指定元素周围的其他元素
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km
1) "beijing"
2) "xian"
127.0.0.1:6379> georadiusbymember china:city shanghai 400 km
1) "hangzhou"
2) "shanghai"
# 将二维的经纬度转换为一纬的字符串,如果两个字符串越接近,那么距离越近
127.0.0.1:6379> geohash china:city beijing chongqing
1) "wx4fbxxfke0"
2) "wm78rkwbtb0"
127.0.0.1:6379> zrange china:city 0 -1 # 查看地图中全部的元素 1) "chongqing" 2) "xian" 3) "shenzhen" 4) "hangzhou" 5) "shanghai" 6) "beijing" 127.0.0.1:6379> zrem china:city beijing # 移除指定元素 (integer) 1 127.0.0.1:6379> zrange china:city 0 -1 1) "chongqing" 2) "xian" 3) "shenzhen" 4) "hangzhou" 5) "shanghai"
基数
A = {1,3,5,7,8,9}
B = {1,3,5,7,8}
基数(不重复的元素) = 5,可以接受误差
简介
Redis2.8.9版本就更新了Hyperloglog数据结构
Reids Hyperloglog基数统计的算法
优点:占用的内存是固定的,2^64不同的元素的基数,只需要费12kb内存,如果要从内存角度来比较的话Hyoerloglog首选
网页的UV(一个人访问一个网站多次但是还是算作一个人)
传统的方式,set保存用户的id,然后就可以统计set中的元素数量作为标准判断
这个方式如果保存大量的用户id就会比较麻烦,我们的目的是为了计数,而不是为了保存用户id
0.81%错误率!统计UV任务,可以忽略不计的
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 # 统计 mykey元素的基数数量
(integer) 10
127.0.0.1:6379> pfadd mykey2 i j z c v b n m # 创建第二组元素
(integer) 1
127.0.0.1:6379> pfcount mykey2
(integer) 8
127.0.0.1:6379> pfmerge mykey3 mykey mykey2 # 合并两组mykey mykey2 => mykey3 并集
OK
127.0.0.1:6379> pfcount mykey3 # 查看并集的数量!
(integer) 14
如果允许容错,那么一定可以使用Hyperloglog!
如果不允许容错,就使用set或者自己的数据类型即可!
位存储
统计用户信息,活跃,不活跃!登录、未登录!打卡,365打卡!两个状态的,都可以使用Bitmaps!Bitmaps位图,数据结构! 都是操作二进制位来进行记录,就只有0和1两个状态
使用bitmap来记录周一到周日的打卡
周一:1 周二:0 周三:0 周四:1…
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) 1
127.0.0.1:6379> setbit sign 6 0
(integer) 0
查看某一天是否打卡
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 6
(integer) 0
统计操作,统计打开的天数!
127.0.0.1:6379> bitcount sign # 统计这周的打卡记录,就可以看到是否右全勤
(integer) 3
Redis事务本质:一组命令的集合!一个事务的所有命令都会被序列化,在事务执行的过程中,会按照顺序执行!
一次性、顺序性、排他性,来执行一系列的命令
------- 队列 set set set 执行 -----------
Redis事务没有隔离级别的概念
所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行! Exec
Redis单条命令保证原子性的,但是事务不保证原子性!
redis事务:
127.0.0.1:6379> multi # 开启事务 OK # 命令入队 127.0.0.1:6379(TX)> set k1 k1 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 k3 QUEUED 127.0.0.1:6379(TX)> exec # 执行事务 1) OK 2) OK 3) "v2" 4) OK
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)
事务中所有的命令都不会被执行
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 k3 v3 QUEUED 127.0.0.1:6379(TX)> getset k3 # 错误的命令 (error) ERR wrong number of arguments for 'getset' command 127.0.0.1:6379(TX)> set k4 v4 QUEUED 127.0.0.1:6379(TX)> set k5 v5 QUEUED 127.0.0.1:6379(TX)> exec # 执行事务报错 (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get k5 # 所有的命令都不会被执行 (nil)
如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行的。错误命令抛出异常!
127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> set k1 "v1" QUEUED 127.0.0.1:6379(TX)> incr k1 # 会执行的时候失败 QUEUED 127.0.0.1:6379(TX)> set k2 v2 QUEUED 127.0.0.1:6379(TX)> set k3 v3 QUEUED 127.0.0.1:6379(TX)> get k3 QUEUED 127.0.0.1:6379(TX)> exec 1) OK 2) (error) ERR value is not an integer or out of range # 虽然第二条命令报错了,但是依旧执行成功了 3) OK 4) OK 5) "v3" 127.0.0.1:6379> get k2 "v2" 127.0.0.1:6379> get k3 "v3"
悲观锁:
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(TX)> decrby money 20 QUEUED 127.0.0.1:6379(TX)> incrby out 20 QUEUED 127.0.0.1:6379(TX)> 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(TX)> decrby moey 10
QUEUED
127.0.0.1:6379(TX)> incrby out 10
QUEUED
127.0.0.1:6379(TX)> exec # 执行之前另外的一个线程修改了值,这个时候,就会导致事务执行失败
(nil)
如果修改失败,获取最新值就好
127.0.0.1:6379> unwatch # 如果发现事务执行失败,就先解锁
OK
127.0.0.1:6379> watch money # 获取最新的值,再次监视,select version
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 money 10
QUEUED
127.0.0.1:6379(TX)> exec # 比对监视的值是否发生了变化,如果没有变化,那么可以执行成功,如果变化了就执行失败
1) (integer) 980
2) (integer) 1000
使用Jedis来操作Redis
Jedis是Redis官方推荐的java连接开发工具,使用java操作Redis中间件!如果要使用Java操作Redis,那么一定要对Redis十分熟悉。
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.37</version>
</dependency>
</dependencies>
import redis.clients.jedis.Jedis;
public class TestPing {
public static void main(String[] args) {
//1、new Jedis 对象即可
Jedis jedis = new Jedis("127.0.0.1", 6379);
//jedis所有的命令就是我们之前学习的所有指令!
System.out.println(jedis.ping());
}
}
输出:PONG
public class TestKey { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1", 6379); System.out.println("清空数据:" + jedis.flushDB()); System.out.println("判断某个键是否存在:" + jedis.exists("username")); System.out.println("新增<'username','kuangshen'>的键值对:" + jedis.set("username", "kuang")); 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("判断键password是否存在:" + jedis.exists("password")); System.out.println("查看键username所存储的值的类型:" + 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的数目L:" + jedis.dbSize()); System.out.println("删除所有数据库中的key:" + jedis.flushAll()); } }
public class TestString { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1", 6379); jedis.flushDB(); System.out.println("=========增加数据==============="); 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", "value1Change")); 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", "key03", "value03")); System.out.println("获取多个键值对:" + jedis.mget("key01", "key02", "key03")); System.out.println("删除多个键值对:" + jedis.del("key01", "key02")); System.out.println("获取多个键值对:" + jedis.mget("key01", "key02", "key03")); 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, "value3")); System.out.println(jedis.get("key3")); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { throw new RuntimeException(e); } 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)); } }
public class TestList { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1", 6379); jedis.flushDB(); System.out.println("============添加一个list==========="); jedis.lpush("collections", "ArrayList", "Vector", "Stack", "HashMap", "WeakHashMap", "LinkedList"); jedis.lpush("collections", "HashSet"); jedis.lpush("collections", "TreeSet"); jedis.lpush("collections", "TreeMap"); System.out.println("collections的内容" + jedis.lrange("collections", 0, -1)); System.out.println("collections区间0-3的元素" + jedis.lrange("collections", 0, 3)); System.out.println("================================"); //删除列表指定的值,第二个参数为删除的个数(有重复时),后add进去的值先被删除,类似于出栈 System.out.println("删除指定的元素个数:" + jedis.lrem("collections", 2, "HashMap")); System.out.println("collections的内容:" + jedis.lrange("collections", 0, -1)); System.out.println("删除下标0-3区间之外的元素:" + jedis.ltrim("collections", 0, 3)); System.out.println("collections的内容:" + jedis.lrange("collections", 0, -1)); System.out.println("collections列表出栈(左端):" + jedis.lpop("collections")); System.out.println("collections的内容" + jedis.lrange("collections", 0, -1)); System.out.println("collections添加元素,从列表右端,与lpush相对应:" + jedis.rpush("collections", "HashMap")); System.out.println("collections的内容:" + jedis.lrange("collections", 0, -1)); System.out.println("collections列表出栈(右端):" + jedis.rpop("collections")); System.out.println("collections的内容:" + jedis.lrange("collections", 0, -1)); System.out.println("修改collection指定下标为1的内容:" + jedis.lset("collections", 1, "Linkedlist")); System.out.println("collections的内容:" + jedis.lrange("collections", 0, -1)); System.out.println("================================"); System.out.println("collections的长度:" + jedis.llen("coolections")); System.out.println("获取collections下标为2的元素:" + jedis.lindex("collections", 2)); System.out.println("================================"); jedis.lpush("sortedList", "3", "6", "2", "0", "7", "4"); System.out.println("sortedList排序前:" + jedis.lrange("sortedList", 0, -1)); System.out.println(jedis.sort("sortedList")); System.out.println("sortedList排序后:" + jedis.lrange("sortedList", 0, -1 )); } }
public class TestSet { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1", 6379); jedis.flushDB(); System.out.println("===========向集合中添加元素(不重复)==============="); System.out.println(jedis.sadd("eleSet", "e1", "e2", "e3", "e4", "e5")); System.out.println(jedis.sadd("eleSet", "e6")); System.out.println(jedis.sadd("eleSet", "e6")); System.out.println("eleSet的所有元素为:" + jedis.smembers("eleSet")); System.out.println("删除一个元素e0:" + jedis.srem("eleSet", "e0")); System.out.println("eleSet的所有元素为:" + jedis.smembers("eleSet")); System.out.println("删除两个元素e7和e6:" + jedis.srem("eleSet", "e7", "e6")); System.out.println("eleSet的所有元素为:" + jedis.smembers("eleSet")); System.out.println("随机的移除集合中的一个元素:" + jedis.spop("eleSet")); System.out.println("随机的移除集合中的一个元素:" + jedis.spop("eleSet")); System.out.println("eleSet的所有元素为:" + jedis.smembers("eleSet")); System.out.println("eleSet中包含元素的个数:" + jedis.scard("eleSet")); System.out.println("e3是否在eleSet中:" + jedis.sismember("eleSet", "e3")); System.out.println("e1是否在eleSet中:" + jedis.sismember("eleSet", "e1")); System.out.println("e5是否在eleSet中" + jedis.sismember("eleSet", "e5")); System.out.println("==================================="); System.out.println(jedis.sadd("eleSet1", "e1", "e2", "e3", "e0", "e8", "e7", "e5")); System.out.println(jedis.sadd("eleSet2", "e1", "e2", "e4", "e3", "e0", "e8")); System.out.println("将eleSet1中删除e1并存入eleSet3中:" + jedis.smove("eleSet1", "eleSet3", "e1")); System.out.println("将eleSet1中删除e1并存入eleSet3中:" + jedis.smove("eleSet1", "eleSet3", "e2")); 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")); jedis.sinterstore("eleSet4", "eleSet1", "eleSet2"); //求交集并将交集保存到dstkey的集合 System.out.println("eleSet4的元素:" + jedis.smembers("eleSet4")6; } }
public class TestHash { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1", 6379); jedis.flushDB(); Map<String, String> map = new HashMap<>(); map.put("key1", "value1"); map.put("key2", "value2"); map.put("key3", "value3"); map.put("key4", "value4"); //添加名称为hash(key)的hash元素 jedis.hmset("hash", map); //向名称为hash的hash中添加key为key5,value为value5的元素 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.hsetnx("hash","key6","value6")); System.out.println("散列hash的所有键值对为:" + jedis.hgetAll("hash")); System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6:" + jedis.hsetnx("hash","key6","value60")); System.out.println("散列hash的所有键值对为:" + jedis.hgetAll("hash")); System.out.println("删除一个或多个键值对" + jedis.hdel("hash","key2","key3")) ; 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中是否存在key4:" + jedis.hexists("hash","key4")); System.out.println("获取hash中的值:" + jedis.hmget("hash","key1")); System.out.println("获取hash中的值:" + jedis.hmget("hash","key1","key2","key4")); } }
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", "xiaohong"); //开启事务 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(); //放弃事务 } finally { System.out.println(jedis.mget("user1", "user2")); jedis.close(); //关闭连接 } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。