APP(网站应用) ----> DAL(数据库访问层) —> Mysql(数据库)
90年代,一个基本网站访问量基本不会太大,单个数据库完全足够
那个时候更多去使用静态页面 服务器根本没有太大压力
思考一下,这种情况下:整个网站瓶颈是什么?
1. 数据量如果太大,一个机器放不下
2. 数据的索引 ,一个机器内存也放不下
3. 访问量(读写混合),一个服务器承受不了
只要你出现以上三种情况之一,那么就必须晋级
网站80%情况都是在读写,每次都要去查询数据库的话就十分麻烦!所以说我们希望减轻数据库压力来保证效率
优化数据结构和索引
发展过程: 优化数据结构和索引 --》 文件缓存(IO) --》Memcached(当时最热门技术)
app dal cache
技术和业务的发展的同时,对人的要求也越来越高了
本质:数据库(读、写)
早些年MylSAM:使用的表锁,十分影响效率
转战 Inno:行锁
慢慢的就开始使用分库来解决写的压力!MYSQL在那个年代推出了表分区很少使用
MYSQL的集群,很好满足了那个年代的需求
Mysql关系型数据库就不够用了!数量很多,变化快
Mysql有的使用它来存储比较大的文件博客图片数据表很大,效率就低了!如果有一种数据库专门处理这种数据库Mysql的压力就变得非常小了(研究如何处理这些问题)大数据的 IO压力下 表几乎没法更大!
当前一个项目
为什么用NoSQL!
用户的个人信息:社交网络 地理位置 用户自己生产的数据,用户日志等等爆发式增长
这个时候我们就需要使用Nosql数据库
泛指非关系型数据库,随着web2.0不联网的诞生传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社区!
站长!暴露出来难以克服的问题,NoSQL在当今大数据环境下发展十分迅速Redis是发展最快的当下必须要掌握的
很多的数据类型用户的个人信息,社交网络,地理位置,这些数据类型的存储不需要一个固定的格式!不需要多余的操作横向扩展!Map
NoSQL特点
1、方便扩展(数据之间没有关系,很好扩展!)
2、大数据里高性能(Redis一秒写8万次,读11万,NoSQL缓存记录级,是一种细粒度缓存,性能会表较高)
3.数据类型是多样多式的!(不需要事先设计数据库!随取随用!如果数据庞大,很多人无法设计!)
4.传统的RDBMS和NoSQL
传统的RDBMS
--结构化组织
--SQL
--数据和关系
--操作,数据定义语言
--严格的一致性
--基础的事务
Nosql
-不仅仅是数据
-没有固定的查询语言
-键值对存储,列存储,文档存储,图形数据库(社交关系)
-最终一致性
-CAP定理 和 BASE(异地多活)
-高性能,高可用,高可扩
了解:3V+3高
1.海量Volume
2.多样Varlety
3.实时Velocity
1.高并发
2.高可扩(随时水平拆分)
3.高性能
真正在公式中的实践:NoSQL+RDBMS一起使用才是最强的,阿里巴巴的架构演讲
1.商品的基本信息
名称、价格、商家信息
关系型数据库就可以解决了!Mysql/Oracle 王坚
淘宝内部的MySQL不是大家使用的MySQL
商品的描述、评论(文章较多)
文档型数据库MongDB
3.图片
分布式文件系统FastDFS
-淘宝自己的TFS
-Gooale的 GFS
-Hadoop HDFS
阿里云OSS
4.商品的关键字
-搜索引擎 solr elasticserach
ISerach 多隆
5、商品人们的波段信息
内存数据库
Redis Tair Memache...
6.商品的交易外部支付接口
-第三方应用
大型互联网应用
数据类型太多
数据源太多了经常重构
数据要改造大面积改造
解决问题:
文档型数据库(bson格式和json一样)
MongDB一般必须掌握
MongDB是基于分布式文件存储的数据库,C++编写,主要是处理大量的文档
MongDB是介于关系型和非关系型数据库中中间产品!MongDB是非关系型数据库中功能最丰富,最想关系型数据库
ConthDB
列存储数据库
HBase
分布式文件系系统
他不是存图像,放的是关系,比如:朋友圈社交网络,广告推荐
Neo4j infoGirl
Redis是什么?
Redis(Remote Dictionary Server)即远程字典服务
是一个开源的使用ANSIc语言编写,支持网络,可基于内存亦可持久化的日志型,Key-Value数据库,并提供多种语言的API。
redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从同步。)
Redis可以干嘛?
1.内存存储、持久化、内存断电即失,所有说持久化很重要(rdb,aof)
2.效率高,可以用与高速缓存
3.发布订阅系统
4.地图信息分析
5.计时器,计数器(浏览量)
…
特性
1.多样的数据类型
2.持久化
3.集群
4.事务
…
学习中需要用的东西
1.redis官网
2.中文网
3.github(停止更新好久的)
注意:推荐使用Linux
下载安装包 redis去官网下载
解压Redis安装包! 移动安装包 mv 包名 /文件夹名
tar -zxvf 名字
进入解压后的文件可以看到redis的配置文件
环境安装
yum install gcc-c++
使用gcc -v查看c++信息
7.make install
8.redis默认按照路径/usr/local/bin
9.将redis配置文件,复制到我们当前目录下
首先创建文件 mkdir 文件名
然后cp /opt/redis-6.2.6/redis.conf tconfig
然后vim redis.config
将daemonsize改为yes,守护进程启动
然后wq退出
10.启动Redis服务
回到/usr/local/bin
使用redis-server tconfig(文件夹名)/redis.conf
使用redis-cli -p 6379
使用ping
11.使用redis-cl进行连接测试
ps -ef|grep redis
12、如何关闭Redis服务器呢?shutdown 再次查看
redsi-benchmark是一个测试工具!
官方自带的性能测试工具
redis-benchmark命令参数
序号 | 选项 | 描述 | 默认值 |
---|---|---|---|
1 | -h | 指定服务器主机名 | 127.0.0.1 |
2 | -p | 指定服务器端口 | 6379 |
3 | -s | 指定服务器socket | |
4 | -c | 指定并发连接数 | 50 |
5 | -n | 指定请求数 | 10000 |
6 | -d | 以字节的形式指定SET/GET值的大小 | 2 |
7 | -k | 1=keep alive 0=reconnect | 1 |
8 | -r | SET/GET/INCR使用随机key,SASS使用随机值 | |
9 | -P | 通过传输请求 | 1 |
10 | -q | 强制退出redis,仅显示query/sec值 | |
11 | -csv | 以CSV格式输出 | |
12 | -I | 生成循环,永久执行测试 | |
13 | -[ | ||
14 |
我们来简答测试
测试:100个并发连接 100000请求
redis-benchmark -h localhsot -p 6379 -c 100 -n 100000
====== SET ======
100000 requests completed in 2.61 seconds 第一个100000就是对10万个请求进行测试
100 parallel clients 100并发客户端
3 bytes payload 每次写入三个字节
keep alive: 1 只有一台服务器来处理这些请求,单机性能
host configuration "save": 3600 1 300 100 60 10000
host configuration "appendonly": no
multi-thread: no
Latency by percentile distribution:
0.000% <= 0.223 milliseconds (cumulative count 1)
50.000% <= 1.327 milliseconds (cumulative count 50632)
75.000% <= 1.631 milliseconds (cumulative count 75332)
87.500% <= 2.007 milliseconds (cumulative count 87634)
93.750% <= 2.503 milliseconds (cumulative count 93789)
96.875% <= 3.279 milliseconds (cumulative count 96885)
98.438% <= 4.263 milliseconds (cumulative count 98450)
99.219% <= 5.135 milliseconds (cumulative count 99225)
99.609% <= 5.703 milliseconds (cumulative count 99610)
99.805% <= 6.487 milliseconds (cumulative count 99805)
99.902% <= 8.295 milliseconds (cumulative count 99903)
99.951% <= 9.247 milliseconds (cumulative count 99952)
99.976% <= 9.791 milliseconds (cumulative count 99976)
99.988% <= 10.087 milliseconds (cumulative count 99988)
99.994% <= 10.463 milliseconds (cumulative count 99994)
99.997% <= 10.543 milliseconds (cumulative count 99997)
99.998% <= 10.615 milliseconds (cumulative count 99999)
99.999% <= 10.719 milliseconds (cumulative count 100000)
100.000% <= 10.719 milliseconds (cumulative count 100000) 花了十毫秒处理10万请求
redis默认有16个数据库默认使用的是第0个数据库
sekect 3 切换到3数据库
DBSIZE查看大小
keys * 查看数据库所有key
flushdb 清除当前数据库
flushall 清除全部数据库内容
明白redis是很快的,官方表示,Redis基于内存操作的,CPU不是Redis性能瓶颈,Redis的瓶颈根据机器内存和网络带宽,既然可以使用单线程来实现,就使用单线程
Redis:是c语言写的 官方提供数据100000+的QPS,完全不比key-value的Memecache差
Redis为什么单线程还那么快
1.误区1:高性能的服务器一定是多线程?
2.误区2:多线程(CPU上下文会切换!)一定比单线程效率高
CPU、内存、硬盘速度要有所了解
核心:redis是将所有数据放在内存里面的,所有说使用单线程操作效率是最高的,多线程(cpu会上下文切换,耗时的操作),对于内存来说如果没有上下文切换效率就是最高的。多次读写在一个cpu,在内存情况下就是最佳的
127.0.0.1:6379> keys *
1) "name"
2) "age"
127.0.0.1:6379> exists name 判断当前key是否存在
(integer) 1
127.0.0.1:6379> exists name1
(integer) 0
127.0.0.1:6379> move name 1 移除当前key
(integer) 1
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> set name tang
OK
127.0.0.1:6379> keys *
1) "name"
2) "age"
127.0.0.1:6379> get name
"tang"
127.0.0.1:6379> EXPIRE name 10 设置key过期时间单位是秒
(integer) 1
127.0.0.1:6379> ttl name 查看剩余时间
(integer) 3
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379>
127.0.0.1:6379> keys *
1) "name"
2) "age"
127.0.0.1:6379> type name
string
127.0.0.1:6379> type age
string
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set key1 v1 设置值
OK
127.0.0.1:6379> get key1 获得值
"v1"
127.0.0.1:6379> keys * 获得所有key
"key1"
127.0.0.1:6379> EXISTS key1 判断key1是否存在
(integer) 1
127.0.0.1:6379> append key1 "hello" "hello" 追加字符串如果key不存在就相当于setkey
(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 ",tang"
(integer) 12
127.0.0.1:6379> STRLEN key1
(integer) 12
127.0.0.1:6379> get key1
"v1hello,tang"
127.0.0.1:6379>
步长 i+=
127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views 自增1
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> get views 减1
"2"
127.0.0.1:6379> decr views
(integer) 1
127.0.0.1:6379> get views
"1"
127.0.0.1:6379> INCRBY views 10 设置步长指定自增数
(integer) 11
127.0.0.1:6379> get key1
"v1hello,tang"
127.0.0.1:6379> GETRANGE key1 0 3 获取字符串[0,3]
"v1he"
127.0.0.1:6379> GETRANGE key1 0 -1 获取全部字符串
"v1hello,tang"
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"
127.0.0.1:6379>
setex(set with expire) 设置过期时间
setnx(set if not exist) 不存在设置 在分布式锁中常使用
127.0.0.1:6379> set key3 60
OK
127.0.0.1:6379> setex key3 30 "hello" 设置key3的值为hello,30秒后过期
OK
127.0.0.1:6379> ttl key3
(integer) 24
127.0.0.1:6379> setnx mykey "redis" "redis" 如果mykey不存在创建mykey
(integer) 1
127.0.0.1:6379> keys *
1) "name"
2) "mykey"
3) "key1"
4) "key2"
5) "views"
127.0.0.1:6379> ttl key3
(integer) -2
127.0.0.1:6379> setnx mykey "MongoDB" 如果存在,创建失败
(integer) 0
127.0.0.1:6379> get mykey
"redis"
127.0.0.1:6379>
批量创建
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 同时设置多个值
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k1"
3) "k2"
127.0.0.1:6379> mget k1 k2 k3 获取多个值
4) "v1"
5) "v2"
6) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4 是一个原子性操作要么一起成功要么一起失败
(integer) 0
127.0.0.1:6379> keys *
7) "k3"
8) "k1"
9) "k2"
127.0.0.1:6379>
设置一个user:1对象 值为json字符串来保存一个对象
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"
127.0.0.1:6379>
组合命令
getset
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 mongdb 如果存在,获取原理的值,并设置新的值
"redis"
127.0.0.1:6379> get db
"mongdb"
127.0.0.1:6379>
String类似的使用场景:value除了是我们的字符串还可以是我们的数字
基本的数据类型,列表
在redis里面我们可以把list玩成栈、队列,堵塞队列!
所有的list命令都是用
127.0.0.1:6379> LPUSH list two 将一个值或者多个值,插入到列表头部(左)
(integer) 1
127.0.0.1:6379> LPUSH list three
(integer) 2
127.0.0.1:6379> LRANGE list 0 -1 获取list值
1) "three"
2) "two"
127.0.0.1:6379> LRANGE list 0 1 通过区间获取对应值
1) "three"
2) "two"
127.0.0.1:6379> RPUSH list righr 将一个或多个值,插入到列表尾部(右)
(integer) 3
127.0.0.1:6379> LRANGE list 0 1
1) "three"
2) "two"
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "righr"
127.0.0.1:6379>
LPOP
RPOP
LPOP
RPOP
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "righr"
127.0.0.1:6379> Lpop list 移除list第一个元素
"three"
127.0.0.1:6379> RPOP list
"righr"
127.0.0.1:6379> LRANGE list 0 -1 移除list最后一个元素
1) "two"
127.0.0.1:6379>
127.0.0.1:6379> LRANGE list 0 -1
1) "one"
2) "two"
127.0.0.1:6379> LINDEX list 1 通过下标获得list中的某一个值!
"two"
127.0.0.1:6379> LINDEX list 0
"one"
127.0.0.1:6379>
127.0.0.1:6379> LPUSH list three
(integer) 3
127.0.0.1:6379> LPUSH list four
(integer) 4
127.0.0.1:6379> LPUSH list one
(integer) 5
127.0.0.1:6379> LLEN list
(integer) 5
127.0.0.1:6379> LREM list 1 one
(integer) 1
127.0.0.1:6379> LRANGE list 0 -1
1) "four"
2) "three"
3) "one"
4) "two"
127.0.0.1:6379> LREM list 2 three
(integer) 1
127.0.0.1:6379> LRANGE list 0 -1
1) "four"
2) "one"
3) "two"
127.0.0.1:6379>
127.0.0.1:6379> keys *
1) "list"
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"
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> keys *
1) "myotherlist"
2) "mylist"
127.0.0.1:6379> lrange myotherlist 0 -1
1) "hello2"
127.0.0.1:6379>
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> keys *
1) "myotherlist"
2) "mylist"
127.0.0.1:6379> lrange myotherlist 0 -1
1) "hello2"
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> 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"
127.0.0.1:6379>
实际上是一个链表 before node after 可以在left right都可以插入值
如果key不存在,创建新的链表
如果存在,新增内容
如果移除了所有的值,空链表也代表不存在
两边插入或者改动值,效率最高!中间元素,相对来说效率低一点
Set集合值是不能重读的!
127.0.0.1:6379> sadd myset "hello" set集合中添加元素
(integer) 1
127.0.0.1:6379> sadd myset "kuangshen"
(integer) 1
127.0.0.1:6379> sadd myset "lovekuangshen"
(integer) 1
127.0.0.1:6379> smembers myset 查看指定set所有值
1) "lovekuangshen"
2) "hello"
3) "kuangshen"
127.0.0.1:6379> keys *
1) "myset"
2) "mylist"
127.0.0.1:6379> sismember myset hello 判断某一个值是不是在set集合中
(integer) 1
127.0.0.1:6379> sismember meset world
(integer) 0
127.0.0.1:6379>
127.0.0.1:6379> scard myset
(integer) 3
127.0.0.1:6379>
127.0.0.1:6379> scard myset
(integer) 3
127.0.0.1:6379> srem myset hello 移除set集合中指定元素
(integer) 1
127.0.0.1:6379> scard myset
(integer) 2
127.0.0.1:6379> smembers myset
1) "lovekuangshen"
2) "kuangshen"
127.0.0.1:6379> smembers myset 随机抽选一个元素
1) "lovekuangshen"
2) "kuangshen"
127.0.0.1:6379> srandmember myset
"kuangshen"
127.0.0.1:6379> srandmember myset
"kuangshen"
127.0.0.1:6379> srandmember myset
"kuangshen"
127.0.0.1:6379> srandmember myset
"kuangshen"
127.0.0.1:6379> srandmember myset
"lovekuangshen"
127.0.0.1:6379> smembers myset
1) "lovekuangshen"
2) "kuangshen"
127.0.0.1:6379> spop myset 随机删除一个值
"kuangshen"
127.0.0.1:6379> smembers myset
1) "lovekuangshen"
127.0.0.1:6379>
127.0.0.1:6379> sadd myset "hello"
(integer) 1
127.0.0.1:6379> sadd myset "hello01"
(integer) 1
127.0.0.1:6379> sadd myset "hello02"
(integer) 1
127.0.0.1:6379> sadd myset2 "set2"
(integer) 1
127.0.0.1:6379> sadd myset2 "set3"
(integer) 1
127.0.0.1:6379> sadd myset2 "set4"
(integer) 1
127.0.0.1:6379> smove myset myset2 "hello02"
(integer) 1
127.0.0.1:6379> smembers myset
1) "hello01"
微博 b站 共同关注:(并集)
127.0.0.1:6379> sadd key1 d
(integer) 1
127.0.0.1:6379> sadd key2 a
(integer) 1
127.0.0.1:6379> sadd key2 b
(integer) 1
127.0.0.1:6379> sadd key2 c
(integer) 1
127.0.0.1:6379> srem key2 a
(integer) 1
127.0.0.1:6379> srem key2 b
(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> SMEMBERS key2
1) "d"
2) "e"
3) "c"
127.0.0.1:6379> sdiff key1 key2 差集
1) "a"
2) "b"
127.0.0.1:6379> sinter key1 key2 交集 共同好友
1) "d"
2) "c"
127.0.0.1:6379> SUNION key1 key2 并集
1) "b"
2) "c"
3) "d"
4) "a"
5) "e"
微博B站将关注的人放在set中
map集合 key-map!的时候这个值是map集合!本质和string类型没有太大区别。还是一个简单的key-value
127.0.0.1:6379> hset myhash field1 kuang set具体的 key-value
(integer) 1
127.0.0.1:6379> hget myhash field1 获取一个
"kuang"
127.0.0.1:6379> hmset myhadh field1 hello field2 world set 多个key-value
OK
127.0.0.1:6379> hmget myhash field1
1) "kuang"
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"
127.0.0.1:6379>
127.0.0.1:6379> hdel myhash field1
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"
27.0.0.1:6379> hlen myhash
(integer) 1
127.0.0.1:6379> hmset myhash field1 hello field2 world
OK
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"
3) "field1"
4) "hello"
127.0.0.1:6379> hlen myhash 获取hash表字段数量
(integer) 2
127.0.0.1:6379>
127.0.0.1:6379> HEXISTS myhash field1
(integer) 1
127.0.0.1:6379> HEXISTS myhash field3
(integer) 0
127.0.0.1:6379>
127.0.0.1:6379> hkeys myhash
1) "field2"
2) "field1"
127.0.0.1:6379> hvals myhash
1) "world"
2) "hello"
127.0.0.1:6379>
127.0.0.1:6379> hset myhash field3 5 指定增量
(integer) 1
127.0.0.1:6379> hincrby myhash field3 1
(integer) 6
127.0.0.1:6379> hincrby myhash field3 -1
(integer) 5
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>
hash变更数据user name age键值尤其是用户信息
在set基础上,增加了一个值,set k1 v1 zset k1 score1 v1
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>
127.0.0.1:6379> zadd salary 2500 xiaohong
(integer) 1
127.0.0.1:6379> zadd salary 5000 kuang 添加三个用户
(integer) 1
127.0.0.1:6379> zadd salary 500 zhangsan
(integer) 1
127.0.0.1:6379> zrangebyscore salary -inf +inf 显示全部用户,从小到大排序
1) "zhangsan"
2) "xiaohong"
3) "kuang"
127.0.0.1:6379> ZREVRANGE salary 0 -1 从大道小排序
1) "kuang"
2) "zhangsan"
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores 显示全部并且带成绩
1) "zhangsan"
2) "500"
3) "xiaohong"
4) "2500"
5) "kuang"
6) "5000"
127.0.0.1:6379> zrangebyscore salary -inf 2500 withscores 显示工资小于2500
1) "zhangsan"
2) "500"
3) "xiaohong"
4) "2500"
127.0.0.1:6379>
127.0.0.1:6379> zrange salary 0 -1
1) "zhangsan"
2) "xiaohong"
3) "kuang"
127.0.0.1:6379> zrem salary xiaohong 移除有序集合中元素
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "zhangsan"
2) "kuang"
127.0.0.1:6379> zcard salary 获取集合中的个数
(integer) 2
127.0.0.1:6379> ZREVRANGE salary 0 -1
1) "kuang"
2) "zhangsan"
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> zadd myset 1 hello 2 word 3 kuang
(integer) 3
127.0.0.1:6379> zcount myset 1 3 获取指定区间成员数量
(integer) 3
127.0.0.1:6379>
案例思路:set 排序 存储班级成绩
三种特殊数据类型
朋友定位、附近的人、打车
Redis的Geo在redis3.2版本就推出了
只有六个命令
GEOADD
GEODIST
GEOHASH
GEOPOS
GEORADOUS
GEORADIUSBYMEMBER
geoadd添加位置
朋友的位置、附近的人
Redis的Go在redis3.2版本就推出!这个功能可以推算地理位置的信息,两地之间的距离
只有六个命令
规则:两级无法添加,我们一般会下载城市的数据,直接通过java程序一次性导入
getadd:添加位置
有效的经度从-180度到180度
有效的经度从-85.05112878度到85.05112878
当坐标位置超出上述指定范围时,该命令将会返回一个错误
参数(key) 值
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijin
(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 chongqi 114.05 22.52 shengzhen
(integer) 2
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2
127.0.0.1:6379>
127.0.0.1:6379> geopos china:city beijing 获取指定的经度纬度
1) (nil)
127.0.0.1:6379> geopos china:city beijin chongqi
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:6379>
单位:
127.0.0.1:6379> geodist china:city beijin shanghai km 查看上海到北京直线距离
"1067.3788"
127.0.0.1:6379>
我附近的人?通过半径来查询
所有数据都应该录入china:city ,才会让结果
127.0.0.1:6379> georadius china:city 110 30 1000 km 获取100 30 这个经纬度为中心,寻找方圆1000km内的城市
1) "chongqi"
2) "xian"
3) "shengzhen"
4) "hangzhou"
127.0.0.1:6379> georadius china:city 110 30 500 km 显示到中心距离位置
1) "chongqi"
2) "xian"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist
1) 1) "chongqi"
2) "341.9374"
2) 1) "xian"
2) "483.8340"
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord
1) 1) "chongqi"
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 withdist withcoord 显示他人(经度和纬度)定位信息
1) 1) "chongqi"
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 withdist withcoord count 1 只显示一条
1) 1) "chongqi"
2) "341.9374"
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 2
1) 1) "chongqi"
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>
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijin 1000 km
1) "beijin"
2) "xian"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 4000 km
1) "chongqi"
2) "xian"
3) "shengzhen"
4) "hangzhou"
5) "shanghai"
6) "beijin"
127.0.0.1:6379>
表返回11个字符的Geohash字符串!
将二维的经度转换为一维如果两个字符串越接近那么吉利越近
127.0.0.1:6379> geohash china:city beijin chongqi
1) "wx4fbxxfke0"
2) "wm5xzrybty0"
127.0.0.1:6379>
GEO底层的实现原理其实就是Zset!我们可以使用Zset命令来操作geo!
127.0.0.1:6379> zrange china:city 0 -1 查看地图全部元素
1) "chongqi"
2) "xian"
3) "shengzhen"
4) "hangzhou"
5) "shanghai"
6) "beijin"
127.0.0.1:6379> zrem china:city 0 -1 移除指定
(integer) 0
127.0.0.1:6379>
什么是基数?
A{1,3,5,7,8,9,7} B{1,3,5,7,8}
基数:不重复的元素 = 5,可以接收误差
Redis2.8.9就更新了Hyperloglog数据结构
redsi hyperloglog 基数统计的算法!
优点:占用的内存是固定的2^64不同元素的技术,只需要花费12kb内存!如果从内存角度比较的话Hyperloglog首选
网页的UV(一个人访问一个网站多次,但是还是算作一个人!)
传统的方式,set保存用户的id。然后就可以统计set中的元素数量作为标准判断!
这个方式保存大量的用户id,就会比较麻烦!我们的目的是为了计数,而不是保存用户的id
127.0.0.1:6379> pfadd myset a b c d e h i j #创建一组元素
(integer) 1
127.0.0.1:6379> pfcount myset #统计myset基数数量
(integer) 8
127.0.0.1:6379> pfadd mykey2 i j k l
(integer) 1
127.0.0.1:6379> pfcount mykey2
(integer) 4
127.0.0.1:6379> pfmerge mykey3 myset mykey2 #合并两组 并集
OK
127.0.0.1:6379> pfcount mykey3 #查看并集数量
(integer) 10
127.0.0.1:6379>
运行容错可以使用hyperloglog
如果不允许则不使用
统计疫情感染人数:0 1 0 1 0
统计用户信息,活跃,不活跃!登录、未登录!打卡、未打卡!两个状态都可以使用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 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 6 0
(integer) 0
127.0.0.1:6379> setbit sign 7 0
(integer) 0
127.0.0.1:6379>
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 7
(integer) 0
127.0.0.1:6379> bitcount sign
(integer) 3
127.0.0.1:6379>
Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行的过程中,会安装顺序执行!
一次性、顺序性、排他性!执行一些命令!
------队列 set set set 执行------
Redis事务没有隔离级别的概念!
所有的命令在事务中,没有直接被执行!只有在发起执行命令的时候才会执行!Exec
Redis单条命令保存原子性的,但是事务不保证原子性!
锁:Redis可以使用乐观锁
exec
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)> exec #执行事务
1) OK
2) OK
3) OK
127.0.0.1:6379>
discard
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>
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>
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)> exec
1) OK
2) (error) ERR value is not an integer or out of range #虽然第一条命令报错了,其它命令可以正常执行
3) OK
4) OK
127.0.0.1:6379>
监控 Watch
正常执行成功
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
127.0.0.1:6379>
测试多线程修改值监视失败使用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 money 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>
如果修改失败获取最新的值就好
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 1
QUEUED
127.0.0.1:6379(TX)> incrby money 1
QUEUED
127.0.0.1:6379(TX)> exec 对比监视的值是否发生变化,如果没有变化,那么可以执行成功,如果变量执行失败
1) (integer) 999
2) (integer) 1000
127.0.0.1:6379>