Redis的学习
1、Nosql概述
为什么要用Nosql
1、单击MySQL的年代
90年代,一个基本的网站访问量一般不会太大,单个数据库完全足够!
那个时候,更多的去使用静态网页Html ~服务器根本没有太大的压力!
思考一下,这种情况下:整个网站的瓶颈是什么?
1、数据量如果太大、一个机器放不下了!
2、数据的索引( B+ Tree ) , 一个机器内存也放不下
3、访问量(读写混合) ,一个服务器承受不了~ 只要你开始出现以上的三种情况之一, 那么你就必须要晋级
2、Memcached(缓存)+MySQL+垂直拆分(读写分离)
网站80%的情况都是在读,每次都要去查询数据库的话就十分的麻烦!所以说我们希望减轻数据的压力, 我们可以使用缓存来保证效率!
发展过程:优化数据结构和索引–>文件缓存(IO ) --> Memcached (当时最热门的技术!
3、分库分表+水平拆分+MySQL集群
本质:数据库(读,写) 早些年MyISAM:表锁,十分影响效率!高并发下就会出现严重的锁问题
转战Innodb :行锁
慢慢的就开始使用分库分表来解决写的压力! MySQL 在哪个年代推出了表分区!
4、如今最近的年代
2010-2020十年之间,世界已经发生了翻天覆地的变化; ( 定位,也是一种数据, 音乐,热榜! )
MySQL等关系型数据库就不够用了!数据量很多,变化很快~ !
MySQL有的使用它来存储一些比较大的文件,博客,图片!数据库表很大,效率就低了!如果有一数据库来专 门处理这种数据
MySQL压力就变得十分小(研究如何处理这些问题! ) 大数据的IO压力下,表几乎没法更大
为什么要用NoSQL
用户的个人信息,社交网络,地理位置。用户自己产生的数据,用户日志等等爆发式增长!
这时候我们就需要使用NoSQL数据库的, Nosql可以很好的处理以上的情况
什么是NoSQL
NoSQL = Not Only SQL(不仅仅是SQL)
泛指非关系型数据库的,随着web2.0互联网的诞生!传统的关系型数据库很难对付web2.0时代!尤其是超 大规模的高并发的社区!暴露出来很多难以克服的问题, NoSQL在当今大数据环境下发展的十分迅速, Redis是发展最快的,而且是我们当下必须要掌握的一个技术!
很多的数据类型用户的个人信息,社交网络,地理位置。这些数据类型的存储不需要一个固定的格式! 不需要多余的操作就可以横向扩展的! map 使用键值对来控制!
NoSQL特点
1、方便扩展(数据之间没有关系,很好扩展!)
2、大数据量高性能(Redis一秒写8万次,读取11万次,NoSQL的缓存记录级,是一种细粒度的缓存,性 能会比较高!)
3、数据类型是多样型的! (不需要事先设计数据库!随取随用!如果是数据量十分大的表,很多人就无法设计 了! )
RDBMS:关系型数据库
传统的RDBMS
-结构化组织
-SQL
-数据和关系都存在单独的表中
-数据操作,数据定义语言
-严格的一致性
-基础的事务
-。。。。。
NoSQL
-不仅仅是数据
-没有固定的查询语言
-键值对存储,列存储,文档存储,图形数据库(社交关系)
-最终一致性
-CAP定理和BASE (异地多活)
-高性能,高可用,高可扩
-。。。。。
了解:3V+3高
大数据时代的3V:主要是描述问题的
1、 海量 Volume
2、多样Variety
3、实时Velocity
大数据时代的3高:主要是对程序的要求
1、高并发
2、高可扩
3、高性能
真正在公司中的实践: NoSQL + RDBMS 一起使用才是最强的
NoSQL的四大分类
KV键值对:
- 新浪:Redis 美团:
- Redis+Tair 阿里、百度:
- Redis + memecache
文档型数据库(bson格式 和 json 一样):
- MongoDB (一般必须要掌握 )
- MongoDB是一个基于分布式文件存储的数据库, C++编写,主要用来处理大量的文档! MongoDB是一个介于关系型数据库和非关系型数据中中间的产品!
- MongoDB是非关系型数 据库中功能最丰富,最像关系型数据库的!
- ConthDB
列存储数据库
-
HBase
-
分布式文件系统
图关系数据库:
-
他不是存图形,放的是关系,比如:朋友圈社交网络,广告推荐!
-
Neo4jr InfoGrid ;
2、Redis入门
概述:
Redis ( Remote Dictionary Server ),即远程字典服务
是一个开源的使用 ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库 , 并提供多种语言的API。
redis 会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了 master-slave (主从)同步。
免费和开源!是当下最热门的NoSQL技术之一!也被人们称之为结构化数据库!
Redis 能干嘛
1、内存存储、持久化,内存中是断电即失、所以说持久化很重要( rdb、 aof )
2、效率高,可以用于高速缓存
3、发布订阅系统.
4、地图信息分析
5、计时器、计数器(浏览量! )
6、……
特性
1、多样的数据类型
2、持久化
3、集群
4、事务
……
3.测试性能(redis-benchmark)
redis-benchmark 是一个压力测试工具! 官方自带的性能测试工具! redis-benchmark 命令参数
简单测试
4.基础知识
**1.**redis默认有16个数据库
默认使用的是第0个
2.可以使用select进行切换
127.0.0.1:6379> select 3 #切换数据库
OK
127.0.0.1:6379[3]> dbsize #查看数据库大小
(integer) 0
127.0.0.1:6379[3]>
3.#查看所有的key keys*
4.清空当前数据库 flushdb
5.清空全部数据库的内容 FLUSHALL
Redis-Key
在redis中无论什么数据类型,在数据库中都是以key-value形式保存,通过进行对Redis-key的操作, 来完成对数据库中数据的操作。
- exists key :判断键是否存在
- del key :删除键值对
- move key db :将键值对移动到指定数据库
- expire key second :设置键值对的过期时间
- type key :查看value的数据类型
127.0.0.1:6379> keys * # 查看所有key
1) "name"
127.0.0.1:6379> set age 10 # set key
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> exists name # 判断当前的key是否存在
(integer) 1
127.0.0.1:6379> exists aga
(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 zhaotong
OK
127.0.0.1:6379> get name
"zhaotong"
127.0.0.1:6379> expire name 10 #设置key的过期时间,单位为秒
(integer) 1
127.0.0.1:6379> ttl name # 查看当前key的剩余时间
(integer) 5
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> type name #查看当前key的一个类型
none
127.0.0.1:6379> type age
string
5.Redis 是单线程的
明白Redis是很快的,官方表示, Redis是基于内存操作, CPU不是Redis性能瓶颈, Redis的瓶颈是根据机器 的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了!所有就使用了单线程了!
Redis是C语言写的,官方提供的数据为100000+ 的QPS ,完全不比同样是使用key-value的Memecache 差!
Redis为什么单线程还这么快
1、误区1 :高性能的服务器一定是多线程的?
2、误区2:多线程( CPU上下文会切换! ) 一定比单线程效率高!
先对CPU>内存>硬盘的速度要有所了解!
核心:
redis是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程( CPU 上下文会切换:耗时的操作! ! ! ) , 对于内存系统来说,如果没有上下文切换效率就是最高的!
6.五大数据类型
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间 件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合 (sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间 (geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting)驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通 过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
7.String(字符串)
########################################################
127.0.0.1:6379> set key1 zhao #设置值
OK
127.0.0.1:6379> get key1 #获取值
"zhao"
127.0.0.1:6379> keys * #获取所有的key
1) "age"
2) "key1"
127.0.0.1:6379> exists key1 #判断一个key是否存在
(integer) 1
127.0.0.1:6379> append key1 "tong" #追加字符串,如果当前key不存在,就相当于 set key
(integer) 8
127.0.0.1:6379> get key1
"zhaotong"
127.0.0.1:6379> strlen key1 #获取字符串长度
(integer) 8
127.0.0.1:6379> append key1 "zuiniubi"
(integer) 16
127.0.0.1:6379> strlen key1
(integer) 16
127.0.0.1:6379> get key1
"zhaotongzuiniubi"
########################################################
#实现自增或自减 i++
# i+= 步长
127.0.0.1:6379> set views 0 #初始浏览量为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
"2"
127.0.0.1:6379> decr views #自减 -1
(integer) 1
127.0.0.1:6379> decr views
(integer) 0
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) 9
127.0.0.1:6379> get views
"9"
127.0.0.1:6379> decrby views 5 #可以设置步长,指定减量
(integer) 4
########################################################
#字符串范围 range
127.0.0.1:6379> set key1 "hello,world"
OK
127.0.0.1:6379> get key1
"hello,world"
127.0.0.1:6379> getrange key1 0 4 #截取字符串[0,4]
"hello"
127.0.0.1:6379> getrange key1 0 -1 #获取全部字符串 和 get key是一样的
"hello,world"
#替换
127.0.0.1:6379> set key2 abcdef
OK
127.0.0.1:6379> get key2
"abcdef"
127.0.0.1:6379> setrange key2 1 xxxxxxxx
(integer) 9
127.0.0.1:6379> get key2
"axxxxxxxx"
########################################################
#setex (set with expire) #设置过期时间
#setnx (set if not exist) #不存在再设置 (在分布式锁中会常常使用!)
127.0.0.1:6379> setex key1 30 "zhao" #设置key1 的值为zhao ,30秒后过期
OK
127.0.0.1:6379> ttl key1
(integer) 25
127.0.0.1:6379> get key1
"zhao"
127.0.0.1:6379> setnx mykey "redis" #如果mykey 不存在,创建mykey
(integer) 1
127.0.0.1:6379> keys *
1) "mykey"
127.0.0.1:6379> ttl key1
(integer) -2
127.0.0.1:6379> setnx mykey "MongoDB" #如果mykey存在,创建失败
(integer) 0
127.0.0.1:6379> get mykey
"redis"
########################################################
mset
mget
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 #同时获取多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4 #msetnx 是一个原子性操作,要么一起成功,要么一起
失败!
(integer) 0
127.0.0.1:6379> get k4
(nil)
#对象
set user:1 {
name:zhaotong,age:21} #设置一个user:1 对象 值为 json字符来保存一个对象!
#这里的key是一个巧妙地设计: user:{id}:{filed} , 如此设计在redis中是完全OK
127.0.0.1:6379> set user:1 {
name:zhaotong,age:21}
OK
127.0.0.1:6379> get user:1
"{name:zhaotong,age:21}"
127.0.0.1:6379> mset user:2:name zhansan user:2:age 20
OK
127.0.0.1:6379> mget user:2:name user:2:age
1) "zhansan"
2) "20"
########################################################
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 mysql #如果存在值,获取原来地值,并设置新的值
"redis"
127.0.0.1:6379> get db
"mysql
String类似的使用场景:value除了是字符串还可以是数字,用途举例:
- 计数器
- 统计多单位的数量:uid:123666:follow 0
- 粉丝数
- 对象存储缓存
8.List(列表)
在redis里面,我们可以把list玩成 栈 、队列、阻塞队列!
所有的list命令都是用L开头的
###########################################################
127.0.0.1:6379> lpush list one #将一个值或者多个值,插入到列表的头部(左)
(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> lrange list 0 1 #通过区间获取具体的值
1) "three"
2) "two"
127.0.0.1:6379> Rpush list 4 #将一个值或者多个值,插入到列表的尾部(右)
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "4"
#LPOP RPOP
127.0.0.1:6379> lpop list #移除list的第一个元素
"three"
127.0.0.1:6379> rpop list #移除list的最后一个元素
"4"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
###########################################################
Lindex
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> lindex list 0 #通过下标获得list中的某一个值
"two"
127.0.0.1:6379> lindex list 1
"one"
###########################################################
Llen
127.0.0.1:6379> lrange list 0 -1
1) "three"
127.0.0.1:6379> lpush list one
(integer) 2
127.0.0.1:6379> lpush list two #返回列表的长度
integer) 3
127.0.0.1:6379> llen list
(integer) 3
###########################################################
lrem 移除指定的值
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "one"
3) "two"
4) "one"
127.0.0.1:6379> lrem list 1 two #移除list集合中指定个数的value,精确匹配
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "one"
3) "one"
127.0.0.1:6379> lrem list 2 one
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "three"
###########################################################
trim 修剪 :list 截断!
127.0.0.1:6379> rpush list1 "1111"
(integer) 1
127.0.0.1:6379> rpush list1 "2222"
(integer) 2
127.0.0.1:6379> rpush list1 3333
(integer) 3
127.0.0.1:6379> rpush list1 4444
(integer) 4
127.0.0.1:6379> lrange list1 0 -1
1) "1111"
2) "2222"
3) "3333"
4) "4444"
127.0.0.1:6379> ltrim list1 1 2 #通过下标截取指定的长度,这个list已经被改变了,截断了只
剩下截取的元素
OK
127.0.0.1:6379> lrange list1 0 -1
1) "2222"
###########################################################
rpoplpush #移除列表的最后一个元素,将他移动到新的列表中!
127.0.0.1:6379> lrange list1 0 -1
1) "2222"
2) "3333"
127.0.0.1:6379> rpoplpush list1 mylist #移除列表的最后一个元素,将它移动到新的列表中
"3333"
127.0.0.1:6379> lrange list 0 -1 #查看原来的列表
1) "4444"
127.0.0.1:6379> lrange mylist 0 -1 #查看目标列表,确实存在该值!
1) "3333"
###########################################################
lset 将列表中指定下标的值替换为另外一个值,更新操作
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> lset list 1 other #如果不存在,报错
(error) ERR index out of range
###########################################################
linsert #将某个具体的value插入到列表中某个元素的前面或者后面!.
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 "world" "other"
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "hello"
2) "other"
3) "world"
127.0.0.1:6379> linsert list after world 00
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "hello"
2) "other"
3) "world"
4) "00"
小结
- 他实际上是一个链表, before Node after ,left , right都可以插入值
- 如果key 不存在,创建新的链表
- 如果key存在,新增内容
- 如果移除了所有值,空链表,也代表不存在!
- 在两边插入或者改动值,效率最高!中间元素,相对来说效率会低一点
消息排队!消息队列( Lpush Rpop ),栈(Lpush Lpop)!
9.Set(集合)
#############################################
127.0.0.1:6379> sadd myset hello #set集合中添加元素
(integer) 1
127.0.0.1:6379> sadd myset zhao
(integer) 1
127.0.0.1: