NoSQL介绍

NoSQL = Not Only SQL

为什么使用NoSQL

关系型数据库:表格,行,列
随着互联网的高速发展,传统的关系型数据库在应付超大规模,超大流量以及高并发的时候力不从心

大数据时代的3V和3高

  • 大数据3V(问题)
    • 海量Volume
    • 多样Variety
    • 实时Velocity
  • 互联网3高(性能)
    • 高并发
    • 高可拓
    • 高性能

NoSQL特点

  1. 方便扩展(数据之间没有关系)
  2. 快速读写(Redis一秒写8万次,读取11万次)
  3. 成本低廉(大部分分布式数据库都是开源)
  4. 数据类型是多样型的(不需要事先设计数据库)
  5. 传统的RDBMS和NoSQL
    • 传统的 RDBMS(关系型数据库管理系统)
      • 结构化组织
      • SQL
      • 数据和关系都在单独的表中
      • 严格的一致性
      • 基础的事务
      • 。。。。。。
    • NoSQL
      • 不仅仅是数据
      • 没有固定的查询语言
      • 键值对存储,列存储,文档存储,图形数据库(社交关系)
      • 最终一致性
      • CAP定理和BASE(异地多活)
      • 高性能,高可用,高可扩
      • 。。。。。。

NoSQL类型

KV键值对

  • 新浪:Redis
  • 美团:Redis+Tair
  • 阿里,百度:Redis+memecache

文档型数据库

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

列存储数据库

  • HBase
  • 分布式文件系统

图形关系数据

社交关系,拓扑图

Redis

缓存

缓存就是数据交换的缓冲区(又称作Cache),当某一硬件要读取数据时,会首先从缓存中查找需要的数据,找到了则直接执行,找不到的话则从内存中查找。由于缓存的运行速度比内存快得多,故缓存的作用就是帮助硬件更快地运行。

因为缓存往往使用的是RAM(断电即掉的非永久性储存),所以在用完后还是会把文件送到硬盘等存储器里永久存储。

概念

Redis(REmote DIctionary Server) 远程字典服务是一个由Salvatore Sanfilippo写的key-value存储系统。
Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从同步)。
它通常也被称为数据结构服务器

Redis能做什么

  1. 内存存储,持久化,内存中是断电即失,所以持久化很重要(rdb,aof)
  2. 效率高,可以用于告诉缓存
  3. 发布订阅系统
  4. 地图信息分析
  5. 计时器,计数器(浏览量)
  6. 。。。。。。

Redis基础知识

1
2
3
4
# 切换数据库(Redis默认有16个数据库)
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]>
1
2
3
# 查看所有key
127.0.0.1:6379[3]> keys *
(empty list or set)
1
2
3
# 清空数据库
127.0.0.1:6379[3]> flushdb
OK
1
2
3
# 清空全部数据库
127.0.0.1:6379[3]> flushall
OK

Redis是单线程的
官方表示,Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是机器的内存和网络带宽。既然可以使用单线程,就是用单线程了。

误区

  1. 高性能的服务器一定是多线程的
  2. 多线程(CPU上下文会切换)一定会比单线程快

核心:Redis是将所有的数据全部存放在内存中,所以使用单线程去操作效率就是最高的,多线程(CPU会上下文切换:耗时),对于内存系统来说,如果没有上下文切换效率就是最高。

Redis-Key命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 设置键值对
127.0.0.1:6379> set 1 test1
OK
127.0.0.1:6379> set 2 test2
OK
# 查询所有key
127.0.0.1:6379> keys *
1) "2"
2) "1"
# 判断key是否存在
127.0.0.1:6379> exists 1
(integer) 1
127.0.0.1:6379> exists 3
(integer) 0
# 移动key到指定数据库
127.0.0.1:6379> move 1 1
127.0.0.1:6379> keys *
1) "2"
# 设置key过期时间
127.0.0.1:6379> expire 2 10
(integer) 1
# 查询key剩余时间
127.0.0.1:6379> ttl 2
(integer) 0
127.0.0.1:6379> ttl 2
(integer) -2
# 查看key的类型
127.0.0.1:6379> type 1
string

Redis中文网文档

Redis事务

Redis事务本质:一组命令的集合。一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行。
一次性,顺序性,排他性

Redis事务没有隔离级别的概念。
所有的命令在事务中,并没有被直接执行。只有发起执行命令的时候才会执行。

Redis单条命令保证原子性,但是事务不保证原子性。

Redis事务:

  • 开启事务(multi)
  • 命令入队()
  • 执行事务(exec)
命令 描述
MULTI 标记一个事务块的开始
EXEC 执行所有事务块内的命令
DISCARD 取消事务,放弃执行事务块内的所有命令
WATCH key [key …] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断(乐观锁)
UNWATCH 取消 WATCH 命令对所有 key 的监视

正常执行事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 开启事务
127.0.0.1:6379> multi
OK
# 命令入队
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
# 执行事务
127.0.0.1:6379> exec
1) OK
2) "v1"
3) OK

放弃事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 开启事务
127.0.0.1:6379> multi
OK
# 命令入队
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
# 放弃事务
127.0.0.1:6379> discard
OK
# 事务中的命令都不执行
127.0.0.1:6379> get k4
(nil)

执行事务出错

编译型异常

代码错误,事务中所有命令都不执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
# 错误命令
127.0.0.1:6379> set k3
(error) ERR wrong number of arguments for 'set' command
# 执行事务报错
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
# 事务中所有命令都不执行
127.0.0.1:6379> get k1
(nil)
运行时异常

如果事务队列中存在语法性错误,那么执行命令的时候,其他命令都是可以正常执行的,错误命令抛出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 "v1"
QUEUED
# 错误命令
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
# 错误命令抛出,其余命令正常执行
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
127.0.0.1:6379> get k2
"v2"

乐观锁

Redis可以用watch代替java中的乐观锁

性能测试

1
redis-benchmark [option] [option value]

可选参数:

来自Redis中文网

参数

整合SpringBoot

SpringBoot操作数据:Spring-data jpa jdbc mongodb redis

说明:在SpringBoot2.x之后,原来使用的jedis被替换为了lettuce
jdis:采用直连,多个线程操作的话是不安全的,可使用jdis pool连接池避免安全问题。更像BIO模式
lettuce:采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况。可以减少线程数据,更像NIO模式

RedisTemplate系统配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Bean
// 可用自定义配置类替换
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
// 默认的RedisTemplate没有过多配置,redis对象都需要序列化
// 两个泛型都是ObjecT类型,使用时需要前置转换
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}

@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate
// 单独提出来的String类型Bean
(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}

步骤

导入依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 支持lettuce -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.8.0</version>
</dependency>

修改配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#redis 配置
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
#spring.redis.password=123456
# 连接池最大连接数(使用负值表示没有限制) 默认 8
spring.redis.lettuce.pool.max-active=10
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
spring.redis.lettuce.pool.max-wait=-1
# 连接池中的最大空闲连接 默认 8
spring.redis.lettuce.pool.max-idle=5
# 连接池中的最小空闲连接 默认 0
spring.redis.lettuce.pool.min-idle=1
# 连接超时时间(毫秒)
spring.redis.timeout=3000

使用

1
2
3
4
5
redisTemplate.opsForValue();//操作字符串
redisTemplate.opsForHash();//操作hash
redisTemplate.opsForList();//操作list
redisTemplate.opsForSet();//操作set
redisTemplate.opsForZSet();//操作有序set

操作与命令行操作一致

自定义配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Configuration
public class RedisConfig {

@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();

return template;
}
}

参考

Redis数据类型