Nosql 数据库介绍
是一种非关系型数据库服务,它能解决常规数据库的并发能力,比如传统的数据库的IO与性能的瓶颈,同样它是关系型数据库的一个补充,有着比较好的高效率与高性能。专注于key-value查询的redis、memcached、ttserver。解决以下问题:
- 对数据库的高并发读写需求
- 大数据的高效存储和访问需求
- 高可扩展性和高可用性的需求
Redis知识体系
什么是 Redis
Redis 是一款内存高速缓存数据库。Redis全称为:Remote Dictionary Server(远程数据服务),使用C语言编写,Redis是一个key-value存储系统(键值存储系统),支持丰富的数据类型,如:String、list、set、zset、hash。
Redis是一种支持key-value等多种数据结构的存储系统。可用于缓存,事件发布或订阅,高速队列等场景。支持网络,提供字符串,哈希,列表,队列,集合结构直接存取,基于内存,可持久化。
官方资料
- Redis官网:http://redis.io/
- Redis官方文档:http://redis.io/documentation
- Redis教程:http://www.w3cschool.cn/redis/redis-intro.html
- Redis下载:http://redis.io/download
为什么要使用 Redis
一个产品的使用场景肯定是需要根据产品的特性,先列举一下Redis的特点:
- 读写性能优异
- Redis能读的速度是110000次/s,写的速度是81000次/s (测试条件见下一节)。
- 数据类型丰富
- Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子性
- Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
- 丰富的特性
- Redis支持 publish/subscribe, 通知, key 过期等特性。
- 持久化
- Redis支持RDB, AOF等持久化方式
- 发布订阅
- Redis支持发布/订阅模式
- 分布式
- Redis Cluster
(PS: 具体再结合下面的使用场景理解下)
下面是官方的bench-mark根据如下条件获得的性能测试(读的速度是110000次/s,写的速度是81000次/s)
- 测试完成了50个并发执行100000个请求。
- 设置和获取的值是一个256字节字符串。
- Linux box是运行Linux 2.6,这是X3320 Xeon 2.5 ghz。
- 文本执行使用loopback接口(127.0.0.1)。
Redis有哪些优缺点
优点
- 读写性能优异, Redis能读的速度是110000次/s,写的速度是81000次/s。
- 支持数据持久化,支持AOF和RDB两种持久化方式。
- 支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
- 数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构。
- 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
缺点
- 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
- Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
- 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
- Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
更多关于 Redis 学习的文章,请参阅:NoSQL 数据库系列之 Redis ,本系列持续更新中。
Redis的使用场景
redis 应用场景总结 redis 平时我们用到的地方蛮多的,下面就了解的应用场景做个总结:
热点数据的缓存
缓存是Redis最常见的应用场景,之所有这么使用,主要是因为Redis读写性能优异。而且逐渐有取代memcached,成为首选服务端缓存的组件。而且,Redis内部是支持事务的,在使用时候能有效保证数据的一致性。
作为缓存使用时,一般有两种方式保存数据:
- 读取前,先去读Redis,如果没有数据,读取数据库,将数据拉入Redis。
- 插入数据时,同时写入Redis。
方案一:实施起来简单,但是有两个需要注意的地方:
- 避免缓存击穿。(数据库没有就需要命中的数据,导致Redis一直没有数据,而一直命中数据库。)
- 数据的实时性相对会差一点。
方案二:数据实时性强,但是开发时不便于统一处理。
当然,两种方式根据实际情况来适用。如:方案一适用于对于数据实时性要求不是特别高的场景。方案二适用于字典表、数据量不大的数据存储。
限时业务的运用
redis中可以使用expire命令设置一个键的生存时间,到时间后redis会删除它。利用这一特性可以运用在限时的优惠活动信息、手机验证码等业务场景。
计数器相关问题
redis由于incrby命令可以实现原子性的递增,所以可以运用于高并发的秒杀活动、分布式序列号的生成、具体业务还体现在比如限制一个手机号发多少条短信、一个接口一分钟限制多少请求、一个接口一天限制调用多少次等等。
int类型,incr方法
例如:文章的阅读量、微博点赞数、允许一定的延迟,先写入Redis再定时同步到数据库。
分布式锁
这个主要利用redis的setnx命令进行,setnx:"set if not exists"就是如果不存在则成功设置缓存同时返回1,否则返回0 ,这个特性在很多后台中都有所运用,因为我们服务器是集群的,定时任务可能在两台机器上都会运行,所以在定时任务中首先 通过setnx设置一个lock, 如果成功设置则执行,如果没有成功设置,则表明该定时任务已执行。当然结合具体业务,我们可以给这个lock加一个过期时间,比如说30分钟执行一次的定时任务,那么这个过期时间设置为小于30分钟的一个时间就可以,这个与定时任务的周期以及定时任务执行消耗时间相关。
String 类型setnx方法,只有不存在时才能添加成功,返回true
public static boolean getLock(String key) {
Long flag = jedis.setnx(key, "1");
if (flag == 1) {
jedis.expire(key, 10);
}
return flag == 1;
}
public static void releaseLock(String key) {
jedis.del(key);
}
在分布式锁的场景中,主要用在比如秒杀系统等。
延时操作
比如在订单生产后我们占用了库存,10分钟后去检验用户是否真正购买,如果没有购买将该单据设置无效,同时还原库存。由于redis自2.8.0之后版本提供Keyspace Notifications功能,允许客户订阅Pub/Sub频道,以便以某种方式接收影响Redis数据集的事件。所以我们对于上面的需求就可以用以下解决方案,我们在订单生产时,设置一个key,同时设置10分钟后过期, 我们在后台实现一个监听器,监听key的实效,监听到key失效时将后续逻辑加上。
当然我们也可以利用rabbitmq、activemq等消息中间件的延迟队列服务实现该需求。
排行榜相关问题
关系型数据库在排行榜方面查询速度普遍偏慢,所以可以借助redis的SortedSet进行热点数据的排序。
比如点赞排行榜,做一个SortedSet, 然后以用户的openid作为上面的username, 以用户的点赞数作为上面的score, 然后针对每个用户做一个hash, 通过zrangebyscore就可以按照点赞数获取排行榜,然后再根据username获取用户的hash信息,这个当时在实际运用中性能体验也蛮不错的。
点赞、好友等相互关系的存储
Redis 利用集合的一些命令,比如求交集、并集、差集等。
在微博应用中,每个用户关注的人存在一个集合中,就很容易实现求两个人的共同好友功能。
假如上面的微博ID是t1001,用户ID是u3001
用 like:t1001 来维护 t1001 这条微博的所有点赞用户
- 点赞了这条微博:sadd like:t1001 u3001
- 取消点赞:srem like:t1001 u3001
- 是否点赞:sismember like:t1001 u3001
- 点赞的所有用户:smembers like:t1001
- 点赞数:scard like:t1001
是不是比数据库简单多了。7000字 Redis 超详细总结笔记 !建议收藏
简单队列
由于Redis有list push和list pop这样的命令,所以能够很方便的执行队列操作。
List提供了两个阻塞的弹出操作:blpop/brpop,可以设置超时时间
- blpop:blpop key1 timeout 移除并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
- brpop:brpop key1 timeout 移除并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
上面的操作。其实就是java的阻塞队列。学习的东西越多。学习成本越低
- 队列:先进先除:rpush blpop,左头右尾,右边进入队列,左边出队列
- 栈:先进后出:rpush brpop
更多关于Redis的应用场景解析请参阅:带你彻底搞懂 Redis 16 大应用场景
Redis为什么这么快
1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是O(1);
2、数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的;
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4、使用多路 I/O 复用模型,非阻塞 IO;
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
Redis 为什么是单线程的?
- 代码更清晰,处理逻辑更简单;
- 不用考虑各种锁的问题,不存在加锁和释放锁的操作,没有因为可能出现死锁而导致的性能问题;
- 不存在多线程切换而消耗CPU;
- 无法发挥多核CPU的优势,但可以采用多开几个Redis实例来完善;
Redis真的是单线程的吗?
- Redis6.0之前是单线程的,Redis6.0之后开始支持多线程;
- Redis内部使用了基于epoll的多路复用,也可以多部署几个Redis服务器解决单线程的问题;
- Redis主要的性能瓶颈是内存和网络;
- 内存好说,加内存条就行了,而网络才是大麻烦,所以Redis6内存好说,加内存条就行了;
- 而网络才是大麻烦,所以Redis6.0引入了多线程的概念;
- Redis6.0在网络IO处理方面引入了多线程,如网络数据的读写和协议解析等,需要注意的是,执行命令的核心模块还是单线程的;
Redis 安装
Linux下安装Redis
下载安装
# 安装gcc
yum install gcc
# 下载redis wget下载或者直接去 http://redis.io/download 官网下载
wget http://download.redis.io/releases/redis-7.0.0.tar.gz
# 把下载好的redis解压
tar xzf redis-7.0.0.tar.gz
# 进入到解压好的redis-7.0.0.tar.gz目录下,进行编译与安装
cd redis-7.0.0.tar.gz
make
make install
修改配置文件
按需修改自己想要的redis配置。
# 编辑redis.conf配置文件
vim redis.conf
# Redis使用后台模式
daemonize yes
# 关闭保护模式
protected-mode no
# 注释以下内容开启远程访问
# bind 127.0.0.1
# 修改启动端口为6381
port 6381
启动Redis
# 启动并指定配置文件
src/redis‐server redis.conf(注意要使用后台启动,所以修改redis.conf里的daemonize改为yes)
# 验证启动是否成功
ps -ef|grep redis
# 进入redis客户端
src/redis-cli
redis-cli -h 192.168.239.131 -p 6379 (指定ip 端口连接redis)
# 退出客户端
quit
# 退出redis服务
pkill redis‐server
kill 进程号
src/redis‐cli shutdown
# 设置redis密码
config set requirepass 123456
# 验证密码
auth 123456
# 查看密码
config get requirepass
Windows 下安装 Redis
官网没有提供 Windows 版本,只有 Linux 版本,GitHub 上可以下载。
下载安装
下载地址:https://github.com/MicrosoftArchive/redis/tags
直接下载 Redis-x64-3.2.100.msi 版本即可,双击安装:都选择默认即可,下一步、下一步安装就行了,非常的简单。然后可以去连接一下,cmd窗口输入命令telnet 127.0.0.1 6379
正常连接,也可以正常操作
Redis.conf 详解
找到启动时指定的配置文件:
[root@itzhouc ~]# cd /usr/local/bin
[root@itzhouc bin]# ls
dump.rdb jemalloc.sh kconfig luajit mcrypt redis-benchmark redis-check-rdb redis-sentinel
jemalloc-config jeprof libmcrypt-config luajit-2.0.4 mdecrypt redis-check-aof redis-cli redis-server
[root@itzhouc bin]# cd kconfig/
[root@itzhouc kconfig]# vim redis.conf
单位
# Redis configuration file example.
#
# Note that in order to read the configuration file, Redis must be
# started with the file path as first argument:
#
# ./redis-server /path/to/redis.conf
# Note on units: when memory size is needed, it is possible to specify
# it in the usual form of 1k 5GB 4M and so forth:
#
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#
# units are case insensitive so 1GB 1Gb 1gB are all the same.
配置文件中 unit 单位对大小写不敏感。
包含
################################## INCLUDES ###################################
# Include one or more other config files here. This is useful if you
# have a standard template that goes to all Redis servers but also need
# to customize a few per-server settings. Include files can include
# other files, so use this wisely.
#
# Notice option "include" won't be rewritten by command "CONFIG REWRITE"
# from admin or Redis Sentinel. Since Redis always uses the last processed
# line as value of a configuration directive, you'd better put includes
# at the beginning of this file to avoid overwriting config change at runtime.
#
# If instead you are interested in using includes to override configuration
# options, it is better to use include as the last line.
#
# include /path/to/local.conf
# include /path/to/other.conf
配置文件可以将多个配置文件合起来使用。
NETWORK 网络
bind 127.0.0.1 #绑定的 IP
protected-mode no #保护模式
port 6379 #端口设置
GENERAL 通用
daemonize yes # 以守护进程的方式运行,默认是 no ,我们需要自己开启为 yes
pidfile /var/run/redis_6379.pid # 如果是后台启动,我们需要指定一个pid 文件
# 日志级别
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice
logfile "" # 日志文件的位置
databases 16 # 数据库的数量,默认是 16
always-show-logo yes # 是否总是显示 LOGO
快照 SNAPSHOTTING
持久化,在规定的时间内,执行了多少次操作则会持久化到文件。
Redis 是内存数据库,如果没有持久化,那么数据断电即失。
################################ SNAPSHOTTING ################################
#
# Save the DB on disk:
#
# save
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
#
# Note: you can disable saving completely by commenting out all "save" lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
#
# save ""
# 如果 900s 内,至少有 1 个 key 进行了修改,进行持久化操作
save 900 1
# 如果 300s 内,至少有 10 个 key 进行了修改,进行持久化操作
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes # 如果持久化出错,是否还要继续工作
rdbcompression yes # 是否压缩 rdb 文件,需要消耗一些 cpu 资源
rdbchecksum yes # 保存 rdb 文件的时候,进行错误的检查校验
dir ./ # rdb 文件保存的目录
SECURITY 安全
可以设置 Redis 的密码,默认是没有密码的。
[root@itzhouc bin]# redis-cli -p 6379
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> config get requirepass # 获取 redis 密码
1) "requirepass"
2) ""
127.0.0.1:6379> config set requirepass "123456" # 设置 redis 密码
OK
127.0.0.1:6379> ping
(error) NOAUTH Authentication required. # 发现所有的命令都没有权限了
127.0.0.1:6379> auth 123456 # 使用密码登录
OK
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "123456"
127.0.0.1:6379>
CLIENTS 限制
################################### CLIENTS ####################################
# Set the max number of connected clients at the same time. By default
# this limit is set to 10000 clients, however if the Redis server is not
# able to configure the process file limit to allow for the specified limit
# the max number of allowed clients is set to the current file limit
# minus 32 (as Redis reserves a few file descriptors for internal uses).
#
# Once the limit is reached Redis will close all the new connections sending
# an error 'max number of clients reached'.
#
# maxclients 10000 # 设置能链接上 redis 的最大客户端数量
# maxmemory # redis 设置最大的内存容量
maxmemory-policy noeviction # 内存达到上限之后的处理策略
- noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错。
- allkeys-lru:在所有键中采用lru算法删除键,直到腾出足够内存为止。
- volatile-lru:在设置了过期时间的键中采用lru算法删除键,直到腾出足够内存为止。
- allkeys-random:在所有键中采用随机删除键,直到腾出足够内存为止。
- volatile-random:在设置了过期时间的键中随机删除键,直到腾出足够内存为止。
- volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。
APPEND ONLY 模式 AOF 配置
appendonly no # 默认是不开启 AOF 模式的,默认使用 rdb 方式持久化,大部分情况下,rdb 完全够用
appendfilename "appendonly.aof" # 持久化的文件的名字
# appendfsync always # 每次修改都会 sync 消耗性能
appendfsync everysec # 每秒执行一次 sync 可能会丢失这 1s 的数据。
# appendfsync no # 不执行 sync 这个时候操作系统自己同步数据,速度最快。
参考来源:https://www.pdai.tech/md/db/nosql-redis/db-redis-overview.html https://www.cnblogs.com/itzhouq/p/redis4.html