Redis学习笔记

MySQL的演进

  • 单机MySQL
  • Memocached(缓存)+MySQL+垂直拆分
  • 分库分表+水平拆分+MySQL集群
  • NoSQL

NoSQL(Not Only SQL)不仅仅是数据库,非关系型数据库

  • 传统的RDBMS

结构化组织

SQL语言

数据和关系都存储在单独的表中

严格的一致性

基础的事务

  • NoSQL

不仅仅是数据

没有固定的查询语言

键值对存储、列存储、文档存储、图形数据库

最终一致性

CAP定理、BASE理论(异地多活)

高性能、高可用、高可扩

  • 大数据时代:3V+3高

3V:描述问题

1、海量Volume

2、多样Variety

3、实时Velocity

3高:对程序的要求

1、高并发

2、高可扩

3、高性能

NoSQL的四大分类

kv键值对:

  • 新浪:Redis
  • 美团:Redis+Tair
  • 阿里、百度:Redis+memocache

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

  • MongoDB

    • 基于分布式文件存储的数据库,由c++编写,主要用来处理大量的文档
    • 介于关系型数据库和非关系型数据库的中间产品,是非关系型数据库中最丰富、最像关系型数据库的
  • ConthDB

列存储数据库:

  • HBase
  • 分布式文件系统

图形关系数据库:

  • 不存放图形,存放关系
  • Neo4j、InfoGrid

Redis

概述

Redis(Remote Dictionary Server),远程字典服务

开源的、ANCI C语言编写、支持网络、可基于内存亦可持久化的日志型、key-Value数据库,并提供多种语言的API,也被称为结构化数据库

Redis作用

1、 内存存储、持久化(rdb、aof)

2、 效率高,可以用于高速缓存

3、 发布订阅系统

4、 地图信息分析

5、 计时器、计数器

特性

1、 多样化的数据类型

2、 持久化

3、 集群

4、 事务

安装

Windows环境下Redis很小,默认端口号6379

测试连接:ping命令,返回PONG

但是Redis推荐使用Linux去开发

基础知识

Redis默认有16个数据库,默认使用的是第0个数据库,可以用select进行切换

  • 数据库切换
连接1:0>select 1
"OK"
  • set与get
连接1:1>set name zhangsan
"OK"
连接1:1>get name
"zhangsan"
  • 数据库大小
连接1:1>dbsize
"1"
  • 查看当前数据库所有的key
连接1:1>keys *
 1)  "name"
  • 清空命令
连接1:1>flushdb  #清空当前数据库
"OK"
连接1:1>dbsize
"0"
连接1:1>flushall #清空所有数据库
"OK"
  • 定时过期
连接1:0>exists name
"1"
连接1:0>expire name 20 #定时20秒
"1"
连接1:0>ttl name #查看剩余时间
"17"
连接1:0>ttl name
"12"
连接1:0>ttl name
"11"
连接1:0>ttl name
"10"
连接1:0>ttl name
"5"
连接1:0>ttl name
"1"
连接1:0>ttl name
"-2"
连接1:0>ttl name
"-2"
连接1:0>get name
null
  • 查看key的类型
连接1:0>type name
"string"
连接1:0>type age
"string"
  • 判断键是否存在
连接1:0>exists name #存在
"1"
连接1:0>exists name1 #不存在
"0"

Redis是单线程的

Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis瓶颈是机器的内存和网络带宽

Redis将所有数据全部放在内存中,所以说使用单线程去操作效率就是最高的,多线程需要CPU上下文切换(耗时),对于内存系统来说如果没有上下文切换效率就是最高的。多次读写都是在一个CPU上,在内存情况下,这个就是最佳方案。

五大基本数据类型

String

基础字符串类型的操作

连接1:0>flushdb
"OK"
连接1:0>set key1 v1 #设置值
"OK"
连接1:0>get key1 #获得值
"v1"
连接1:0>append key1 hello #添加字符串值
"7"
连接1:0>get key1
"v1hello"
连接1:0>strlen key1 #获取长度
"7"
连接1:0>append key1 "hello"
"12"
连接1:0>get key1
"v1hellohello"
连接1:0>append key1 \"hello\" #转义字符
"19"
连接1:0>get key1
"v1hellohello"hello""

