一、NoSQL

1.1、概述

NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库。

NoSQL 不依赖业务逻辑方式存储,而以简单的 key-value 模式存储。因此大大的增加了数据库的扩展能力。

  • 不遵循SQL标准。

  • 不支持ACID。

  • 远超于SQL的性能。

  • 用不着 SQL 的和用了 SQL 也不行的情况,请考虑用 NoSQL

1.2、适用场景

  • 对数据高并发的读写
  • 海量数据的读写
  • 对数据高可扩展性的

1.3、不合适场景

  • 需要事务支持

  • 基于 SQL 的结构化查询存储,处理复杂的关系,需要即席查询。

即席查询指那些用户在使用系统时,根据自己当时的需求定义的查询

1.4、NoSQL 常见产品

1、Memcached

  • 很早出现的 NoSQL数据库

  • 数据都在内存中,一般不持久化

  • 支持简单的 key-value 模式,支持类型单一

  • 一般是作为缓存数据库辅助持久化的数据库

2、Redis

  • 几乎覆盖了 Memcached 的绝大部分功能
  • 数据都在内存中,支持持久化,主要用作备份恢复
  • 除了支持简单的 key-value 模式,还支持多种数据结构的存储,比如 list、set、hash、zset等。
  • 一般是作为缓存数据库辅助持久化的数据库

3、MongoDB

  • 高性能、开源、模式自由(schema free)的文档型数据库
  • 数据都在内存中, 如果内存不足,把不常用的数据保存到硬盘
  • 虽然是key-value模式,但是对value(尤其是**json**)提供了丰富的查询功能
  • 支持二进制数据及大型对象
  • 可以根据数据的特点**替代 RDBMS ** ,成为独立的数据库。或者配合 RDBMS ,存储特定的数据。

二、Redis 入门

2.1、概述

  • RedisRemote Dictionary Server ,即远程字典服务

  • 一个开源的由ANSI C语言编写,支持网络、可基于内存也可持久化的日志型Key-value数据库,支持多种语言

  • Redis 会周期性将更新的数据写入磁盘或者把修改操作写入追加的记录文件

2.2、特性

  • 开源
  • 支持多种语言
  • 支持持久化、集群和事务

2.3、基础知识

  • redis 有16个数据库,默认使用第1个数据库(下标为0)
  • 使用select可以切换数据库,使用DBSIZE可以查看当前数据库的数据数量

  • 清除当前数据库:flushdb
  • 清楚所有数据库:flushall
  • redis 是单线程的,redis 的瓶颈是根据机器的内存和网络带宽。

2.4、多样的数据结构存储持久化数据

image-20210425215507641

三、五大数据类型

image-20210203221317019

3.1、Redis-key

EXISTS 键名,如果有这个键,返回1,否则返回0
exists 键名

image-20201011214514520

3.2、String(字符串)

1、append 键名 要追加的内容:

往已有的键值对的值中拼接新内容,如果没有该键,那么就新建一个键值对(相当于set)

1
2
3
4
5
6
7
set name hello
get name
输出hello
append name world
输出10-->返回拼接后值的长度
get name
输出helloworld
  • 先设置一个键值对,即name–>hello,然后往该键值对的值中拼接字符world,最后使用get name查看结果

image-20201011222603013

2、strlen 键名 :

获取该键值对中值的长度

1
2
strlen name
输出10

image-20201011222916865

3、incr 键名:

针对数值使用,让数值的值+1

  • 初始化一个键值对(views–0),然后使用incr 命令让其+1
1
2
3
4
5
set views 0
输出0
incr views
get views
输出1

image-20201011224023734

4、decr 键名:

同针对数值使用,让该数值的值-1

  • 将上面的views从3减到2
1
decr views

image-20201011224227673

5、incrby/decrby:

类似incr/decr,多了个步长

  • 将views的值从2直接加到12(步长设置为10),然后把views的值从12降到7(步长为5)
1
2
incrby views 10
decrby views 5

image-20201011224913175

6、GETRANGE 键名 起始坐标 终止坐标(类似java中String类的substring)

截取(终止坐标 - 起始坐标) + 1个字符,闭区间[起始,终止]

  • 设置一个键值对,然后截取该值的一部分

image-20201011225254904

使用GETRANGE 键名 0 -1获取值对应的整个字符串,相当于get 键名

  • 获取该键值对值的全部内容

image-20201011225441783

7、SETRANGE 键名 偏移量n 要替换的字符串

替换指定字符开始的字符串

  • 先设置一个键值对:key2–>abcdefg,然后偏移一个单位,将xx替换到目标串中

image-20201011230122084

8、setex 键名 过期时间 值

为指定的key设置值和过期时间,如果key已经存在,SETEX命令会替换旧的值

  • 先设置一个键值对,然后使用setex覆盖

image-20201011232438878

9、setnx(SET IF NOT EXISTS) 键名 值

