redis笔记

文章目录

  • Nosql概述
      • 为什么要用Nosql
        • 1、单机Mysql的年代
        • 2、Memcached(缓存)+Mysql+垂直拆分
        • 3、分库分表+水平分表+MYSQL集群
        • 4、如今年代
      • 什么是NoSQL
        • NoSQL = Not Only SQL(不仅仅是SQL)
          • 解耦
        • 大数据的3V:主要是描述问题的
        • 大数据的3高:主要是对程序的要求
      • NoSQL四大分类
        • KV键值对:
      • Redis入门
      • Linux安装
      • 测试性能
      • 基础知识
      • redis是单线程的
      • 五大数据类型
        • Redis-key
          • 查看key类型
        • String
          • i++
          • 字符串范围range
          • 替换
          • setex(set with expire) 设置过期时间
          • setnx(set if not exist) 不存在设置 在分布式锁中常使用
        • List
          • LPUSH
          • RPUSH
          • 移除一个元素
          • lindex通过下标获得list中的某一个值
          • 获取list长度
          • 移除指定的值,精确匹配
          • tirm 修剪。 list:截断
          • rpoplpush 移除列表最后一个元素
          • lset将列表中指定下标的值替换为另外一个值,更新操作
          • linsert将某一个具体的value插入到你中某个元素或者后面
        • 小结
        • Set
          • 获取set集合中的内容元素个数
          • 移除某一个元素
          • 随机抽选元素
          • 删除指定的key、随机删除key
          • 将一个指定的值,移动到另外一个set集合中
          • 数字集合:差集 交集 并集
        • Hash(哈希)
          • set myhash field 名字
          • 删除指定hash
          • 获得hash长度
          • 判断指定hash是否存在
          • 只获得field
          • 只获得value
          • incr decr
          • hsetnx
        • Zset(有序集合)
          • 排序如何实现
          • 移除元素
          • zcount
        • geospatital地理位置
          • geoadd添加地理位置
          • geopos获取当前定位
          • geodist两人之间的距离
          • georadius以给定的纬度为中心,找出一半径内的元素
          • georadiusbymember:找出位于指定元素周围的其它元素
          • geohash命令返回一个或多个位置元素的Geohash表示
        • Hyperloglog
          • 简介
        • Bitmaps 位存储
          • 使用bitmap记录周一到周日七天打卡 分为两种状况 0和1 表示 未打卡 和 打卡
          • 查看某一天是否打卡
          • 统计打卡天数
        • 事务
          • redis事务:
          • 正常执行事务
          • 放弃事务
          • 编译形异常(命令有错),事务中命令都不会执行!
          • 运行时异常(1/0),如果事务队列中存在语法性,那么执行命令的时候,其它命令式可以正常执行,错误命令会抛出异常
        • 悲观锁:
        • 乐观锁:
          • Redis测监视测试

Nosql概述

为什么要用Nosql

1、单机Mysql的年代

​ APP(网站应用) ----> DAL(数据库访问层) —> Mysql(数据库)
redis笔记_第1张图片

90年代,一个基本网站访问量基本不会太大,单个数据库完全足够

那个时候更多去使用静态页面 服务器根本没有太大压力

思考一下,这种情况下:整个网站瓶颈是什么?

1. 数据量如果太大,一个机器放不下
2. 数据的索引 ,一个机器内存也放不下
3. 访问量(读写混合),一个服务器承受不了

只要你出现以上三种情况之一,那么就必须晋级

2、Memcached(缓存)+Mysql+垂直拆分

redis笔记_第2张图片

网站80%情况都是在读写,每次都要去查询数据库的话就十分麻烦!所以说我们希望减轻数据库压力来保证效率

优化数据结构和索引

发展过程: 优化数据结构和索引 --》 文件缓存(IO) --》Memcached(当时最热门技术)

3、分库分表+水平分表+MYSQL集群
app dal cache 
技术和业务的发展的同时,对人的要求也越来越高了
本质:数据库(读、写)
早些年MylSAM:使用的表锁,十分影响效率
转战 Inno:行锁
慢慢的就开始使用分库来解决写的压力!MYSQL在那个年代推出了表分区很少使用
MYSQL的集群,很好满足了那个年代的需求

redis笔记_第3张图片
)]

4、如今年代
Mysql关系型数据库就不够用了!数量很多,变化快
Mysql有的使用它来存储比较大的文件博客图片数据表很大,效率就低了!如果有一种数据库专门处理这种数据库Mysql的压力就变得非常小了(研究如何处理这些问题)大数据的	IO压力下 表几乎没法更大!
当前一个项目

redis笔记_第4张图片

为什么用NoSQL!

用户的个人信息:社交网络 地理位置 用户自己生产的数据,用户日志等等爆发式增长

这个时候我们就需要使用Nosql数据库

什么是NoSQL

NoSQL = Not Only SQL(不仅仅是SQL)

泛指非关系型数据库,随着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高
大数据的3V:主要是描述问题的

1.海量Volume

2.多样Varlety

3.实时Velocity

大数据的3高:主要是对程序的要求

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.商品的交易外部支付接口
-第三方应用

大型互联网应用

数据类型太多

数据源太多了经常重构

数据要改造大面积改造

解决问题:

NoSQL四大分类

KV键值对:

文档型数据库(bson格式和json一样)

MongDB一般必须掌握

​ MongDB是基于分布式文件存储的数据库,C++编写,主要是处理大量的文档

MongDB是介于关系型和非关系型数据库中中间产品!MongDB是非关系型数据库中功能最丰富,最想关系型数据库

ConthDB

列存储数据库

HBase

分布式文件系系统

他不是存图像,放的是关系,比如:朋友圈社交网络,广告推荐

Neo4j infoGirl

Redis入门

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

