NoSql之Redis详细笔记

Posted tea_year

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NoSql之Redis详细笔记相关的知识,希望对你有一定的参考价值。

所有牛逼的人都有一段苦逼的岁月,但是你只要像傻逼一样的去坚持,终将牛逼

NoSQL

NoSQL

什么是NoSQl

NoSQL

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

关系型数据库:表格,行,列

反之非关系型数据库的,随着web2.0互联网的诞生!传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社区!暴露出来很多难以克服的问题,NoSQL在当今大数据环境下发展的十分迅速,Redis是发展最快的,而且是我们当下必须掌握的一个技术

为什么要用NoSQL

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

这时候我们就需要使用NoSQL数据库,NoSQL可以很好地处理以上的情况!

很多的数据类型用户的个人信息,社交网络,地理位置。这些数据类型的存储不需要一个固定的格式!不需要过多的操作就可以横向扩展的!Map<String,Object>使用键值对来控制!

NoSQL特点

  1. 方便扩展(数据之间没有关系,很好扩展!)
  2. 大数据量高性能(Redis一秒写8万次,读取11万次,NoSQL的缓存记录级,是一种细粒度的缓存,性能较高)
  3. 数据类型是多样型的!(不需要事先设计数据库!随取随用!如果是数据库量十分大的一些表,很多人就无法设计了)
  4. 传统的 RDBMS 和 NoSQL
传统的RDBMS
- 结构化阻止
- SQL
- 数据可关系都存在单独的表中
- 操作语言,数据定义语言
- 严格的一致性(ACID原则)
- 基础的事务
- ……
NoSQL
- 不仅仅是数据
- 没有固定的查询语言
- 键值对存储,列存储,文档存储,图形数据库(社交关系)
- 最终一致性
- CAP定理 和BASE 理论 (异地多活!)初级架构师
- 高性能,高可用,高扩展
- ……

了解: 3V + 3高

大数据时代的3V:主要是描述问题的

  1. 海量Volume
  2. 多样Variety
  3. 实时Velocity

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

  1. 高并发
  2. 高可拓(随时水平拆分,机器不够了,可以扩展机器来结局)
  3. 高性能(保证用户体验和性能!)

真正在公司中的实践:NoSQL + RDBMS 一起使用才是最强的,阿里巴巴架构演进!

阿里巴巴演进分析

开源才是技术的王道!

任何一家互联网的公司,都不可能只是简简单单让用户能用就好了

大量公司做的都是相同的业务;(竞品协议)

随着这样的竞争,业务是越来越完善,然后对开发者的要求也是越来越高!

高中,大一开始就应该是认真的学习了!

如果你未来相当一个架构师:没有什么是加一层解决不了的

# 1、商品的基本信息
    名称、价格、商家信息;
    关系型数据库就可以解决了!mysql / Oracle (淘宝早年就去IOE了!-王坚:推荐文章:阿里云的这群疯子)
    淘宝内部的MySQL 不是大家用的 MySQL
    
# 2、商品的描述、评论(文字较多)
	文档型数据库中,MongoDB
	
# 3、图片
    分布式文件系统 FastDFS
    - 淘宝自己的 TFS
    - Google的 GFS
    - Hadoop HDFS
    - 阿里云的 OSS
    
# 4、商品的关键字(搜索)
	- 搜索引擎 solr elasticsearch
	- ISerach:多隆

# 5、商品热门的波段信息(秒杀)
	- 内存数据库
	- Redis Tair Memacahe
	
# 6、商品的交易,外部的支付接口
	- 三方应用
	

要知道,一个简单的网页背后的技术一定不是大家所想的那么简单!

大型互联网应用问题:

  • 数据类型过多!
  • 数据源繁多,经常重构!
  • 数据要改造,就需要大面积改造

数据层解决方案:

数据层热点缓存设计:


这以上都是NoSQL入门概述,不仅能够提高大家的知识,还可以帮助大家了解大厂的工作内容!


NoSQL的四大分类

KV键值对:

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

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

  • MongoDB(一般必须要掌握)
    • MongoDB是一个基于分布式文件存储的数据库,C++编写的,主要用来处理大量的文档!
    • MongoDB是一个介于关系型数据库和非关系型数据库中中间的产品!MongoDB是非关系型数据库中功能最丰富,最像关系型数据库的!
  • ConthDB(不做了解)

列存储数据库:

  • HBase
  • 分布式文件系统

图关系数据库:

  • 他不是存图形,放的是关系,比如:朋友圈社交网络,广告推荐!
  • Neo4j,InfoGrid;

四者对比!

Redis入门

概述

Redis 是什么?

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

是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

免费和开源!是当下最热门的NoSQL技术之一!也被人们称之为结构化数据库!

Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

Redis 能干什么?

  1. 内存存储、持久化,内存是断电即失,所以说持久化很重要(RDB、AOF)
  2. 效率高,可以用于高速缓存
  3. 发布订阅系统(简单的消息队列)
  4. 地图信息分析
  5. 计时器、计数器(浏览量!)
  6. ……

特性

  1. 多样的数据类型
  2. 持久化
  3. 集群
  4. 事务

学习中需要用到的东西

  1. 官网:https://redis.io
  2. 中文网:https://redis.cn
  3. 下载地址:

注意:Windows在GitHub上下载(但官方不推荐,支持Linux下载)

Windows安装

  1. 下载地址:https://github.com/MicrosoftArchive/redis/releases
  2. 解压到本地环境!
  3. 开启Redis,双击运行服务即可
  4. 使用Redis客户端来连接Redis
  5. 使用Redis客户端来连接Redis

Windows下使用确实简单,但是Redis推荐我们使用Linux去进行开发

Linux安装

  1. 下载安装包 redis-5.0.8.tar.gz

  2. 解压Redis的安装包

  3. 进入解压后的文件,可以看到我们Redis的配置文件

  4. 基本的环境安装

    yum install gcc-c++
    make
    

  5. Redis的默认安装路径 usr/local/bin

  6. 将Redis配置文件复制到我们当前目录下

  7. Redis默认不是后台启动的

  8. 启动Redis服务

  9. 使用redis-cli进行连接测试

  10. 查看Redis的进程是否开启

  11. 如何关闭Redis服务?

  12. 再次查看进程是否存在

  13. 后面我们会使用单机多Redis启动集群测试

测试性能

redis-benchmark是一个压力测试工具!

官方自带的性能测试工具!

redis-benchmark 命令参数

我们来简单测试下

# 测试: 100个并发连接 100000 请求
	redis-benchmark -h localhost -p 6379 -c 100 -n 100000

测试结果:

查看分析

基础的知识

Redis默认有16个数据库

默认使用第0个

可以使用select进行数据库切换

127.0.0.1:6379> select 3	# 切换数据库
OK
127.0.0.1:6379[3]> dbsize	# 查看数据库大小
(integer) 0

127.0.0.1:6379[3]> keys *	# 查看数据库所有的key
1) "name"

清除当前数据库 flushdb

清空全部数据库的内容 flushall

127.0.0.1:6379[3]> flushdb	
OK
127.0.0.1:6379[3]> keys *
(empty list or set)

Redis是单线程的

明白Redis是很快的,官方表示,Redis是基于内存操作的,CPU不是Redis性能瓶颈,Redis的瓶颈是根据及其的带宽,既然可以使用单线程来实现,就是用单线程了!所以就使用了单线程

Redis是C语言写的,官方提供的数据为 10W+ 的QPS,完全不必同样是使用key-value的Memecache差!

Redis为什么单线程还这么块?

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

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

    线程速度:CPU > 内存 > 硬盘

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

五大数据类型

官方文档

全段翻译:

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库缓存消息中间件。 它支持多种类型的数据结构,如 字符串(strings)散列(hashes)列表(lists)集合(sets)有序集合(sorted sets) 与范围查询, bitmapshyperloglogs地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication)LUA脚本(Lua scripting)LRU驱动事件(LRU eviction)事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

所有命令一定要全部记住,后面使用SpringBoot,Jedis,所有的方法就是这些命令

Redis-Key

127.0.0.1:6379> keys *	# 查看所有的key
(empty list or set)
127.0.0.1:6379> set name zhuangb1	# set key
OK
127.0.0.1:6379> set age 1
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 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 zhuangb1
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> get name
"zhuangb1"
127.0.0.1:6379> 
127.0.0.1:6379> expire name 10	# 设置key的过期时间---单点登录
(integer) 1
127.0.0.1:6379> ttl name	# 查看当前key的剩余时间
(integer) 3
127.0.0.1:6379> ttl name
(integer) -2
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的类型
string
127.0.0.1:6379> type age
string

后面如果遇到不会的命令,可以在官网查看帮助文档

String

90% 的Java程序员使用Redis只会使用一个String类型!

#######################################################################
127.0.0.1:6379> set key1 v1	# 设置值
OK
127.0.0.1:6379> get key1	# 获得值
"v1"
127.0.0.1:6379> keys *		# 获取所有的可以
1) "key1"
127.0.0.1:6379> exists key1		# 查看某一个key是否存在
(integer) 1
127.0.0.1:6379> append key1 "hello"	# 追加字符串 如果当前key不存在,就相当于set key
(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 ",zhuangb1"
(integer) 16
127.0.0.1:6379> get key1
"v1hello,zhuangb1"

#######################################################################
# 步长 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> incrby views 10
(integer) 19
127.0.0.1:6379> decrby views 5	# 设置步长 指定减量
(integer) 14

#######################################################################
# 字符串 range
127.0.0.1:6379> set key1 "hello,zhuangb1"	# 设置key1的值
OK
127.0.0.1:6379> get key1
"hello,zhuangb1"
127.0.0.1:6379> getrange key1 0 3	# 截取字符串(substring)
"hell"
127.0.0.1:6379> getrange key1 0 -1	# 获取全部的字符串
"hello,zhuangb1"

# 替换
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"


#######################################################################
# setex(set with expire)	# 设置过期时间 
# setnx(set if not exists)	# 不存在再设置(分布式锁中会常用)

127.0.0.1:6379> setex key3 30 "hello"	# 设置key3 的值为hello,30秒后过期
OK
127.0.0.1:6379> ttl key3
(integer) 26
127.0.0.1:6379> get key3
"hello"
127.0.0.1:6379> setnx mykey redis	# 如果不存在,创建mykey
(integer) 1
127.0.0.1:6379> keys *
1) "key2"
2) "key1"
3) "mykey"
127.0.0.1:6379> ttl key3
(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) "k2"
3) "k1"
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: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"

#######################################################################
getset # 先get然后再set

127.0.0.1:6379> getset db redis	# 如果不存在值,则返回null
(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开头的

Redis命令不区分大小写

#######################################################################
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> lrange list 0 0
1) "three"
127.0.0.1:6379> rpush list right	# 将一个或多个值插入到列表的尾部
(integer) 4
127.0.0.1:6379> lrange list 0 -1	
1) "three"
2) "two"
3) "one"
4) "right"

#######################################################################
# lpop
# rpop

127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> lpop list	# 移除list的第一个元素
"three"
127.0.0.1:6379> rpop list	# 移除list的最后一个元素
"right"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"

#######################################################################
# lindex	通过下标获取 list 中的某一个值
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> lindex list 1
"one"
127.0.0.1:6379> lindex list 0
"two"

#######################################################################
# llen    返回指定列表的长度
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> llen list
(integer) 3

#######################################################################
# 移除指定的值	取关
# lrem

127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> lrem list 1 one	# 移除list集合中指定个数的value
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "two"
127.0.0.1:6379> lrem list 1 three
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrem list 2 three
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "two"
127.0.0.1:6379> 

#######################################################################
# trim去除两边空格 list截断
127.0.0.1:6379> keys *
(empty list or set)
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> lrange myotherlist 0 -1	# 查看目标列表,确实存在该值
1) "hello2"

#######################################################################
# lset 将列表中指定下标的值替换为另外一个值(update)
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 ohter	# 如果不存在则会报错
(error) ERR index out of range

#######################################################################
# 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"




小结

  • 它实际上是一个链表,before Node after,left,right 都可以插入值
  • 如果key不存在,创建新的链表
  • 如果key存在,新增内容
  • 如果移除了所有值,空链表,也代表不存在
  • 在两边插入或者改动值,效率最高!中间操作元素,相对来说效率较低

消息排队!消息队列!队列(lpush rpop) 栈(lpush lpop)

Set

set中的值不能重复

#######################################################################
127.0.0.1:6379> sadd myset hello	# set集合中添加元素
以上是关于NoSql之Redis详细笔记的主要内容,如果未能解决你的问题,请参考以下文章

NoSQL学习笔记之——Redis基础

Redis_NoSQL入门学习笔记

Redis_NoSQL入门学习笔记

我用一天时间爆肝出了这份Redis详细笔记

NoSQL之 Redis的五大数据类型的详细介绍

大数据笔记(二十一)——NoSQL数据库之Redis