文章目录
1、简介
基于内存可以做缓存(快),又可以持久化,
数据结构服务器
优化数据库数据结构、索引------>文件缓存(IO)------>memcached
单机mysql---->
memcached(缓存)+mysql+垂直拆分(读写分离)—>
分库分表+水平拆分+做一些集群------>
分库分表解决写的压力,做成微服务
为什么要用NoSQL?
用户的个人信息,社交网络,地理位置,用户自己产生的数据,用户日志爆发式增长。
NOSQL可以很好的处理以上情况。
Nosql = not only sql
泛指非关系型数据库
web2.0互联网(视频)诞生,传统的关系型数据库不能满足需求。超大规模高并发的社区。
关系型数据库:表格,行列,
个人信息,社交网络,地理位置,这些数据类型的存储不需要一个固定的格式。不需要过多的操作就可以横向扩展了map<String,Object>
nosql特点
解耦
1.方便扩展,(数据之间没有关系,很好扩展)
2.大数据量,高性能,redis1秒写8万次,读11万次。nosql缓存记录级,细粒度缓存,性能高。
3.数据类型多样(5+3,)不需要事先设计数据库,随取随用
5种基本数据类型:string,list,hash,set,Zset
3种特殊数据类型:geo,hyperloglog,bitmap
4.传统的RDBMS和NOSQL
传统的RDBMS:
结构化组织
sql
数据和关系都存在单独的表中
操作,数据定义语言
严格的一致性
基础的事务
nosql
不仅仅是数据
没有固定的查询语言
键值对存储,列存储,文档存储,图形数据库(社交关系)
最终一致性
CAP定理和BASE(异地多活)
高性能,高可用,高可扩展
大数据时代的3V+3高
3V:主要描述问题:海量(Volume),多样(Variety),实时(Velocity)
3高:对程序员的要求:高并发,高性能,高可扩
数据层:统一数据服务平台
业务层:服务化中心+模型关系路由框架
展现层:CMS+Service tag + 页面组件化框架+店铺装修平台+组件服务平台
1、商品的基本信息:
名称、价格、商家信息
关系型数据库就可以解决,
2、商品的描述,评论(文字比较多)
文档型数据库:MongoDB
3.图片:
分布式文件系统: fastDFS
淘宝自己:TFS
Google: GFS
hadoop: HDFS
阿里云:oss
4.商品的关键字:
搜索引擎:solr, elasticsearch
ISearch:多隆
5、商品波段信息(秒杀)
内存数据库
redis,Tair,Memache
6、商品交易,外部的支付接口
三方应用
nosql四大分类
KV键值对:
新浪:Redis
美团:redis,+ Tair
阿里,百度:redis + memcached
文档型数据库(bson)
MongoDB,(基于分布式文件存储的数据库,C++编写,主要处理大量文档)
MongoDB是一个介于关系型数据库和非关系型数据库中间的产品,是非关系型数据库功能最丰富,最像关系型数据库的
ConthDB
列存储数据库
HBase
分布式文件系统
图关系数据库
社交拓扑图。广告推荐,朋友圈
Neo4j,
2、安装
redis(remote dictionary server)远程字典服务
c语言编写,支持内存,可基于内存可持久化的日日志型,key-value数据库,并提供多种语言API,被称为结构化数据库。免费开源
能干嘛?
1、内存存储,持久化(rdb,aof)
2、效率高,可以用于告诉缓存
3、发布订阅系统
4、地图信息分析
5、计时器,计数器(微信浏览量,incr, decre)
特点:
1、多种数据类型
2、持久化
3、集群
4、事务
2.1、ubuntu安装redis
默认安装路径在/usr/local/bin/
一个链接
1、安装redis
$ sudo apt update
$ sudo apt install redis-server
执行之后redis的配置文件在:/etc/redis/redis.conf
redis-server 在/usr/bin/
redis-cli 也在/usr/bin/
2、Redis 配置
打开 Redis的配置文件
$ sudo vi /etc/redis/redis.conf
supervised systemd
重启 sudo service redis restart
查看redis的状态$ sudo systemctl status redis
3、Redis 控制
启动 sudo service redis start
关闭sudo service redis stop
重启 sudo service redis restart
4、Redis 设置密码
打开 Redis 的配置文件
$ sudo vi /etc/redis/redis.conf
找到下面这一行
# requirepass foobared
将注释符号去掉,将后面修改成自己的密码,如:
requirepass 123456
5、redis使用
redis 客户端连接
在命令行中输入如下命令来登陆进 redis 客户端
$ redis-cli
redis 远程连接
打开配置文件
$ sudo vi /etc/redis/redis.conf
将 bind 127.0.0.1 ::1 改为 bind 0.0.0.0
之后重新启动 redis
->redis-server
或者
->redis-server /etc/redis/redis.conf
连接数据库redis
redis-cli -p 6379
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set name yym
OK
127.0.0.1:6379> get name
"yym"
127.0.0.1:6379> keys *
1) "name"
shutdown关闭连接,在连接到的redis状态中执行shutdown ,就退出,并关闭redis服务了
3、测试性能redis-benchmark
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
4、基础知识
有16个数据库
默认使用第0个,
在redis.conf中有显示
yym@yym001:/etc/redis$ redis-cli -p 6379
127.0.0.1:6379> dbsize
(integer) 4
127.0.0.1:6379> select 3 // 选择3号数据库
OK
127.0.0.1:6379[3]> dbsize // 查看当前数据库的大小
(integer) 0
127.0.0.1:6379[3]> set age 22 // 设置一个key
OK
127.0.0.1:6379[3]> dbsize
(integer) 2
127.0.0.1:6379[3]> get age
"22"
127.0.0.1:6379[3]> flushdb // 清空当前数据库
OK
127.0.0.1:6379[3]> dbsize
(integer) 0
127.0.0.1:6379[3]> flushall // 清空所有数据库
OK
127.0.0.1:6379[3]> select 1
OK
C
(integer) 0
127.0.0.1:6379[1]> keys * // 查看所有的keys
127.0.0.1:6379[1]> exists name // 判断某个键是否存在
move name 1 // 从当前1号数据库中移除name这个键
127.0.0.1:6379[1]> set name yym
OK
127.0.0.1:6379[1]> keys *
1) "name"
127.0.0.1:6379[1]> expire name 10 // 设置过期时间
(integer) 1
127.0.0.1:6379[1]> ttl name // 查看剩余时间
(integer) 7
127.0.0.1:6379[1]> keys *
1) "school"
2) "name"
3) "age"
127.0.0.1:6379[1]> type name // 查看数据类型
string
127.0.0.1:6379[1]> type age
string
4.2、redis是单线程的
很快,是基于内存操作的,cpu不是我们的一个性能瓶颈(是多线程的性能瓶颈),redis的性能瓶颈是内存和网络带宽,既然可以使用单线程,那就使用单线程来做了。
redis是C语言写的。每秒10万+的qps,完全不比同样使用key-value的memecache差!
redis6是支持多线程的。
redis为什么那么快?
误区1:高性能的服务器一定是多线程的?
误区2、多线程一定比单线程效率高?多线程有cpu调度,如果只有一个cpu,上下文切换也消耗资源(耗时)。
核心:redis是将所有的数据都放在内存中,所以说使用单线程的效率就是最高的。对于内存系统来说,没有上下文切换效率就是最高的!多次读写都是在一个cpu上的,在内存系统上,这就是最佳方案。
没必要使用多线程,而且使用了多线程也不能提高效率,更可能效率下降
5、五大数据类型
可以做数据库、缓存、消息中间件MQ订阅发布。
redis-key
string
list
set
hash
zset
三种特殊数据类型
geospatial// 地理位置相关
hyperloglog
bitmaps //位图
5.1、string类型
127.0.0.1:6379[1]> keys *
1) "school"
2) "name"
3) "age"
127.0.0.1:6379[1]> append name " hello" // 追加字符串,如果不存在相当于创建一个
(integer) 16
127.0.0.1:6379[1]> keys *
1) "school"
2) "name"
3) "age"
127.0.0.1:6379[1]> get name
"yangyanmeng hello"
127.0.0.1:6379[1]> get name
"yangyanmenghello"
127.0.0.1:6379[1]> strlen name # 获取长度
(integer) 16
127.0.0.1:6379[1]> set views 0
OK
127.0.0.1:6379[1]> get views
"0"
127.0.0.1:6379[1]> incr views // 增加1
(integer) 1
127.0.0.1:6379[1]> incr views
(integer)
127.0.0.1:6379[1]> decr views // 减少1
(integer) 1
127.0.0.1:6379[1]> incrby views 10 // 步长
(integer) 12
// 字符串截取
127.0.0.1:6379[1]> set key1 "hello,huangshen"
OK
127.0.0.1:6379[1]> get key1
"hello,huangshen"
127.0.0.1:6379[1]> getrange key1 1 5
"ello,"
127.0.0.1:6379[1]> getrange key1 0 -1 // 获取所有字符串
"hello,huangshen"
127.0.0.1:6379> set key2 "qwertttyyefaefwefa" //
OK
127.0.0.1:6379> setrange key2 2 xx // 修改字符串
(integer) 18
127.0.0.1:6379> get key2
"qwxxtttyyefaefwefa"
127.0.0.1:6379> setex key3 30 " hello" // exist设置过期时间,如果存在就设置过期时间
OK
127.0.0.1:6379> ttl key3
(integer) 25
127.0.0.1:6379> setnx key4 "yym" // not exist 如果不在就创建key4
(integer) 1
127.0.0.1:6379> keys *
1) "key4"
2) "key2"
127.0.0.1:6379> setnx key4 "mongeDB"// 如果存在就创建失败
(integer) 0
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3//批量设置
OK
127.0.0.1:6379> keys *
1) "k2"
2) "k3"
3) "k1"
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 // 创建原子性,一个不成功就返回失败
(integer) 0
// 这里的key是一个巧妙的设计,user:{id}:{files}
127.0.0.1:6379> mset user:1:name zhangshan user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangshan"
2) "2"
127.0.0.1:6379> getset db redis // 先获取再赋值
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongeDB
"redis"
127.0.0.1:6379> get db
"mongeDB"
5.2、list
127.0.0.1:6379> lpush list one
(integer) 1
127.0.0.1:6379> loush list two
(error) ERR unknown command 'loush'
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> keys *
1) "list"
127.0.0.1:6379> keys *
1) "list"
127.0.0.1:6379> lrange list 0 -1 # 插入值是从头往右插入,显示也是从左往右显示
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1
1) "three"
2) "two"
127.0.0.1:6379> lpop list
"three"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> rpush list right
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "right"
127.0.0.1:6379> rpop list
"right"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> lindex list 1 # 通过下标获取值,
"one"
127.0.0.1:6379> lindex list 0
"two"
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty list or set)
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
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
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "two"
127.0.0.1:6379> lrem list 2 three
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "two"
###########################################################
127.0.0.1:6379> flushdb
OK
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> rpush mylist "hello4"
(integer) 5
127.0.0.1:6379> lrange mylist 0 -1\
(error) ERR value is not an integer or out of range
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "hello1"
3) "hello2"
4) "hello3"
5) "hello4"
127.0.0.1:6379> ltrim mylist 1 2 # 按下标截取指定的长度
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello2"
###########################################################
127.0.0.1:6379> lrange mylist 0 -1
1) "helloo"
2) "hello1"
3) "hello2"
127.0.0.1:6379> lpoprpush mylist mylist2 #没有这个命令
(error) ERR unknown command 'lpoprpush'
127.0.0.1:6379> rpoplpush mylist mylist2 # 从右边删除,从左边推进去
"hello2"
127.0.0.1:6379> lrange mylist 0 -1
1) "helloo"
2) "hello1"
127.0.0.1:6379> lrange mylist2 0 -1
1) "hello2"
###########################################################
127.0.0.1:6379> flushdb
OK
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 -1
1) "value1"
127.0.0.1:6379> lset list 0 item # 将列表中指定下标的值替换成指定的值
OK
127.0.0.1:6379> lrange list 0 -1
1) "item"
127.0.0.1:6379> lset list 1 qqqq # 如果不存在,会报错
(error) ERR index out of range
###########################################################
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> rpush mylist "hello"
(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) "hello"
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) "hello"
2) "other"
3) "world"
4) "new"
是一个链表,before,Node,after,left,right,都可以插入值
如果key不存在,创建新的链表
如果key存在,新增内容,
如果移除了所有值,空链表也代表不存在
在两边插入或者改动,效率最高,中间元素,相对来说效率低
消息队列:(lpush,rpop),栈(lpush,lpop)
###########################################################
5.3、set
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd myset "hell"
(integer) 1
127.0.0.1:6379> sadd myset "hahahaha"
(integer) 1
127.0.0.1:6379> sadd myset "tianxia"
(integer) 1
127.0.0.1:6379> smembers myset
1) "hahahaha"
2) "tianxia"
3) "hell"
127.0.0.1:6379> sismember myset hell
(integer) 1
127.0.0.1:6379> sismember myset world
(integer) 0
###########################################################
scard myset # 统计set集合中的元素个数
srem myset hello # 移除元素
smembers myset
set无序,不重复集合,抽随机。
127.0.0.1:6379> srandmember myset
"tianxia"
127.0.0.1:6379> srandmember myset
"hahahaha"
127.0.0.1:6379> srandmember myset
"hahahaha"
127.0.0.1:6379> srandmember myset 2 随机抽出指定个数的随机个数,
"hell"
spop myset # 随机删除一些set集合中的元素
127.0.0.1:6379> flushdb
OK
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 "yangyanmeenfnaewf"
(integer) 1
127.0.0.1:6379> sadd myset2 "hahaha"
(integer) 1
127.0.0.1:6379> smove myset myset2 "hello'
Invalid argument(s)
127.0.0.1:6379> smove myset myset2 "hello"
(integer) 1
127.0.0.1:6379> smembers myset
1) "world"
2) "yangyanmeenfnaewf"
127.0.0.1:6379> smembers myset2
1) "hello"
2) "hahaha"
###########################################################
127.0.0.1:6379> flushdb
OK
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 e
(integer) 1
127.0.0.1:6379> sdiff key1 key2 # 差集
1) "b"
2) "a"
127.0.0.1:6379> sinter key1 key2 # 交集
1) "c"
sunion key1 key2 # 并集
共同关注,二度好友
###########################################################
5.4、hash
map集合,key-
key — map
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> hset myhash field1 hahaah
(integer) 1
127.0.0.1:6379> hget myhash field1
"hahaah"
127.0.0.1:6379> hmset myhash field1 hello field2 world
OK
127.0.0.1:6379> hmget myhash field1 field2
1) "hello"
2) "world"
127.0.0.1:6379> hgetall myhash
1) "field1"
2) "hello"
3) "field2"
4) "world"
hdel myhash field1 #删除指定的值
hlen myhash # 获取长度
hexists myhash field1 # 判断是否存在
hkeys myhash #获取所有的keys
hvals myhash # 获取所有的values
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> hset myhash field3 5
(integer) 1
127.0.0.1:6379> hincrby myhash field3 1 # 自增1
(integer) 6
127.0.0.1:6379> hsetnx myhash field4 hello # 如果不存在则可以设置
(integer) 1
127.0.0.1:6379> hsetnx myhash field4 world
(integer) 0
###########################################################
127.0.0.1:6379> hset user:1 name qinjiang age 12
(integer) 2
127.0.0.1:6379> hget user:1
(error) ERR wrong number of arguments for 'hget' command
127.0.0.1:6379> hget user:1 name
"qinjiang"
127.0.0.1:6379> hget user:1 age
"12"
hash更适合对象(用户信息)的存储,string更适合字符串的存储,
5.5、zset有序集合
127.0.0.1:6379> flushdb
OK
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 2300 xiaohong
(integer) 1
127.0.0.1:6379> zadd salary 5000 zhangshan
(integer) 1
127.0.0.1:6379> zadd salary 400 tianshi
(integer) 1
127.0.0.1:6379> zrangebyscore salary -inf +inf
1) "tianshi"
2) "xiaohong"
3) "zhangshan"
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores
1) "tianshi"
2) "400"
3) "xiaohong"
4) "2300"
5) "zhangshan"
6) "5000"
127.0.0.1:6379> zrevrangebyscore salary +inf -inf # 降序
1) "zhangshan"
2) "xiaohong"
3) "tianshi"
127.0.0.1:6379> zrangebyscore salary -inf 2500 withscores
1) "tianshi"
2) "400"
3) "xiaohong"
4) "2300"
127.0.0.1:6379> zrange salary 0 -1
1) "tianshi"
2) "xiaohong"
3) "zhangshan"
127.0.0.1:6379> zrem salary xiaohong # 移除元素
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "tianshi"
2) "zhangshan"
###########################################################
127.0.0.1:6379> zcard salary # 获取有序集合中的个数
(integer) 2
###########################################################
127.0.0.1:6379> zrevrange salary 0 -1
1) "zhangshan"
2) "tianshi"
# 获取数量,给出区间
127.0.0.1:6379> zadd myset 1 hello 2 world 3 hahaha
(integer) 3
127.0.0.1:6379> zcount myset 1 3
(integer) 3
127.0.0.1:6379> zcount myset 1 2
(integer) 2
应用实例
set排序,
1、存储班级成绩表,工资表排序,
2、普通消息 1;重要消息2,带权重执行。
3、排行榜应用实现,取top N
6、3种特殊数据类型
6.1、geo
朋友的定位,附近的人,打车距离计算,
地理位置信息,两地之间的距离,方圆几里的人。
有效的经度更大,-180 到180,
6个命令
geoadd
geopos
geodist
georadius
127.0.0.1:6379> flushdb
OK
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 114.05 22.52 shenzhen
(integer) 2
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2
###########################################################
# geopos 获得当前定位
127.0.0.1:6379> geopos china:city beijing # 获取指定城市的经纬度
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
127.0.0.1:6379> geopos china:city xian
1) 1) "108.96000176668167114"
2) "34.25999964418929977"
###########################################################
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"
127.0.0.1:6379>
###########################################################
# 查找附近的人
以给定的经纬度为中心,找出某一个半径内的元素
127.0.0.1:6379> georadius china:city 110 30 1000 km # 以110,20经纬度为中心,寻找方圆1000km的城市
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
还可以加参数
127.0.0.1:6379> georadius china:city 110 30 1000 km withdist
1) 1) "chongqing"
2) "341.9374"
2) 1) "xian"
2) "483.8340"
3) 1) "shenzhen"
2) "924.6408"
4) 1) "hangzhou"
2) "977.5143"
127.0.0.1:6379> georadius china:city 110 30 1000 km withcoord
1) 1) "chongqing"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) 1) "108.96000176668167114"
2) "34.25999964418929977"
3) 1) "shenzhen"
2) 1) "114.04999762773513794"
2) "22.5200000879503861"
4) 1) "hangzhou"
2) 1) "120.1600000262260437"
2) "30.2400003229490224"
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) "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 shanghai
1) "wx4fbxxfke0"
2) "wtw3sj5zbj0"
实现原理:
geo底层实现原理就是Zset!我们可以使用Zset命令来操作geo
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"
6.2、hyperloglog
基数:不重复的元素个数
Hyperloglog数据结构、用来做基数统计的算法、
优点:占用的内存是固定的,2^64不同元素,只需12kB内存、0.81%错误率
统计网页的UV,(一个人访问一个网站多次,只算一次)
直播人气值
传统方法:使用set保存用户ID,然后统计set
但是如果保存大量的用户id,就会比较麻烦,目的是统计个数,而不是保存用户id,
127.0.0.1:6379> pfadd mykey a b c d e f i j # 创建第一组元素
(integer) 1
127.0.0.1:6379> pfcount mykey # 统计基数数量
(integer) 8
127.0.0.1:6379> pfadd mykey2 i j q w e r t f a
(integer) 1
127.0.0.1:6379> pfconut mykey2
(error) ERR unknown command 'pfconut'
127.0.0.1:6379> pfcount mykey2
(integer) 9
127.0.0.1:6379> pfmerge mykey3 mykey mykey2 # 合并两组
OK
127.0.0.1:6379> pfcount mykey3
(integer) 12
6.3、Bitmaps
统计疫情感染人数 0 1 010101010101010101
用户信息,活跃不活跃,登录未登录,打卡未打卡,
只有两个状态的
位图,也是一种数据结构,都是操作二进制位进行记录、就只有0 和1两种状态
使用bitmap记录周一到周日的打卡、
127.0.0.1:6379> flushdb
OK
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 0
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
127.0.0.1:6379> getbit sign 3 # 查看某一天有没有打卡
(integer) 0
127.0.0.1:6379> bitcount sign # 统计这周的打卡记录,就可以看有没有全勤
(integer) 3