Linux安装

  1. 下载安装包 redis去官网下载

  2. 解压Redis安装包! 移动安装包 mv 包名 /文件夹名

    tar -zxvf 名字
    

在这里插入图片描述

  1. 进入解压后的文件可以看到redis的配置文件

  2. 环境安装

    yum install gcc-c++
    
  3. 使用gcc -v查看c++信息

在这里插入图片描述

  1. 使用make下载环境

redis笔记_第5张图片

​ 7.make install

redis笔记_第6张图片

​ 8.redis默认按照路径/usr/local/bin

redis笔记_第7张图片

​ 9.将redis配置文件,复制到我们当前目录下

首先创建文件 mkdir 文件名
然后cp /opt/redis-6.2.6/redis.conf tconfig
然后vim redis.config
将daemonsize改为yes,守护进程启动
然后wq退出

redis笔记_第8张图片

​ 10.启动Redis服务

回到/usr/local/bin
使用redis-server tconfig(文件夹名)/redis.conf
使用redis-cli -p 6379
使用ping

redis笔记_第9张图片

11.使用redis-cl进行连接测试
ps -ef|grep redis

redis笔记_第10张图片

​ 12、如何关闭Redis服务器呢?shutdown 再次查看

redis笔记_第11张图片

redis笔记_第12张图片

测试性能

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

redis笔记_第13张图片

====== 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

redis笔记_第14张图片

flushdb 清除当前数据库

redis笔记_第15张图片

flushall 清除全部数据库内容

redis笔记_第16张图片

redis是单线程的

明白redis是很快的,官方表示,Redis基于内存操作的,CPU不是Redis性能瓶颈,Redis的瓶颈根据机器内存和网络带宽,既然可以使用单线程来实现,就使用单线程

Redis:是c语言写的 官方提供数据100000+的QPS,完全不比key-value的Memecache差

Redis为什么单线程还那么快

1.误区1:高性能的服务器一定是多线程?

2.误区2:多线程(CPU上下文会切换!)一定比单线程效率高

CPU、内存、硬盘速度要有所了解

核心:redis是将所有数据放在内存里面的,所有说使用单线程操作效率是最高的,多线程(cpu会上下文切换,耗时的操作),对于内存来说如果没有上下文切换效率就是最高的。多次读写在一个cpu,在内存情况下就是最佳的

五大数据类型

Redis-key
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> 

查看key类型
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
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++

步长 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
字符串范围range
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) 不存在设置 在分布式锁中常使用
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除了是我们的字符串还可以是我们的数字

List

基本的数据类型,列表

在redis里面我们可以把list玩成栈、队列,堵塞队列!

所有的list命令都是用

LPUSH
RPUSH
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> 
lindex通过下标获得list中的某一个值
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> 
获取list长度
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>
tirm 修剪。 list:截断
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"
rpoplpush 移除列表最后一个元素
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>
lset将列表中指定下标的值替换为另外一个值,更新操作
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"
linsert将某一个具体的value插入到你中某个元素或者后面
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

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> 
获取set集合中的内容元素个数
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"

删除指定的key、随机删除key
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> 
将一个指定的值,移动到另外一个set集合中
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中

Hash(哈希)

map集合 key-map!的时候这个值是map集合!本质和string类型没有太大区别。还是一个简单的key-value

set myhash field 名字
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> 
删除指定hash
127.0.0.1:6379> hdel myhash field1
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"
获得hash长度
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> 
判断指定hash是否存在
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> 

只获得field
只获得value
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> 

incr decr
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

hsetnx
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键值尤其是用户信息

Zset(有序集合)

在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

zcount
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 排序 存储班级成绩

三种特殊数据类型

geospatital地理位置

朋友定位、附近的人、打车

Redis的Geo在redis3.2版本就推出了

只有六个命令

GEOADD
GEODIST
GEOHASH
GEOPOS
GEORADOUS
GEORADIUSBYMEMBER

geoadd添加位置

朋友的位置、附近的人

Redis的Go在redis3.2版本就推出!这个功能可以推算地理位置的信息,两地之间的距离

只有六个命令

geoadd添加地理位置

规则:两级无法添加,我们一般会下载城市的数据,直接通过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>
geopos获取当前定位
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> 
geodist两人之间的距离

单位:

  • m表示单位为米
  • km表示单位为其千米
  • mi表示单位为英里
  • ft表示单位为英尺
127.0.0.1:6379> geodist china:city beijin shanghai km 查看上海到北京直线距离
"1067.3788"
127.0.0.1:6379> 
georadius以给定的纬度为中心,找出一半径内的元素

我附近的人?通过半径来查询

所有数据都应该录入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> 
georadiusbymember:找出位于指定元素周围的其它元素
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>
geohash命令返回一个或多个位置元素的Geohash表示

表返回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> 

Hyperloglog
什么是基数?

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

如果不允许则不使用

Bitmaps 位存储

统计疫情感染人数:0 1 0 1 0

统计用户信息,活跃,不活跃!登录、未登录!打卡、未打卡!两个状态都可以使用bitmaps!

测试

使用bitmap记录周一到周日七天打卡 分为两种状况 0和1 表示 未打卡 和 打卡
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事务:
  • 开启事务(muiti)
  • 命令入队(命令)
  • 执行事务(exec)

锁: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> 

运行时异常(1/0),如果事务队列中存在语法性,那么执行命令的时候,其它命令式可以正常执行,错误命令会抛出异常
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

悲观锁:
  • 认为什么时候都出问题,无论干什么都会加锁!
乐观锁:
  • 很乐观,认为什么时候都不会出问题,所以不会上锁!更新数据的时候去判断一下,在此字段是否有人修改过数据
  • 更新的时候比较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(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> 

你可能感兴趣的:(redis,redis,nosql,数据库)