自增incr、自减decr

连接1:0>set views 0
"OK"
连接1:0>get views
"0"
连接1:0>incr views #自增1
"1"
连接1:0>incr views
"2"
连接1:0>incr views
"3"
连接1:0>decr views #自减1
"2"
连接1:0>incrby views 10 #固定步长增
"12"
连接1:0>decrby views 7 #固定步长减
"5"

查看字符串

连接1:0>set key1 hello,hello,everyone
"OK"
连接1:0>get key1
"hello,hello,everyone"
连接1:0>getrange key1 0 5 #查看前五个字符
"hello,"
连接1:0>getrange key1 0 -1 #查看全部字符
"hello,hello,everyone"

替换字符串

连接1:0>set key1 abcdefg
"OK"
连接1:0>get key1
"abcdefg"
连接1:0>setrange key1 2 xx  #替换b后面两个字符为xx
"7"
连接1:0>get key1
"abxxefg"

setex与setnx

在分布式锁中常用

连接1:0>setex key1 30 hello	#设置key1,30秒过期
"OK"
连接1:0>ttl key1
"23"
连接1:0>setnx key2 myhello	#如果不存在键key2,那么就给值myhello
"1"		#赋值成功
连接1:0>ttl key1   
"-2"		#key1已过期
连接1:0>setnx key2 my	#如果不存在键key2,那么就给值my
"0"		#赋值失败
连接1:0>get key2
"myhello"

批量set、get

连接1:0>mset key1 v1 key2 v2 key3 v3	#批量set
"OK"
连接1:0>keys *
 1)  "key3"
 2)  "key1"
 3)  "key2"
连接1:0>mget key1 key2 key3	#批量get
 1)  "v1"
 2)  "v2"
 3)  "v3"

批量setnx

即msetnx

连接1:0>msetnx key1 v1 key4 v4	#msetnx是一个原子性操作,要么一起成功,要么一起失败
"0"

上面的代码表示如果key1和key4不存在,就分别赋值v1和v4。但是v1存在,v4不存在,由于事务的原子性,导致两个赋值都失败。

对象操作

连接1:0>mset user:1:name zhangsan user:1:age 2
"OK"
连接1:0>mget user:1:name user:1:age
 1)  "zhangsan"
 2)  "2"
 
#或者用简单点的,以json来保存值
连接1:0>set user:1 {name:zhangsan,age:3}
"OK"
连接1:0>get user:1
"{name:zhangsan,age:3}"

getset

连接1:0>getset key1 123	#先get再set,如果没有则get空值
null
连接1:0>getset key1 456
"123"
连接1:0>getset key1 789
"456"
连接1:0>get key1
"789"

List

类似于一个双端队列,两端都可以插值

插入值

连接1:0>lpush list1 one	#左添加元素
"1"
连接1:0>lpush list1 two
"2"
连接1:0>lpush list1 three
"3"
连接1:0>lrange list1 0 -1	#get所有元素
 1)  "three"
 2)  "two"
 3)  "one"
连接1:0>lrange list1 0 1
 1)  "three"
 2)  "two"
连接1:0>rpush list1 right	#右添加元素
"4"
连接1:0>lrange list1 0 -1	#右添加的元素放在栈底
 1)  "three"
 2)  "two"
 3)  "one"
 4)  "right"

注意:list的get方法是倒叙输出,即先进后出,类似栈

移除值

移除列表的第一个元素

连接1:0>lrange list1 0 -1
 1)  "three"
 2)  "two"
 3)  "one"
 4)  "right"
连接1:0>lpop list1	#从左抛
"three"
连接1:0>lrange list1 0 -1
 1)  "two"
 2)  "one"
 3)  "right"
连接1:0>rpop list1	#从右抛
"right"
连接1:0>lrange list1 0 -1
 1)  "two"
 2)  "one"

取值

连接1:0>lrange list1 0 -1	#存放有4个元素
 1)  "four"
 2)  "three"
 3)  "two"
 4)  "one"
