基于狂神视频的笔记
Nosql概述
为什么Nosql
- 原来的架构–单机Mysql
- Memcached(缓存)+mysql+垂直拆分(读写分离)
减轻数据的压力 使用缓存来保证效率
- 分库分表 +水平拆分+Mysql集群
本质:数据库(读,写)
早些年MyISAM:表锁,十分影响效率!高并发下就会出现严重的锁问题
转战Innodb:行锁
慢慢就开始使用分库分表来解决写的压力!
- 如今的年代
Mysql等关系型数据库就不够用了!数据量很多,变化很快~!
目前基本互联网架构
什么是NoSQL
nosql =not only sql 泛指非关系型数据库
很多的数据类型用户的个人信息,社交网络,地理位置等。这些数据类型的存储不需要一个固定的格式!
NoSQL特点
- 方便扩展(数据之间没有关系,很好扩展!)
- 大数据量高性能(Redis 一秒写8w次,读取11万次,NoSQL的缓存记录级,是一种细粒度的缓存,性能会比较高)
- 数据类型是多样型的!(不需要事先设计数据库!随取随用!)
- 传统的RDBMS 和 NoSQL
传统的RDMBMS
- 结构化组织
- sql
- 数据和关系都存在单独的表中
- 操作,数据定义语言
- 严格的一致性
- 基础的事务
- 。。。。。
NoSQL
- 不仅仅是数据
- 没有固定的查询语言
- 键值对存储,列存储 文档存储 图形数据库(社交关系)
- 最终一致性
- CAP定理和BASE(异地多活)
- 高性能,高可用,高可扩
- 。。。。。
了解:3V+3高
大数据时代的3V:主要是描述问题
海量Velume
多样Variety
实时Velocity
大数据时代的3高:主要对程序的要求
高并发
高可扩(随时水平拆分,机器不够直接+)
高性能
阿里巴巴分析
# 1,商品的基本信息
关系型数据库就可以解决
# 2,商品的描述,评论(文字比较多)
文档型数据库,MongoDB
# 3,图片
分布式文件系统 FastDFS
- 淘宝自己的 TFS
- Google的 GFS
- Hadoop HDFS
- 阿里云 oss
# 4,商品的关键字(搜索)
- 搜索引擎 solr elasticsearch
- ISearch
# 5,商品热门的波段信息
- 内存数据库
- Redis Tair Memcache
# 6,商品的交易,外部的指出接口
- 三方应用
大型互联网应用问题:
- 数据类型太多了!
- 数据源繁多,经常重构!
- 数据要改造
解决办法:统一数据服务从 -----UDSL:
NoSQL的四大分类
KV键值对:
- 新浪:Redis
- 美团:Redis+Tair
- 阿里,百度:Redis—+memechache
文档型数据库(bson格式和json一样):
- MangoDB (必须掌握)
MangoDB是以后个基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档!
MangoDB椒词汇表介于关系型数据库和非关系型数据库中中间的产品! MongoDB是非关系型数据库中功能最丰富,最像关系型数据库 的! - ConthDB
列存储数据库
- HBase
- 分布式文件系统
图关系型数据库
- 他不是存图形,放的是关系,比如:朋友前社交网络,广告推荐等
- Neo4j,infoGrid
Redis入门
概述
redis是什么
Redis(Remote Dictionary Server ),即远程字典服务!
能干嘛?
1,内存存储,持久化(rdb,aof)
2,效率高 可以用于告诉缓存
3,发布订阅系统
4,地图信息分析
5,计时器,计数器
。。。。。
特性
1,多样的数据类型
2,持久化
3,集群
4,事务
中文官网:http://www.redis.cn/
Linux安装
# 1 下载安装包
# 2 解压后放入 /opt目录
[root@iZ2vc20ehn0q0ihrgccmd2Z xuanmeng]# tar -zxvf redis-6.0.6.tar.gz
[root@iZ2vc20ehn0q0ihrgccmd2Z xuanmeng]# mv redis-6.0.6 /opt/
[root@iZ2vc20ehn0q0ihrgccmd2Z opt]# ls
containerd redis-6.0.6
# 3 安装基本的环境
yum install gcc-c++
make
make install
# 4 redis的 默认安装路径 /usr/local/bin/
# 5 将redis的配置文件 拷贝到当前目录下 /usr/local/bin/xxx
# 6 redis默认不是后台启动,修改配置文件!redis.conf daemonize 改为yes
# 7 启动redis服务!
[root@iZ2vc20ehn0q0ihrgccmd2Z bin]# redis-server xmconfig/redis.conf
# 8 测试链接
[root@iZ2vc20ehn0q0ihrgccmd2Z bin]# redis-cli -p 6379
127.0.0.1:6379>
# 9 查看redis进程是否开启
[root@iZ2vc20ehn0q0ihrgccmd2Z ~]# ps -ef|grep redis
# 10如何关闭redis shutdown
# 11 再次查看redis的进程
测试性能
redis-benchmark 是一个压力测试工具!
命令参数:
简单测试下:
# 测试:100个并发连接 100000个请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
如何查看这些分析
基础知识
redis默认有16个数据库 默认使用第0个
使用select 切换数据库
[root@iZ2vc20ehn0q0ihrgccmd2Z bin]# redis-cli -p 6379
127.0.0.1:6379> select 2
OK
127.0.0.1:6379[2]> DBSIZE # 查看当前库的大小
# 查看所有的key
keys *
清除当前的数据库 flushdb
清除全部的数据库 flushall
flushdb
Redis是单线程的!
redis是基础内存操作,CPU不是Redis性能瓶颈, Redis的瓶颈是根据机器的内存和网络带宽!
Redis 为什么单线程还这么快?
- 误区1: 高性能的服务器一定是多线程的?
- 误区2:多线程(CPU上下文会切换!耗时)一定比单线程效率高!
核心:redis将所有的数据放入内存中,所以使用单线程操作是最高的!对于内存系统来说 没有上下文切换效率就是最高的!多次读写都是在一个cpu上的 在内存情况下 这个就是最佳的方案!
五大数据类型
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-key
127.0.0.1:6379> select 2 # 选择数据库
OK
127.0.0.1:6379[2]> DBSIZE # 当前DB大小
(integer) 0
127.0.0.1:6379[2]> select 0
OK
127.0.0.1:6379> set name xm
OK
127.0.0.1:6379> get name
"xm"
127.0.0.1:6379> EXISTS name # 判断 key是否存在
(integer) 1
127.0.0.1:6379> keys * # 查看所有keys
1) "myzset"
2) "key:__rand_int__"
3) "counter:__rand_int__"
4) "name"
5) "myhash"
127.0.0.1:6379> EXPIRE name 10 #设置key的过期时间 单位 秒
(integer) 1
127.0.0.1:6379> ttl name # 查看当前key的剩余时间
(integer) 2
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> set name xx
OK
127.0.0.1:6379> MOVE name 1 # 移除key到其他库
(integer) 1
127.0.0.1:6379> get name
127.0.0.1:6379[1]> type name # 查看key的类型
string
官网命令地址:http://www.redis.cn/commands.html
String(字符串)
127.0.0.1:6379> set name xm #设值
OK
127.0.0.1:6379> APPEND name 123 # 给key 追加字符串 (如果key不存在 相当于set key)
(integer) 5
127.0.0.1:6379> get name
"xm123"
127.0.0.1:6379> STRLEN name # 字符串长度
(integer) 5
#####################################
# i++
# 步长 i+=
127.0.0.1:6379> INCR views
(integer) 1
127.0.0.1:6379> INCR views
(integer) 2
127.0.0.1:6379> decr views
(integer) 1
127.0.0.1:6379> decr views
(integer) 0
127.0.0.1:6379> INCRBY views 10 # 指定增量
(integer) 8
127.0.0.1:6379> INCRBY views 10
(integer) 18
127.0.0.1:6379> DECRBY views 5 # 指定减量
(integer) 13
#####################################
# 字符串范围 range
127.0.0.1:6379> set name "hello,world!"
OK
127.0.0.1:6379> get name
"hello,world!"
127.0.0.1:6379> GETRANGE name 0 3
"hell"
127.0.0.1:6379> GETRANGE name 0 -1
"hello,world!"
# 替换
127.0.0.1:6379> set key1 hdhsahd
OK
127.0.0.1:6379> SETRANGE key1 4 jjs
(integer) 7
127.0.0.1:6379> get key1
"hdhsjjs"
#####################################
# setex (set with expire) #设置过期时间
# setnx (set if not exist) #不存在再设置(在分布式锁中会常常使用!)
127.0.0.1:6379> SETEX key3 30 "kbskbs" 设置key3 30秒过期
127.0.0.1:6379> SETNX mykey "springCloud"
(integer) 1
127.0.0.1:6379> SETNX mykey "springBoot"
(integer) 0
127.0.0.1:6379> get mykey
"springCloud"
#####################################
mset
mget
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379> MSETNX k1 v2 k4 v4 # msetnx 是一个原子性的操作 要么一起成功 要么一起失败!
(integer) 0
# 对象
set user:1{name:xm,age:3} # 设置一个user:1 对象 值为json字符来保存一个对象
# 这里的key是一个巧妙的设计: user:{id}:{filed}
127.0.0.1:6379> mset user:1:name xm user:1:age 32
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "xm"
2) "32"
#####################################
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除了是我们的字符串还可以是数字!
- 计数器
- 统计多单位的数量
- 粉丝数
- 对象缓存存储!
List
基本的数据类型,在redis中我们可以吧list 玩成 队列,栈,阻塞队列!
所有的List命令都是 l 开头
127.0.0.1:6379> LPUSH list 1 # lpush 插入到列表头部(左)
(integer) 1
127.0.0.1:6379> LPUSH list 2
(integer) 2
127.0.0.1:6379> LPUSH list 3
(integer) 3
127.0.0.1:6379> LRANGE list 0 1
1) "3"
2) "2"
127.0.0.1:6379> RPUSH list 4 # rpush 插入到列表尾部(右)
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1 # 利用区间获取具体值
1) "3"
2) "2"
3) "1"
4) "4"
###################################
127.0.0.1:6379> LPOP list # 移除list 第一个元素
"3"
127.0.0.1:6379> RPOP list # 移除list 最后一个元素
"4"
###################################
127.0.0.1:6379> LINDEX list 1 # 通过下表获得list的某一个值
###################################
127.0.0.1:6379> LPUSH list 11
(integer) 1
127.0.0.1:6379> LPUSH list 22
(integer) 2
127.0.0.1:6379> LPUSH list 33
(integer) 3
127.0.0.1:6379> LLEN list # list长度
(integer) 3
###################################
移除指定的值!
127.0.0.1:6379> LREM list 1 11 # 移除1个 11内容
(integer) 1
###################################
trim 修剪: list截断!
127.0.0.1:6379> rpush list "hello1"
(integer) 1
127.0.0.1:6379> rpush list "hello2"
(integer) 2
127.0.0.1:6379> rpush list "hello3"
(integer) 3
127.0.0.1:6379> rpush list "hello4"
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "hello1"
2) "hello2"
3) "hello3"
4) "hello4"
127.0.0.1:6379> LTRIM list 1 2 # 通过下表截取指定长度 list被改变 截断后只剩下截断的元素!
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "hello2"
2) "hello3"
###################################
rpoplpush # 移除列表的最后一个元素
127.0.0.1:6379> LRANGE list 0 -1
1) "hello1"
2) "hello2"
3) "hello3"
4) "hello4"
127.0.0.1:6379> RPOPLPUSH list list2
"hello4"
127.0.0.1:6379> RPOPLPUSH list list2
"hello3"
127.0.0.1:6379> LRANGE list2 0 -1
1) "hello3"
2) "hello4"
###################################
lset 将列表中指定下标的值替换为另外一个值,更新操作
127.0.0.1:6379> LSET list 0 111 # 如果不存在列表 更新报错
(error) ERR no such key
127.0.0.1:6379> LPUSH list v1
(integer) 1
127.0.0.1:6379> lset list 0 v11 # 如果不存在列表 更新值
OK
127.0.0.1:6379> LRANGE list 0 -1
###################################
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 hello xm
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1
1) "xm"
2) "hello"
3) "world"
小结
消息队列:LPUSH RPOP 反过来一样(RPUSH LPOP)
栈:LPUSH LPOP
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 xm
(integer) 1
127.0.0.1:6379> SMEMBERS myset # 查看指定set的所有值
1) "world"
2) "xm"
3) "hello"
127.0.0.1:6379> SISMEMBER myset hello # 是否存在某个值
(integer) 1
127.0.0.1:6379> SISMEMBER myset 123
(integer) 0
##############################################
127.0.0.1:6379> SCARD myset # 获取set集合中的元素个数
(integer) 3
##############################################
127.0.0.1:6379> SREM myset hello # 移除set中的某个元素
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "world"
2) "xm"
##############################################
127.0.0.1:6379> SRANDMEMBER myset # 随机抽选出一个元素
"world"
127.0.0.1:6379> SRANDMEMBER myset 2 # 随机抽选出n个元素
"xm"
"world"
##############################################
删除指定的key,随机删除key!
127.0.0.1:6379> SPOP myset # 随机删除一个值
"1232"
##############################################
将一个指定的值 移动到另外一个set集合
127.0.0.1:6379> sadd myset2 xxx
(integer) 1
127.0.0.1:6379> SMOVE myset myset2 hello
(integer) 1
127.0.0.1:6379> SMEMBERS myset2
1) "xxx"
2) "hello"
##############################################
- 并集 SUNION
- 交集 SINTER
- 差集 SDIFF
127.0.0.1:6379> sadd set1 a
(integer) 1
127.0.0.1:6379> sadd set1 b
(integer) 1
127.0.0.1:6379> sadd set1 c
(integer) 1
127.0.0.1:6379> sadd set2 c
(integer) 1
127.0.0.1:6379> sadd set2 d
(integer) 1
127.0.0.1:6379> sadd set2 e
(integer) 1
127.0.0.1:6379> SDIFF set1 set2 # 差集
1) "a"
2) "b"
127.0.0.1:6379> SINTER set1 set2 # 交集
1) "c"
127.0.0.1:6379> SUNION set1 set2 # 并集
1) "a"
2) "b"
3) "c"
4) "e"
5) "d"
微博 A用户将所有关注的人放在一个set集合中!将他的粉丝也放在一个集合中! 共同关注等(六度分割理论)
Hash(哈希)
Map集合,key-map集合!本质和String类型没有太大区别!
127.0.0.1:6379> HSET myhash f1 xuanmeng # set一个具体 k-v
(integer) 1
127.0.0.1:6379> hget myhash f1
"xuanmeng"
127.0.0.1:6379> HMSET myhash f1 xxx f2 ddd # set多个 k-v
OK
127.0.0.1:6379> HMGET myhash f1 f2 # 获取多个字段值
1) "xxx"
2) "ddd"
127.0.0.1:6379> HGETALL myhash # 获取全部数据
1) "f1"
2) "xxx"
3) "f2"
4) "ddd"
############################################
127.0.0.1:6379> HDEL myhash f1 # 删除hash指定key value也会删除
(integer) 1
127.0.0.1:6379> HGETALL myhash
1) "f2"
2) "ddd"
############################################
127.0.0.1:6379> HLEN myhash # 获取字段数量
(integer) 4
############################################
127.0.0.1:6379> HEXISTS myhash f1 #判断hash指定字段是否存在!
(integer) 1
127.0.0.1:6379> HEXISTS myhash f5
(integer) 0
############################################
127.0.0.1:6379> HKEYS myhash # 只获得所有filed
1) "f2"
2) "f1"
3) "f3"
4) "f4"
127.0.0.1:6379> HVALS myhash # 只获得所有value值
1) "ddd"
2) "qqq"
3) "www"
4) "eee"
############################################
HINCRBY myhash f1 1 #指定增量! 如果不存在报错
DECRBY # 指定减量!
127.0.0.1:6379> HSETNX myhash f5 ddd #如果不存在则可以设置
(integer) 1
127.0.0.1:6379> HSETNX myhash f5 ddd #如果存在则不能设置
(integer) 0
############################################
hash变更数据(user name age )尤其是用户信息之类 经常变动的信息!
Zset(有序集合)
在set的基础上 增加了一个值
127.0.0.1:6379> ZADD myset 1 noe
(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) "noe"
2) "two"
3) "three"
############################################
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # 从小到大排序
1) "zhangsan"
2) "lisi"
3) "wangwu"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores
1) "zhangsan"
2) "1000"
3) "lisi"
4) "5000"
5) "wangwu"
6) "5500"
127.0.0.1:6379> ZREVRANGE salary 0 -1 withscores #从大到小排序
1) "wangwu"
2) "5500"
3) "lisi"
4) "5000"
127.0.0.1:6379> ZRANGEBYSCORE salary 1000 5000 # 1000-5000之间的排序
1) "zhangsan"
2) "lisi"
############################################
# 移除rem中的元素
127.0.0.1:6379> ZRANGE salary 0 -1
1) "zhangsan"
2) "lisi"
3) "wangwu"
127.0.0.1:6379> ZREM salary zhangsan #移除某个元素
(integer) 1
127.0.0.1:6379> ZRANGE salary 0 -1
1) "lisi"
2) "wangwu"
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 3 xm
(integer) 2
127.0.0.1:6379> ZCOUNT myset 1 3 #指定区间的成员数量!
(integer) 3
############################################
案例思路:set 排序 存储班级成绩 工资等
普通消息 1 重要 2 带权重判断!
排行榜应用实现!!
三种特殊数据类型
geospatial 地理位置
朋友定位,附近的人,打车距离计算!
geoadd
# geoadd 添加地理位置
# 规则:两极无法直接添加
# 参数 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.50 29.53 chongqing 114.05 22.52 shenzheng
(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 shanghai chongqing
1) 1) "121.47000163793563843"
2) "31.22999903975783553"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
geodist
两人之间的距离!
单位:
- m 米
- km 千米
- mi 英里
- ft 英尺
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"
georadius 以指定的经纬度为中心 找出某一半径内的地方
我附近的人?
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km # 以 110 30为中心 寻找方圆1000km内的城市(所有数据要在china:city中)
1) "chongqing"
2) "xian"
3) "shenzheng"
4) "hangzhou"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist # 显示到中心点的直线距离
1) 1) "chongqing"
2) "341.9374"
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.52999957900659211"
2) 1) "xian"
2) 1) "108.96000176668167114"
2) "34.25999964418929977"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord withdist 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"
georadiusbymember
# 找出位于指定元素周围的其他元素!
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km
1) "beijing"
2) "xian"
geohash : 返回一个或多个位置元素的geohash表示
GEO底层的实现原理其实就是Zset!
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqing"
2) "xian"
3) "shenzheng"
4) "hangzhou"
5) "shanghai"
6) "beijing"
127.0.0.1:6379> ZREM china:city shenzheng
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqing"
2) "xian"
3) "hangzhou"
4) "shanghai"
5) "beijing"
Hyperloglog
什么是基数?
不重复的元素
A{1,3,5,7,8,7}
B{1,3,5,7,8}
基数=5 可以接受误差!
简介
优点:占用内存是固定
redis hyperloglog 基数统计的算法!
网页的UV(一个人访问一个网页多次,但是还是算一个人!)
传统方式 set保存用户的id 然后就可以统计set中的元素数量作为判断标准!
目的:用来计数
测试使用:
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 # 统计 基数数量
(integer) 10
127.0.0.1:6379> PFADD mykey2 i j k l m # 创建第二组元素
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 5
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 # 合并两组 mykey mykey2 到 mykey3 并集
OK
127.0.0.1:6379> PFCOUNT mykey3
(integer) 13
如果允许容错 那么可以使用Hyperloglog!
Bitmaps
位存储
如 统计疫情感染人数: 0 1 0 1 0 0 0
统计用户信息,活跃,不活跃! 登录 未登录!打卡,不打卡!
2个状态的 都可以使Bitmaps
BitMaps 位图,数据结构! 都是操作二进制位来进行记录 只有0和1 两个状态!
使用bitmaps记录 周一到周日的打卡!
127.0.0.1:6379> SETBIT sign 0 0
(integer) 0
127.0.0.1:6379> SETBIT sign 1 1
(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 1
(integer) 0
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> BITCOUNT sign # 统计打卡天数!
(integer) 4