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压力下,表几乎没法更改。
目前一个基本的互联网项目
为什么要用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一起使用才是最强的.
键值对存储
文档型数据库(bson格式和json一样)
MongoDB(一般必须要掌握)
MongoDB是一个基于分布式文件存储的数据库,c++编写,主要用来处理大量的文档
MongoDB是一个介于关系型数据库和非关系型数据库中间品!MongoDB是非关系型数据库中功能最丰富,最像关系型数据库的。
CouchDB(国外的一款数据库)
列存储数据库
图关系数据库
Redis (Remote Dictionary Server)远程字典服务
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
1、内存存储,持久化,内存是断电即失,所以说持久化很重要(rdb,aof)
2、效率高可以用于高速缓存
3、发布订阅系统
4、地图信息分析
5、计时器,计数器(浏览量!)
6、…
1、多样的数据
2、持久化
3、集群
4、事务
5、…
redis-benchmark是一个压力测试工具!
官方自带的性能测试工具!
redis-benchmark命令参数:
#测试 100个并发 100000个请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
select 3
keys *
flushdb
flushall
官方表示,Redis是基于内存操作的,CPU不是Redis性能的瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以用单线程实现,就用单线程了!所以就是用了单线程!
Redis是C语言编写的,官方提供数据为100000+的QPS,完全不比同样是使用key-value的MemCache差。
1.redis是基于内存的,内存的读写速度非常快;
2.redis是单线程的,省去了很多上下文切换线程的时间;
3.redis使用多路复用技术,可以处理并发的连接;
目前,多路复用主要有三种技术:select,poll,epoll。它们出现的顺序是按时间先后的,越排后的技术改正了之前技术的缺点。epoll是最新的也是目前最好的多路复用技术。
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
###########################################################
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"
#############################################################
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)
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中!,将他的粉丝也放在一个集合中!
共同关注,共同爱好,二度好友,推荐好友(六度分割理论!!+)
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更适合字符串的存储
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
有效的经度从-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"
什么是基数?
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 位图,数据结构!都是操作二进制位进行记录,就只有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事务:
正常事务执行
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
我们要使用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();
}
}
}