连接1:0>lindex list1 2	#根据下标去取值
"two"
连接1:0>lindex list1 0
"four"

返回列表长度

连接1:0>llen list1
"4"

移除list集合中指定个数的某value

连接1:0>lrem list1 1 one	#移除1个“one”
"1"
连接1:0>lrange list1 0 -1		
 1)  "four"
 2)  "three"
 3)  "two"

再比如

连接1:0>lrange list1 0 -1	#现在有2两“four”
 1)  "four"
 2)  "four"
 3)  "three"
 4)  "two"
 5)  "one"
连接1:0>lrem list1 2 four	#移除2个“four”
"2"
连接1:0>lrange list1 0 -1
 1)  "three"
 2)  "two"
 3)  "one"

截取

截取时,只保留截取到的数据,其他的数据会被push

连接1:0>lrange list1 0 -1
 1)  "five"
 2)  "four"
 3)  "three"
 4)  "two"
 5)  "one"
连接1:0>ltrim list1 1 2	#截取下标1、2
"OK"
连接1:0>lrange list1 0 -1
 1)  "four"
 2)  "three"

rpoplpush

移除列表的最后一个元素,并将最后一个元素lpush到另一个list中

连接1:0>lrange list1 0 -1
 1)  "five"
 2)  "four"
 3)  "three"
 4)  "two"
 5)  "one"
连接1:0>rpoplpush list1 list2	#rpoplpush
"one"
连接1:0>lrange list1 0 -1	#list1中没有“one”元素了
 1)  "five"
 2)  "four"
 3)  "three"
 4)  "two"
连接1:0>lrange list2 0 -1	#list2中增加了“one”元素
 1)  "one"

lset

将列表中指定下标的值替换为另外一个值,更新操作

连接1:0>lrange list1 0 -1
 1)  "five"
 2)  "four"
 3)  "three"
 4)  "two"
 5)  "one"
连接1:0>lset list1 0 fivefive	#指定list1中的第0个元素改为“fivefive”
"OK"
连接1:0>lrange list1 0 -1
 1)  "fivefive"
 2)  "four"
 3)  "three"
 4)  "two"
 5)  "one"

当list中没有值时是无法做lset的

指定位置插入linsert

连接1:0>lrange list1 0 -1
 1)  "four"
 2)  "three"
 3)  "two"
 4)  "one"
连接1:0>linsert list1 before two hello	#在“two”前面插入“hello”
"5"
连接1:0>lrange list1 0 -1
 1)  "four"
 2)  "three"
 3)  "hello"
 4)  "two"
 5)  "one"
连接1:0>linsert list1 after two welcome	#在“two”后面插入“welcome”
"6"
连接1:0>lrange list1 0 -1
 1)  "four"
 2)  "three"
 3)  "hello"
 4)  "two"
 5)  "welcome"
 6)  "one"

list小结

  • list实际上是一个链表,left和right都可以执行push和pop操作

  • 如果key不存在,会创建新的链表

  • 如果移除了所有值,产生了空链表,则key会被删除

  • 在两边插入或者改动值,效率最高;对于中间元素的操作,相对来说效率会低一点

  • 队列(Lpush+Rpop)、栈(Lpush+Lpop)

set

set中的值不能重复!无序不重复集合

添加sadd、读取smembers

连接1:0>sadd set1 one
"1"
连接1:0>sadd set1 two
"1"
连接1:0>sadd set1 three
"1"
连接1:0>sadd set1 four
"1"
连接1:0>smembers set1
 1)  "four"
 2)  "three"
 3)  "two"
 4)  "one"

判断是否存在

连接1:0>smembers set1
 1)  "four"
 2)  "three"
 3)  "two"
 4)  "one"
连接1:0>sismember set1 one	#判断set1中是否存在“one”
"1"		#存在
连接1:0>sismember set1 zero	#判断set1中是否存在“zero”
"0"		#不存在

元素个数

连接1:0>smembers set1
 1)  "four"
 2)  "one"
 3)  "two"
 4)  "three"
连接1:0>scard set1	#获取set集合中元素个数
"4"

移除指定元素

连接1:0>smembers set1
 1)  "four"
 2)  "one"
 3)  "two"
 4)  "three"
