文章目录
Nosql概述
为什么要用Nosql
-
我们正处于大数据时代。对于大数据,一般的数据库无法进行分析处理
-
思考一下,整个网站的瓶颈是什么?
-
优化服务器发展过程:优化数据结构和索引->文件缓存->Memcached(当时最热门的技术)
-
再然后,分库分表 + 水平拆分 + MysqL集群
-
有的使用MysqL来存一些比较大的文件,博客,图关系等等。数据库表很大,效率就低了,如果有一种数据库来专门处理这种数据,MysqL压力就变得十分小(研究如何处理这些问题)大数据的IO压力下,表几乎没法更大,1亿条数据动态加个列,相当于要加1亿条数据
-
目前一个基本的互联网项目
-
为什么要用Nosql
什么是Nosql
- Nosql = Not Only sql (不仅仅是sql)
- 关系型数据库:表格,行,列
- 泛指非关系型数据库,随着web2.0互联网的诞生,传统的关系型数据库很难对付web2.0时代,尤其是超大规模的高并发的社区,会暴露出来很多难以克服的问题,Nosql在当今大数据环境下发展的十分迅速,Redis是发展最快的,而且是我们当下必须要掌握的一个技术
- 很多的数据类型用户的个人信息,社交网络,地理位置,这些数据类型的存储不需要一个固定的格式,不需要多余的操作就可以横向扩展的,如万能的
Map<String, Object>
使用键值对来控制
Nosql特点
- 方便扩展 (数据之间没有关系,很好扩展。解耦)
- 大数据量,高性能 (Redis一秒可以写8W次,读取11W次,Nosql的缓存记录级,是一种细粒度的缓存,性能会比较高)
- 数据类型是多样型的 (不需要事先设计数据库,随取随用,如果表数据量十分大,很多人就无从设计了)
- 传统RDBMS和Nosql的区别
- 了解3V + 3高
- 真正的在公司中的实践:Nosql + RDBMS 一起使用才是最强的
Nosql的四大分类
@H_404_268@KV键值对 @H_404_268@文档型数据库- 文档型数据库是bson格式,和json差不多
- MongoDB (一般必须要掌握)
- ConthDB
- HBase
- 分布式文件系统
- 它不是存图形,放的是关系,比如:朋友圈社交网络,广告推荐
- Neo4j,InfoGrid
分类 | Examples举例 | 典型应用场景 | 数据模型 | 优点 | 缺点 |
---|---|---|---|---|---|
Redis入门
概述
- Redis是什么
- Redis(Remote Dictionary Server ),即远程字典服务。是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API
- redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步
- 免费和开源,当下最热门的Nosql技术之一,也被人们称之为结构化数据库
- Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(high availability)
- Redis能干嘛
- 内存存储、持久化,内存是断电即失、所以说持久化很重要 (rdb、aof)
- 效率高,可以用于高速缓存
- 发布订阅系统
- 地图信息分析
- 计时器、计数器 (浏览量)
- …
- 特性
- 多样的数据类型
- 持久化
- 集群
- 事务
- …
- Redis推荐都是在Linux服务器上搭建的
安装
@H_404_268@Windows安装 @H_404_268@Linux安装正常安装
- 下载安装包
redis-6.2.4.tar.gz
- 解压Redis安装包,程序一般放
/opt
目录下 - 安装基本环境
sudo yum install centos-release-scl
sudo yum install devtoolset-7-gcc*
scl enable devtoolset-7 bash
make
make install
确认安装完毕
- redis的默认安装路径
/usr/local/bin
- 将redis配置文件复制到当前目录
cp /opt/redis-6.2.4/redis.conf testconfig
- redis默认不是后台启动的,去配置文件把
daemonize no改成yes
- 回到默认安装路径,
redis-server testconfig/redis.conf
通过指定的配置文件启动redis redis-cli -p 6379
使用Redis客户端进行连接- ping测试
- 查看redis进程状态
ps -ef|grep redis
shutdown
关闭redis
docker安装
docker run --name testRedis -d -p 6379:6379 redis
拉取最新版镜像并后台启动容器,测试用,没配置挂载docker exec -it testRedis /bin/bash
进入容器redis-cli
使用Redis客户端进行连接- ping测试
shutdown
关闭redis
测试性能
-
redis-benchmark命令参数
-
简单测试
# 测试:100个并发 100 000个请求 redis-benchmark -h loaclhost -p 6379 -c 100 -n 100000
-
- 第一行:10w个请求写入测试
- 第二行:100个并发客户端
- 第三行:每次写入3个字节
- 第四行:只有一台服务器处理
基础知识
-
redis默认有16个数据库,默认使用的是第0个。
select 2
切换到第三个数据库127.0.0.1:6379> dbsize # 查看数据库已用容量 (integer) 4 127.0.0.1:6379> keys * # 查看所有key 1) "test" 2) "counter:__rand_int__" 3) "mylist" 4) "key:__rand_int__" 127.0.0.1:6379> select 1 # 切换数据库 OK 127.0.0.1:6379[1]> dbsize (integer) 0
-
清除当前数据库
flushdb
,清除所有数据库flushall
127.0.0.1:6379> flushdb OK 127.0.0.1:6379> keys * (empty array) 127.0.0.1:6379> set name cbc OK 127.0.0.1:6379> get name "cbc" 127.0.0.1:6379> select 2 OK 127.0.0.1:6379[2]> flushall OK 127.0.0.1:6379[2]> select 0 OK 127.0.0.1:6379> keys * (empty array)
-
思考:为什么redis是6379 (了解一下即可)
- 6379在是手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字
-
Redis是单线程的。Redis4.0之前是单线程运行的,Redis4.0后开始支持数据异步删除,Redis6.0后支持多线程
-
Redis为什么单线程还这么快
五大数据类型
-
Redis-Key
127.0.0.1:6379> set name cbc OK 127.0.0.1:6379> set age 1 OK 127.0.0.1:6379> exists name # 判断是否有该键,是返回1,否返回2 (integer) 1 127.0.0.1:6379> exists name1 (integer) 0 # rename重命名k 127.0.0.1:6379> rename k key OK # 移动键值到其他数据库 127.0.0.1:6379> move name 2 # 移入3号数据库 (integer) 1 127.0.0.1:6379> select 2 OK 127.0.0.1:6379[2]> keys * # 查看所有key 1) "name" # 设置键值的过期时间 127.0.0.1:6379[2]> expire name 10 # 设置10s后过期 (integer) 1 127.0.0.1:6379[2]> ttl name # time to live 查看该键剩余时间 (integer) 5 127.0.0.1:6379[2]> ttl name (integer) 2 127.0.0.1:6379[2]> ttl name (integer) 0 127.0.0.1:6379[2]> ttl name (integer) -2 # -2代表已经没了,-1代表不会过期 127.0.0.1:6379> type age # 查看当前key的类型 string
String (字符串)
127.0.0.1:6379> get age
"1"
127.0.0.1:6379> append age 1 # append字符串拼接,动态修改存的字符串,如果当前key不存在,就相当于set了一个key
(integer) 2 # 拼接后字符串长度
127.0.0.1:6379> get age
"11"
127.0.0.1:6379> append age "1" # 这样写也行," "可用来追加空格
(integer) 3
127.0.0.1:6379> get age
"111"
127.0.0.1:6379> strlen age # strlen获取字符串长度
(integer) 3
127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> incr views # incr自增
(integer) 1
127.0.0.1:6379> get views
"1"
127.0.0.1:6379> decr views # decr自减
(integer) 0
127.0.0.1:6379> incrby views 10 # 自增10
(integer) 10
127.0.0.1:6379> decrby views 10 # 自减10
(integer) 0
127.0.0.1:6379> getrange key1 0 3 # getrange截取字符串,[0,3]
"hell"
127.0.0.1:6379> getrange key1 0 -1 # -1代表到结尾
"hello,world"
127.0.0.1:6379> getrange key1 -1 -1 # 最后一个字符
"d"
127.0.0.1:6379> get key2
"abcdefg"
127.0.0.1:6379> setrange key2 1 xx # setrange替换字符串,从下标1开始的两个字符替换成xx
(integer) 7 # 替换后的字符串长度
127.0.0.1:6379> get key2
"axxdefg"
# setex (set with expire) 设置过期时间
# setnx (set if not exist) 不存在再设置 (在分布式锁中会常常使用)
127.0.0.1:6379> setex key3 30 hello # setex设置并使30s后过期
OK
127.0.0.1:6379> setnx mykey redis # 不存在mykey再设置
(integer) 1
127.0.0.1:6379> setnx mykey mongodb
(integer) 0 # key已存在,设置失败
127.0.0.1:6379> get mykey
"redis"
127.0.0.1:6379> del key1 # del k1 k2...(批量)删除键值对
(integer) 1
# mset一次设置多个键值
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> keys *
1) "k1"
2) "k3"
3) "k2"
127.0.0.1:6379> mget k1 k2 k3 # mget批量取值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4 # msetnx批量不存在再设置
(integer) 0 # 因为k1存在,所以失败
127.0.0.1:6379> get k4
(nil) # 发现k4也没设置成功,说明msetnx具有原子性
127.0.0.1:6379> set user:1 {name:zhangsan,age:3} # 存可转化为json对象的字符串
OK
127.0.0.1:6379> get user:1
"{name:zhangsan,age:3}"
127.0.0.1:6379> getset db redis # getset先get再set,取旧值设新值,如果之前的值不存在则相当于直接set
(nil)
127.0.0.1:6379> getset db mongodb
"redis"
127.0.0.1:6379> get db
"mongodb"
List (列表)
-
列表,在redis里,我们可以把list当做栈、队列、阻塞队列。大部分list命令都是以
l
开头的127.0.0.1:6379> lpush list one # list插入数据 (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> rpush list right # 想象成从左往右读的表,lpush是头插往left方向放值,rpush是尾插往right方向放值 (integer) 4 127.0.0.1:6379> lrange list 0 -1 1) "three" 2) "two" 3) "one" 4) "right" 127.0.0.1:6379> lpop list 1 # lpop左移除值,移除一个 1) "three" 127.0.0.1:6379> rpop list 1 # rpop右移除值 1) "right" 127.0.0.1:6379> lrange list 0 -1 1) "two" 2) "one" 127.0.0.1:6379> lindex list 0 # lindex通过下标获取list中某一个值 "two" 127.0.0.1:6379> llen list # llen返回列表的长度 (integer) 2 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 # lrem移除一个one (integer) 1 127.0.0.1:6379> lrem list 1 three # 移除一个three (integer) 1 127.0.0.1:6379> lrange list 0 -1 # 还有一个three 1) "three" 2) "two" 127.0.0.1:6379> lpush list three (integer) 3 127.0.0.1:6379> lrem list 2 three # 移除两个three (integer) 2 127.0.0.1:6379> lrange list 0 -1 1) "two" 127.0.0.1:6379> lrange list 0 -1 1) "hello123" 2) "hello12" 3) "hello1" 4) "hello" 127.0.0.1:6379> ltrim list 1 3 # ltrim截取list中两个下标中间的值,会改变list OK 127.0.0.1:6379> lrange list 0 -1 1) "hello12" 2) "hello1" 3) "hello" 127.0.0.1:6379> rpoplpush list myotherlist # rpoplpush移除列表最右元素并放入另一个列表最左,另一个列表不存在则会创建 "hello1" 127.0.0.1:6379> lrange list 0 -1 1) "hello123" 2) "hello12" 127.0.0.1:6379> lrange myotherlist 0 -1 1) "hello1" 127.0.0.1:6379> exists mylist # exists判断列表是否存在,存在1,不存在0 (integer) 0 127.0.0.1:6379> lset mylist 0 item # 不能往不存在的list用下标存值 (error) ERR no such key 127.0.0.1:6379> lrange list 0 -1 1) "value1" 2) "item" 3) "hello12" 127.0.0.1:6379> lset list 0 value100 # lset,可以用lset把list指定下标的值替换掉 更新 OK 127.0.0.1:6379> lrange list 0 -1 1) "value100" 2) "item" 3) "hello12" 127.0.0.1:6379> lset list 3 test # lset不能设置没有值的下标 (error) ERR index out of range 127.0.0.1:6379> lpush list hello (integer) 1 127.0.0.1:6379> lpush list world (integer) 2 # linsert key before|after pivot value,若存在重复值,则在最左侧开始第一个匹配到的进行插入 127.0.0.1:6379> linsert list before world other # linsert在list的某个值的前或后插入值 (integer) 3 127.0.0.1:6379> lrange list 0 -1 1) "other" 2) "world" 3) "hello"
-
小结
@L_404_51@Set (集合)
-
set中的值是不可以重复的,看起来是根据字母顺序排列的,set命令基本都是以
s
开头的127.0.0.1:6379> sadd myset hello # sadd添加元素 (integer) 1 127.0.0.1:6379> smembers myset # smembers查看所有元素 1) "cbc" 2) "hello" 3) "sb" 127.0.0.1:6379> sismember myset hello # sismember查看某元素是否在该集合中 是为1,否为2 (integer) 1 127.0.0.1:6379> sismember myset lll (integer) 0 127.0.0.1:6379> scard myset # scard获取set集合中元素的个数 (integer) 6 127.0.0.1:6379> srem myset asdf # srem在集合中删除指定元素 (integer) 1 127.0.0.1:6379> srandmember myset # srandmember随机抽出一个元素 "cbc" 127.0.0.1:6379> SRANDMEMBER myset 2 # 随机抽出指定个数元素 1) "hello" 2) "zbc" 127.0.0.1:6379> spop myset # spop随机干掉一位元素 "aa" 127.0.0.1:6379> spop myset 2 # 随机干掉指定位元素 1) "cbc" 2) "sb" 127.0.0.1:6379> SMEMBERS myset 1) "hello" 2) "zbc" 127.0.0.1:6379> smove myset myset2 hello # smove转移指定元素到另一个集合 (integer) 1 127.0.0.1:6379> SMEMBERS myset2 1) "hello" 127.0.0.1:6379> SMEMBERS myset 1) "zbc" 127.0.0.1:6379> SMEMBERS key1 1) "c" 2) "b" 3) "a" 127.0.0.1:6379> SMEMBERS key2 1) "a" 2) "3" 3) "2" 4) "1" 127.0.0.1:6379> SDIFF key1 key2 # sdiff查看不同元素,key1中与key2不同的元素 1) "c" 2) "b" 127.0.0.1:6379> SDIFF key2 key1 # key2中与key1不同的元素 1) "1" 2) "2" 3) "3" 127.0.0.1:6379> sinter key1 key2 # sinter查看两个集合中相同的元素 共同好友可以这样实现 1) "a" 127.0.0.1:6379> sinter key2 key1 # 等价于上 1) "a" 127.0.0.1:6379> SUNION key1 key2 # sunion合并去重 1) "3" 2) "a" 3) "b" 4) "c" 5) "1" 6) "2"
-
共同关注,共同爱好,二度好友 、推荐好友(六度分割理论)
Hash (哈希)
-
Map的集合,
key-<key,value>
,本质和string类型没有太大区别,hash命令基本都是以h
开头的127.0.0.1:6379> hset myhash field1 cbc # hset设置哈希中的元素 (integer) 1 127.0.0.1:6379> hget myhash field1 # hget获取哈希中的元素 "cbc" 127.0.0.1:6379> hmset myhash field1 hello field2 world # hmset批量设置哈希中的元素,4.0后已被弃用,hset也可以做到 OK 127.0.0.1:6379> hmget myhash field1 field2 # hmget批量获取哈希中的元素,4.0后已被弃用,hget也可以做到 1) "hello" 2) "world" 127.0.0.1:6379> hgetall myhash # hgetall获取哈希中全部值,以k-v的形式 1) "field1" 2) "hello" 3) "field2" 4) "world" 127.0.0.1:6379> hdel myhash field1 # hdel删除哈希中特定k的一对k-v (integer) 1 127.0.0.1:6379> hgetall myhash 1) "field2" 2) "world" 127.0.0.1:6379> hlen myhash # hlen查看哈希中有多少个元素(键值对) (integer) 1 127.0.0.1:6379> HEXISTS myhash field1 # hexists判断哈希中是否存在该键,是1,否0 (integer) 0 127.0.0.1:6379> HEXISTS myhash field2 (integer) 1 127.0.0.1:6379> hkeys myhash # hkeys获取哈希中所有的键 1) "field2" 2) "field1" 127.0.0.1:6379> hvals myhash # hkeys获取哈希中所有的值 1) "world" 2) "hello" 127.0.0.1:6379> hset myhash field3 5 (integer) 1 127.0.0.1:6379> hincrby myhash field3 1 # hincrby自增1 (integer) 6 127.0.0.1:6379> hincrby myhash field3 -1 # 自减1 (integer) 5 127.0.0.1:6379> hsetnx myhash field4 hello # hsetnx不存在则创建, (integer) 1 127.0.0.1:6379> hsetnx myhash field4 world # 存在则创建失败 (integer) 0
-
hash可以存变更的数据user: name age,尤其是用户信息之类的,可以当做一个实体类来存些基本数据,经常变动的信息
-
hash更适合于对象的存储,string更适合于字符串存储
Zset (有序集合)
-
在set的基础上增加了一个值,
zset k1 score1 v1
,zset命令基本都是以z
开头的127.0.0.1:6379> zadd myset 1 one # zadd添加一个值 (integer) 1 127.0.0.1:6379> zadd myset 2 two 3 three # 添加多个值 (integer) 2 127.0.0.1:6379> ZRANGE myset 0 -1 # 遍历myset所有值,从小到大排序 1) "one" 2) "two" 3) "three" 127.0.0.1:6379> zadd salary 2500 xiaohong 5000 zhangsan 500 cbc # 格式zadd k score1 v1 score2 v2 score3 v3 (integer) 3 127.0.0.1:6379> ZRANGEBYscore salary -inf +inf # zrangebyscore按score从小到大排序,范围为负无穷(-inf)到正无穷(+inf) 1) "cbc" 2) "xiaohong" 3) "zhangsan" 127.0.0.1:6379> ZRANGEBYscore salary -inf +inf withscores # 按score排序并显示k对应的score 1) "cbc" 2) "500" 3) "xiaohong" 4) "2500" 5) "zhangsan" 6) "5000" 127.0.0.1:6379> ZRANGEBYscore salary -inf 2500 withscores # 范围只在负无穷到2500(闭区间),(2500指开区间 1) "cbc" 2) "500" 3) "xiaohong" 4) "2500" 127.0.0.1:6379> ZREVRANGE salary 0 -1 withscores # zrevrange遍历myset所有值,从大到小排序 1) "zhangsan" 2) "5000" 3) "cbc" 4) "500" 5) "cbc" 6) "500" 127.0.0.1:6379> ZREVRANGEBYscore salary +inf -inf withscores # zrevrangebyscore从大到小排序,范围为正无穷到负无穷 1) "zhangsan" 2) "5000" 3) "xiaohong" 4) "2500" 5) "cbc" 6) "500" 127.0.0.1:6379> zrange salary 0 -1 1) "cbc" 2) "xiaohong" 3) "zhangsan" 127.0.0.1:6379> zrem salary xiaohong # zrem移除元素 (integer) 1 127.0.0.1:6379> zrange salary 0 -1 1) "cbc" 2) "zhangsan" 127.0.0.1:6379> zcard salary # zcard获取集合中元素的个数 (integer) 2 127.0.0.1:6379> zrange myset 0 -1 1) "hello" 2) "world" 3) "cbc" 4) "sb" 5) "666" 127.0.0.1:6379> zcount myset (1 3 # zcount获取score区间的元素个数 (integer) 2
-
set的排序版,存储班级成绩表,工资表排序。消息带权重,排行榜
-
这些只是基础API,其他API若有需要,去官方文档查看是最好的
三种特殊数据类型
geospatial (地理位置)
-
朋友的定位,附近的人,打车距离计算
-
相关命令
-
GEOADD
# getadd k 经度 纬度 v 添加地理位置 # 规则:两级无法直接添加。我们一般会下载城市数据,直接通过java程序一次性导入 127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing (integer) 1
-
GEOdisT
# geodist获取两个位置间的距离,单位 m:米 km:千米 mi:英里 ft:英尺,默认为米 127.0.0.1:6379> GEOdisT china:city beijing xian # geodist计算两地之间的距离 "910056.5237" 127.0.0.1:6379> geodist china:city beijing xian km # 千米做单位 "910.0565"
-
-
GEOHASH
-
返回11个字符的Geohash字符串
-
GEOPOS
127.0.0.1:6379> geopos china:city beijing # geopos获取指定的城市的经纬度 1) 1) "116.39999896287918091" 2) "39.90000009167092543" 127.0.0.1:6379> GEOPOS china:city beijing xian # 获取多个城市的经纬度 1) 1) "116.39999896287918091" 2) "39.90000009167092543" 2) 1) "108.96000176668167114" 2) "34.25999964418929977"
-
-
GEORADIUS
# 以给定的经度纬度为中心,找出某一半径内的元素 127.0.0.1:6379> GEORADIUS china:city 110 30 500 km # 以110,30经纬度为中心查找半径500km内的元素 1) "changqin" 2) "xian" 127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist withcoord # 并显示距离和经纬度 1) 1) "changqin" 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> GEORADIUS china:city 110 30 500 km count 1 # 限制只取一个 1) "changqin"
-
GEORADIUSBYMEMBER
- 这个命令和 GEORADIUS 命令一样, 都可以找出位于指定范围内的元素, 但是 GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的, 而不是像 GEORADIUS 那样, 使用输入的经度和纬度来决定中心点
-
GEOSEARCH
# 该命令扩展了GEORADIUS命令,因此除了在圆形区域内搜索外,它还支持在矩形区域内搜索。此命令代替了现已弃用的GEORADIUS和GEORADIUSBYMEMBER geosearch key frommember|fromlonlat v byradius|byBox asc|desc withcoord withdist withhash # FROMMEMBER: 使用元素作为中心。FROMLONLAT: 使用经纬度作为中心。 # ASC:相对于中心,从最近到最远对返回的项目进行排序。DESC:相对于中心,从最远到最近对返回的项目进行排序。
-
GEOSEARCHSTORE
-
-
GEO底层实现原理其实就是 Zset ,我们可以使用 Zset 命令来操作GEO
127.0.0.1:6379> type china:city zset 127.0.0.1:6379> zrange china:city 0 -1 1) "changqin" 2) "xian" 3) "shengzheng" 4) "hangzhou" 5) "shanghai" 6) "beijing" 127.0.0.1:6379> zrem china:city changqin # zrem删除GEO的元素 (integer) 1 127.0.0.1:6379> zrange china:city 0 -1 1) "xian" 2) "shengzheng" 3) "hangzhou" 4) "shanghai" 5) "beijing"
hyperloglog
-
什么是基数
- 基数为一个集合内不重复的元素的个数,可以接受误差
-
简介
- Redis 2.8.9 版本就更新了hyperloglog数据结构
- hyperloglog基数统计算法可以用来计算网页的UV (一个人访问一个网站多次,但是还是算作一个人)
- 传统方式,用set保存用户id,这个方式如果保存大量的用户id,就会很麻烦,我们的目的是为了计数,而不是保存用户id
- hyperloglog优点:占用的内存很小。但会有0.81%的错误率,对于统计UV任务可以忽略不计
127.0.0.1:6379> PFADD mykey a b c d e f g h i j k # fadd创建一组元素 (integer) 1 127.0.0.1:6379> PFCOUNT mykey # pfcount统计元素基数数量 (integer) 11 127.0.0.1:6379> PFADD mykey5 a a a a (integer) 1 127.0.0.1:6379> PFCOUNT mykey5 (integer) 1 127.0.0.1:6379> PFADD mykey2 i j z x c v b d (integer) 1 127.0.0.1:6379> PFCOUNT mykey2 (integer) 8 127.0.0.1:6379> pfmerge mykey3 mykey mykey2 # pfmerge合并去重mykey和mykey2元素至mykey3中 OK 127.0.0.1:6379> PFCOUNT mykey3 (integer) 14
-
如果允许hyperloglog的错误率,建议使用,不允许的话使用set或自己的数据类型
bitmap
-
位存储
-
bitmap位图,都是操作二进制位来进行记录,只有0和1
# 使用bitmap来记录周一到周日的打卡 # 周一:1 周二:0 周三:0 周四:1 周五:1 周六:0 周天:1 127.0.0.1:6379> setbit sign 0 1 # setbit key offset value (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) 0 127.0.0.1:6379> setbit sign 6 1 (integer) 0 # getbit查看某天是否打卡 127.0.0.1:6379> getbit sign 3 (integer) 1 # bitcount统计打卡的天数 127.0.0.1:6379> bitcount sign # bitcount key [start] [end],[start]和[end]代表起始和结束字节数 (integer) 4
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。