当指定的key不存在时,为key设置指定的值,如果存在会覆盖失败,返回0,成功返回1,这个命令经常在分布式锁中用到

  • 先设置一个键值对,然后尝试使用setnx覆盖,观察结果,发现返回0,且key的值还是wuhu111

image-20201011232537974

10、mset 键1 值1 键2 值2 …

使用这个命令可以批量添加键值对

  • 添加三个键值对

image-20201011233358741

11、mget 键1 键2 …

批量取得值

  • 取得上面添加的值

image-20201011233547127

12、msetnx 键1 值1 …

同时插入多个键值对,如果有一个键已经存在,就不执行插入操作,所有键都不存在时才插入

13、设置对象

set user : 1 {username : zhuo,age : 18}

  • 使用mset和mget存储和获取对象
1
2
3
4
5
127.0.0.1:6379> mset user:1:name hzx user:1:age 18
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "hzx"
2) "18"

image-20201011234455595

14、getset 先取后赋值

由于是先get后set,所以注意结果是上次的值,先返回当前值,后设置新值

  • 设置一个键值对k1-v1,然后使用getset将值变为value1,观察

image-20201011235025047

15、String应用场景:

  • 由于redis中没有数值类型,所以数字也是用string存储
  • 可以用作计数器
  • 统计数量
  • 对象缓存存储

3.3、List

  • 在redis中,我们可以通过设置规则来使list成为一个栈或队列
  • 所有list命令大部分以l开头

1、LPUSH 集合名 值

将一个或多个值从左边插进列表

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> LPUSH list one # 在列表左边插入值
(integer) 1
127.0.0.1:6379> LPUSH list two three # 在列表中插入多个值
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1 # 获取列表中所有的值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> Lrange list 0 1 #获取列表中下标在[0,1]的值
1) "three"
2) "two"

2、RPUSH 集合名 值

将一个或多个值从右边插进列表

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> Rpush list right
(integer) 4
127.0.0.1:6379> rpush list right1 right2
(integer) 6
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
5) "right1"
6) "right2"

3、LPOP 列表名

从左边弹出(移出)一个元素

1
2
3
4
5
6
7
8
127.0.0.1:6379> LPOP list 
"three"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "right"
4) "right1"
5) "right2"

4、RPOP 列表名

从右边弹出一个元素

1
2
3
4
5
6
7
127.0.0.1:6379> RPOP list
"right2"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "right"
4) "right1"

5、LINDEX 列表名 下标

通过下标获取值

1
2
3
4
127.0.0.1:6379> LINDEX list 0
"two"
127.0.0.1:6379> LINDEX list 3
"right1"

6、Llen 列表名

获取列表长度

1
2
3
4
5
6
7
8
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
3) "right"
4) "right1"
127.0.0.1:6379> Llen list
(integer) 4

7、Lrem 列表名 移除个数 精确值

移除列表中的一个或多个值,精确匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "one"
3) "one"
4) "one"
5) "two"
6) "one"
7) "right"
8) "right1"
127.0.0.1:6379> lrem list 1 two
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "one"
3) "one"
4) "one"
5) "one"
6) "right"
7) "right1"
127.0.0.1:6379> lrem list 5 one
(integer) 5
127.0.0.1:6379> lrange list 0 -1
1) "right"
2) "right1"

8、ltrim 列表名 起始坐标 结束坐标

截断列表,列表元素变为[start ,end]

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> lrange list 0 -1
1) "hello1"
2) "hello2"
3) "hello3"
4) "hello4"
127.0.0.1:6379> ltrim list 1 2
OK
127.0.0.1:6379> lrange list 0 -1
1) "hello2"
2) "hello3"

9、exists 列表名

判断列表中有几个值

10、LSET 列表名 下标 值

替换列表指定下标的值,如果当前列表中不存在指定下标所对应的值,就报错

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> lrange list 0 -1
1) "value3"
2) "value2"
3) "value1"
4) "hello2"
127.0.0.1:6379> lset list 0 item
OK
127.0.0.1:6379> lrange list 0 -1
1) "item"
2) "value2"
3) "value1"
4) "hello2"

11、LINSERT 列表名 前|后 要插入的位置 要插入的值

在列表的某个值前|后插入一个值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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> lrange list 0 -1
1) "hello"
2) "world"
# 在world单词前插入一个值:other
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"
  • 往上面list的other元素后插入一个值wuhu
1
2
3
4
5
6
7
127.0.0.1:6379> linsert list after "other" wuhu
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "hello"
2) "other"
3) "wuhu"
4) "world"

12、小结:

  • list实际上是一个链表,所以可以在节点前后插入节点,也可以在最左端、最右端插入元素

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

  • 如果key存在,新增内容

  • 如果移除了key,那么链表被移除

  • 在两边插入或改动值效率最高!操作中间值效率会降低

  • 可以用来模拟栈(LPUSH、LPOP)、消息队列(LPUSHR、RPOP)