连接1:0>srem set1 two		#移除元素“two”
"1"
连接1:0>smembers set1
 1)  "four"
 2)  "one"
 3)  "three"

随机抽取元素

连接1:0>smembers set1
 1)  "four"
 2)  "three"
 3)  "two"
 4)  "one"
连接1:0>srandmember set1	#随机抽取一个元素
"four"
连接1:0>srandmember set1	#随机抽取一个元素
"three"
连接1:0>srandmember set1	#随机抽取一个元素
"three"
连接1:0>srandmember set1	#随机抽取一个元素
"one"
连接1:0>srandmember set1 2	#随机抽取两个元素
 1)  "two"
 2)  "three"

随机移除元素

连接1:0>smembers set1
 1)  "four"
 2)  "three"
 3)  "two"
 4)  "one"
连接1:0>spop set1		#随机移除一个元素
"two"
连接1:0>smembers set1
 1)  "four"
 2)  "three"
 3)  "one"
连接1:0>smembers set1
 1)  "four"
 2)  "three"
 3)  "two"
 4)  "one"
连接1:0>spop set1 2	#随机移除两个元素
 1)  "one"
 2)  "three"
连接1:0>smembers set1
 1)  "four"
 2)  "two"

移动元素到另一个set

连接1:0>smembers set1
 1)  "four"
 2)  "three"
 3)  "two"
 4)  "one"
连接1:0>smove set1 set2 two	#将“two”从set1移动到set2
"1"
连接1:0>smembers set1
 1)  "four"
 2)  "three"
 3)  "one"
连接1:0>smembers set2		#即使set2不存在也能移动过去
 1)  "two"

set的交、并、差

连接1:0>smembers set1
 1)  "b"
 2)  "c"
 3)  "a"
连接1:0>smembers set2
 1)  "e"
 2)  "d"
 3)  "c"
连接1:0>sdiff set1 set2	#差集,即set1中有但是set2没有的元素
 1)  "b"
 2)  "a"
连接1:0>sinter set1 set2	#交集
 1)  "c"
连接1:0>sunion set1 set2	#并集
 1)  "a"
 2)  "c"
 3)  "d"
 4)  "b"
 5)  "e"

Hash

类似于key-Map类型,即key-<key-map>

添加、获取、删除

连接1:0>hset hash1 k1 v1	#添加
"1"
连接1:0>hset hash1 k2 v2
"1"
连接1:0>hset hash1 k3 v3
"1"
连接1:0>hget hash1 k1	#获取
"v1"
连接1:0>hmset hash1 k1 one k2 two	#批量添加会覆盖原先的值
"OK"
连接1:0>hget hash1 k1
"one"
连接1:0>hget hash1 k2
"two"
连接1:0>hset hash1 k1 vv1	#普通重复添加则不会执行
"0"
连接1:0>hmget hash1 k1 k2 k3	#批量获取值
 1)  "one"
 2)  "two"
 3)  "v3"
连接1:0>hgetall hash1	#获取所有
 1)  "k1"
 2)  "one"
 3)  "k2"
 4)  "two"
 5)  "k3"
 6)  "v3"
连接1:0>hdel hash1 k1	#删除指定的key
"1"
连接1:0>hgetall hash1
 1)  "k2"
 2)  "two"
 3)  "k3"
 4)  "v3"

hash本质和String没有太大的区别,是一个简单的key-value

获取长度

连接1:0>hmget hash1 k1 k2 k3 k4
 1)  "one"
 2)  "two"
 3)  "three"
 4)  "four"
连接1:0>hlen hash1	#获取长度
"4"

判断是否存在

连接1:0>hmget hash1 k1 k2 k3 k4
 1)  "one"
 2)  "two"
 3)  "three"
 4)  "four"
连接1:0>hexists hash1 k1	#k1存在
"1"
连接1:0>hexists hash1 key1	#key1不存在
"0"

获取key与value

连接1:0>hmget hash1 k1 k2 k3 k4
 1)  "one"
 2)  "two"
 3)  "three"
 4)  "four"
连接1:0>hkeys hash1	#获取所有的key
 1)  "k1"
 2)  "k2"
 3)  "k3"
 4)  "k4"
连接1:0>hvals hash1	#获取所有values
 1)  "one"
 2)  "two"
 3)  "three"
 4)  "four"

自增、自减

连接1:0>hset hash1 k1 2	#添加<k1-2>
"1"
连接1:0>hincrby hash1 k1 1	#增加1,<k1-3>
"3"
连接1:0>hincrby hash1 k1 3	#增加3,<k1-6>
"6"
连接1:0>hincrby hash1 k1 -2	#增加-2,相当于减2
"4"

Hash中只有hincrby字段,没有hdecrby字段,因此减少就用hincrby+负值

Zset

有序集合,在set的基础上增加了一个值

set:<k1,v1> ——> zset:<k1,score,v1>

添加、获取

连接1:0>zadd myset 1 one	#单个添加
"1"
连接1:0>zadd myset 2 two
"1"
连接1:0>zadd myset 3 three
"1"
连接1:0>zadd myset 4 four 5 five	#批量添加
"2"
连接1:0>zrange myset 0 -1		#获取值
 1)  "one"
 2)  "two"
 3)  "three"
 4)  "four"
 5)  "five"
连接1:0>zrangebyscore myset 1 5	#获取1~5的值
 1)  "one"
 2)  "two"
 3)  "three"
 4)  "four"
 5)  "five"

排序的实现

连接1:0>zadd salary 1000 lisi
"1"
连接1:0>zadd salary 500 zhangsan
"1"
连接1:0>zadd salary 800 xiaoming
"1"
连接1:0>zrangebyscore salary 0 1000	#在0-1000之间对salary排序,默认升序
 1)  "zhangsan"
 2)  "xiaoming"
 3)  "lisi"
连接1:0>zrevrange myset 0 -1	#降序排列
 1)  "lisi"
 2)  "xiaoming"
 3)  "zhangsan"
连接1:0>zrevrangebyscore myset  4 1	#降序排列
 1)  "lisi"
 2)  "xiaoming"
 3)  "zhangsan"
连接1:0>zrangebyscore salary -inf +inf	#在负无穷与正无穷间排序
 1)  "zhangsan"
 2)  "xiaoming"
 3)  "lisi"
连接1:0>zrangebyscore salary 200 900
 1)  "zhangsan"
 2)  "xiaoming"
连接1:0>zrangebyscore salary 0 1000 withscores	#可以加上参数
 1)  "zhangsan"
 2)  "500"
 3)  "xiaoming"
 4)  "800"
 5)  "lisi"
 6)  "1000"

移除元素

连接1:0>zrange myset 0 -1
 1)  "one"
 2)  "two"
 3)  "three"
 4)  "four"
连接1:0>zrem myset three	#移除元素“three”
"1"
连接1:0>zrange myset 0 -1
 1)  "one"
 2)  "two"
 3)  "four"

元素个数

连接1:0>zrange myset 0 -1
 1)  "one"
 2)  "two"
 3)  "three"
 4)  "four"
连接1:0>zcard myset	#获取zset的元素个数
"4"
连接1:0>zcount myset 1 3	#区间计数(闭区间)
"3"

三种特殊数据类型

geospatial(地理空间)

geospatial用于存储地理位置,常用于地图。

geo的底层其实是Zset

geoadd

添加经纬坐标。

写经纬度时顺序不能乱,经度在前,纬度在后,而且写反了会报错

连接1:0>geoadd china:city 116.40 39.90 beijing	#经纬度必须是float类型
"1"
连接1:0>geoadd china:city 121.47 31.23 shanghai
"1"
连接1:0>geoadd china:city 106.50 29.53 chongqin
"1"
连接1:0>geoadd china:city 114.05 22.54 shenzhen
"1"
连接1:0>geoadd china:city 120.16 30.24 hangzhou
"1"

geopos

读取坐标

连接1:0>geopos china:city beijing
 1)    1)   "116.39999896287918091"
  	   2)   "39.90000009167092543"

连接1:0>geopos china:city shanghai
 1)    1)   "121.47000163793563843"
       2)   "31.22999903975783553"

由于数据时float类型,所以存在精度问题

geodist

返回两个给定位置之间的距离

连接1:0>geodist china:city beijing shanghai	#默认单位是m
"1067378.7564"
连接1:0>geodist china:city beijing shanghai km	#单位换成km
"1067.3788"

单位:

  • m
  • km
  • mi 英里
  • ft 英尺

georadius

以给定的经纬度为中心,找出某一半径内的元素

#以(110,30)为中心,半径800km内的城市
连接1:0>georadius china:city 110 30 800 km
 1)  "chongqin"
连接1:0>georadius china:city 110 30 800 km withdist	#加上距离
 1)    1)   "chongqin"
       2)   "341.9374"
连接1:0>georadius china:city 110 30 800 km withcoord	#加上经纬度
 1)    1)   "chongqin"
 2)    1)    "106.49999767541885376"
       2)    "29.52999957900659211"
连接1:0>georadius china:city 110 30 800 km withcoord withdist
 1)    1)   "chongqin"
 2)   "341.9374"
 3)    1)    "106.49999767541885376"
       2)    "29.52999957900659211"
       
#指定数量,例如输出3个(如果数量不足,则输出仅有的)     
连接1:0>georadius china:city 110 30 1800 km withcoord withdist count 3
 1)    1)   "chongqin"
       2)   "341.9374"
       3)    1)    "106.49999767541885376"
             2)    "29.52999957900659211"
 2)    1)   "shenzhen"
       2)   "922.6257"
       3)    1)    "114.04999762773513794"
             2)    "22.53999903789756587"
 3)    1)   "hangzhou"
       2)   "977.5143"
       3)    1)    "120.1600000262260437"
             2)    "30.2400003229490224"

georadiusbymember

找出位于指定范围内的元素,中心点是由给定位置元素决定

#查询杭州1000km以内的城市
连接1:0>georadiusbymember china:city hangzhou 1000 km
 1)  "hangzhou"
 2)  "shanghai"

geohash

将二维经纬度返回为11个字符的字符串,如果两个字符串长得越像则两地越接近

连接1:0>geohash china:city beijing hangzhou
 1)  "wx4fbxxfke0"
 2)  "wtmkn31bfb0"

补充

由于geo的底层是Zset,我们也可以操作Zset来操作geo

#例如用zrange来显示
连接1:0>zrange china:city 0 -1
 1)  "chongqin"
 2)  "shenzhen"
 3)  "hangzhou"
 4)  "shanghai"
 5)  "beijing"
连接1:0>zrem china:city hangzhou	#移除某元素
"1"
连接1:0>zrange china:city 0 -1
 1)  "chongqin"
 2)  "shenzhen"
 3)  "shanghai"
 4)  "beijing"

Hyperloglog

基数统计的算法

连接1:0>pfadd mykey a b c d e f g h	#添加元素
"1"
连接1:0>pfcount mykey	#统计基数
"8"
连接1:0>pfadd yourkey g h i j k
"1"
连接1:0>pfcount yourkey
"5"
连接1:0>pfmerge key mykey yourkey	#将两个集合合并,放入一个新集合
"OK"
连接1:0>pfcount key	#重复的值不计入新集合
"11"

其优点是占用内存小,2^64个元素只占用12KB内存,有0.81%的错误率

如果允许容错,又对内存有需求,那可以使用Hyperloglog,例如计算访客人数

如果没有容错,可以使用set

Bitmaps

位存储,适用于只有两个状态的情况(true/false)

#例如对周一到周五进行打卡记录,1表示打卡,0表示未打卡
连接1:0>setbit sign 1 1	#输入打卡情况
"0"
连接1:0>setbit sign 2 1
"0"
连接1:0>setbit sign 3 0
"0"
连接1:0>setbit sign 4 1
"0"
连接1:0>setbit sign 5 0
"0"
连接1:0>getbit sign 3		#查看打卡情况
"0"
连接1:0>getbit sign 4
"1"
连接1:0>bitcount sign		#统计5天一共打卡的天数
"3"
连接1:0>bitcount sign 0 -1	#或者加上范围也行
"3"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值