分布式存储
-
NoSQL
NoSQL,泛指非关系型的数据库,全称Not Only SQL,意即“不仅仅是SQL”。
NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。在过去几年,关系型数据库一直是数据持久化的唯一选择,数据工作者考虑的也只是在这些传统数据库中做筛选,比如SQL Server、Oracle或者是MySQL。
网络应用程序的规模日渐变大,我们需要储存更多的数据、服务更多的用户以及需求更多的计算能力。但是关系型数据库存储结构是面向关系的的,而应用却是面向对象的,所以在每次存储或者查询数据时,我们都需要做转换。为了应对这种情形,我们需要不停的扩展。扩展分为两类:一种是纵向扩展,即增强单台主机的性能;另一种是横向扩展,即购买更多的节点组成集群。在巨大的规模下,纵向扩展发挥的作用并不是很大。因为单机器性能提升需要巨额的开销并且有着性能的上限。鉴于这种情况,我们需要新的数据库,因为关系数据库并不能很好的运行在集群上。不错你也可能会去搭建关系数据库集群,但是他们使用的是共享存储,这并不是我们想要的类型。
NoSQL数据库在以下的这几种情况下比较适用:
数据模型比较简单
需要灵活性更强的IT系统
对数据库性能要求较高
不需要高度的数据一致性
对于给定key,比较容易映射复杂值的环境
键值(Key-Value)数据库
键值数据库就像在传统语言中使用的哈希表。你可以通过key来添加、查询或者删除数据,鉴于使用主键访问,所以会获得不错的性能及扩展性。 键值数据库查找速度快,数据无结构化,通常只被当作字符串或者二进制数据。
适用的场景
储存用户信息,比如会话、配置文件、参数、购物车等等。这些信息一般都和 ID(键)挂钩,这种情景下键值数据库是个很好的选择。
不适用场景
取代通过键查询,而是通过值来查询。Key-Value数据库中根本没有通过值查询的途径。
需要储存数据之间的关系。在Key-Value数据库中不能通过两个或以上的键来关联数据。
事务的支持。在Key-Value数据库中故障产生时不可以进行回滚。
产品:Riak、Redis、Memcached、Amazon’s Dynamo、Project Voldemort
面向文档(Document-Oriented)数据库
面向文档数据库会将数据以文档的形式储存。每个文档都是自包含的数据单元,是一系列数据项的集合。每个数据项都有一个名称与对应的值,值既可以是简单的数据类型,如字符串、数字和日期等;也可以是复杂的类型,如有序列表和关联对象。数据存储的最小单位是文档,同一个表中存储的文档属性可以是不同的,数据可以使用XML、JSON或者JSONB等多种形式存储。
数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构,但是查询性能不高,而且缺乏统一的查询语法。
适用的场景
日志。企业环境下,每个应用程序都有不同的日志信息。Document-Oriented数据库并没有固定的模式,所以我们可以使用它储存不同的信息。
分析。鉴于它的弱模式结构,不改变模式下就可以储存不同的度量方法及添加新的度量。
不适用场景
在不同的文档上添加事务。Document-Oriented数据库并不支持文档间的事务,如果对这方面有需求则不应该选用这个解决方案。
产品:MongoDB、CouchDB、RavenDB、Terrastore 、OrientDB
列存储(Wide Column Store/Column-Family)数据库
列存储数据库将数据储存在列族(column family)中,一个列族存储经常被一起查询的相关数据。举个例子,如果我们有一个Person类,我们通常会一起查询他们的姓名和年龄而不是薪资。这种情况下,姓名和年龄就会被放入一个列族中,而薪资则在另一个列族中。
列存储查找速度快,可扩展性强,更容易进行分布式扩展,适用于分布式的文件系统。
适用的场景
日志。因为我们可以将数据储存在不同的列中,每个应用程序可以将信息写入自己的列族中。
博客平台。我们储存每个信息到不同的列族中。举个例子,标签可以储存在一个,类别可以在一个,而文章则在另一个。
不适用场景
如果我们需要ACID事务。Vassandra就不支持事务。
原型设计。如果我们分析Cassandra的数据结构,我们就会发现结构是基于我们期望的数据查询方式而定。在模型设计之初,我们根本不可能去预测它的查询方式,而一旦查询方式改变,我们就必须重新设计列族。
产品:Cassandra、HBase
图(Graph-Oriented)数据库
图数据库允许我们将数据以图的方式储存。实体会被作为顶点,而实体之间的关系则会被作为边。比如我们有三个实体,Steve Jobs、Apple和Next,则会有两个“Founded by”的边将Apple和Next连接到Steve Jobs。
主要用于社交网络,推荐系统等。专注于构建关系图谱。
适用的场景
在一些关系性强的数据中
推荐引擎。如果我们将数据以图的形式表现,那么将会非常有益于推荐的制定
不适用场景
不适合的数据模型。图数据库的适用范围很小,因为很少有操作涉及到整个图。
产品:Neo4J、Infinite Graph、OrientDB
-
分布式系统特性
2000年,Eric Brewer教授在PODC的研讨会上提出了一个猜想:一致性、可用性和分区容错性三者无法在分布式系统中被同时满足,并且最多只能满足其中两个
C(一致性):所有的节点上的数据时刻保持同步
A(可用性):每个请求都能接受到一个响应,无论响应成功或失败
P(分区容错):系统应该能持续提供服务,即使系统内部有消息丢失
高可用、数据一致是很多系统设计的目标,但是分区又是不可避免的事情:
CA without P:如果不要求P(不允许分区),则C(强一致性)和A(可用性)是可以保证的。但其实分区不是你想不想的问题,而是始终会存在,因此CA的系统更多的是允许分区后各子系统依然保持CA。
CP without A:如果不要求A(可用),相当于每个请求都需要在Server之间强一致,而P(分区)会导致同步时间无限延长,如此CP也是可以保证的。很多传统的数据库分布式事务都属于这种模式。
AP wihtout C:要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。现在众多的NoSQL都属于此类。
BASE:BA,S,E,基于CAP演化而来
BA:Basically Available,基本可用
S:Soft state,软状态/柔性事务,即状态可以在一个时间窗口内是不同步的
E:Eventually consistency,最终一致性
Redis
Redis 是一个高性能的 key-value 数据库。 redis的出现,很大程度补偿了 memcached 这类 key/value 存储的不足,在部分场合可以对关系数据库起到很好的补充作用。它提供了 Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang 等客户端,使用很方便。
-
Redis特性
Redis 是一款使用 C 语言编写的开源 Key-Value 数据库,并具有以下特性:
使用内存存储
数据结构存储
支持网络交互
也可以持久化
所以可以用作为:数据库、缓存、消息队列
Redis支持多种数据结构:字符串、列表(数组)、hashes(关联数组)、集合、有序集合、bitmaps、hyperloglogs、空间索引
Redis 支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得 Redis 可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。
-
Redis安装
软件包在 epel 仓库中
[root@CentOS74 ~]# yum install redis
查看 Redis 的主配置文件 /etc/redis.conf 中的配置段
[root@CentOS74 ~]# cat /etc/redis.conf | grep "^###"
################################## INCLUDES ###################################
################################## NETWORK ####################################网络配置
################################# GENERAL #####################################通用配置
################################ SNAPSHOTTING ################################快照配置
################################# REPLICATION #################################主从复制
################################## SECURITY ###################################安全认证
################################### LIMITS ####################################资源限制
############################## APPEND ONLY MODE ###############################AOF持久
################################ LUA SCRIPTING ###############################LUA监本
################################ REDIS CLUSTER ###############################集群配置
################################## SLOW LOG ###################################慢日志
################################ LATENCY MONITOR ##############################延迟监控
############################# EVENT NOTIFICATION ##############################事件通知
############################### ADVANCED CONFIG ###############################高级配置
启动 Redis(启动前注意修改监听地址)
Redis-cli
使用 redis-cli 打开 redis 客户端
语法:redis-cli [选项]
选项:
-h 指定主机IP地址
-p 指定端口
-a 密码
-n 数据库编号
- @string
SET key value [EX seconds] [PX milliseconds] [NX|XX] 设置键值对
EX 设置有效时间,等同于 SETEX
NX 仅当键不存在时才设置,等同于SETNX
XX 仅当键存在时才设置
GET key 读取指定键的值
INCR key 给指定键的值加1
DECR key 给指定键的值减1
INCRBY key increment 按指定整数值加键的值
DECRBY key increment 按指定整数值减键的值
MGET key [key...] 读取多个键的值
MSET key value [key value ...] 设置多个键的值
- list
在 Redis 中认为队列左侧为首部,右侧为尾部
LPUSH key value [value ...] 由队首向列表中添加值
RPUSH key value [value ...] 由队尾向列表中添加值
LPOP key 从指定列表的队首取走一个值
RPOP key 从指定列表的队尾取走一个值
LPUSHX key value 仅当列表存在时向其队首添加一个值
RPUSHX key value 仅当列表存在时向其队尾添加一个值
BLPOP key [key ...] timeout 从指定列表的队首取走一个值,若没有则在超时时间内等待至有值并取走
BRPOP key [key ...] timeout 从指定列表的队尾取走一个值,若没有则在超时时间内等待至有值并取走
LRANGE key start stop 从列表中读取指定范围的元素
LINDEX key index 从列表中读取指定元素
LSET key index value 设置列表中指定下标元素的值
LLEN key 获取列表长度
- hash
HSET key field value 设置指定字典中的键值对
HMSET key field value [field value ...] 设置指定字典中的多个键值对
HGET key field 获取指定字典中键的值
HMGET key field [field ...] 获取指定字典中多个键的值
HGETALL key 获取指定字典中的所有键值对
HDEL key field [field ...] 删除字典中的键值对
HEXISTS key field 确定字典中是否存在指定键
HINCRBY key field increment 递增字典中指定一个键的值
HINCRBYFLOAT key field increment 递增字典中指定一个键的浮点值
HKEYS key 获取字典中的所有键
HVALS key 获取字典中的所有值
HSTRLEN key field 获取字典中指定键值得长度
- set
SADD key member [member ...] 向集合中添加元素
SCARD key 获取集合中的元素数量
SISMEMBER key member 确定集合中是否存在指定元素
SMEMBERS key 列出集合中的所有元素
SMOVE source destination member 移动指定元素由源集合到目标集合
SPOP key [count] 随机取走指定数量的元素
SRANDMEMBER key [count] 随机读取指定数量的元素
SREM key member [member ...] 从集合中删除元素
SUNION key [key ...] 求集合之间的并集
SUNIONSTORE destination key [key ...] 求集合之间的并集之后保存在目标集合中
SDIFF key [key ...] 由左至右求集合之间的差集
SDIFFSTORE destination key [key ...] 求集合之间的差集之后保存在目标集合中
SINTER key [key ...] 求集合之间的交集
SINTERSTORE destination key [key ...] 求集合之间的交集之后保存在目标集合中
- pubsub
PSUBSCRIBE pattern [pattern ...] 监听发布到指定模式匹配的频道的消息
SUBSCRIBE channel [channel ...] 监听指定频道的消息
PUBLISH channel message 发布消息到指定频道
PUNSUBSCRIBE [pattern [pattern ...]] 停止监听指定模式匹配的频道
UNSUBSCRIBE [channel [channel ...]] 体制监听指定频道
其他数据结构使用 help @数据结构 即可查看详细帮助
- 连接设置
AUTH password 进行密码认证
ECHO message 打印指定字符串
PING [message] 测试连接性
SELECT index 切换数据库
QUIT 退出
- server
CLIENT SETNAME connection-name 设置当前节点名称
CLIENT GETNAME 获取当前节点名称
CLIENT LIST 获取连接当前节点所有主机的信息
CLIENT KILL [ip:port] [ID client-id] [TYPE normal|master|slave|pubsub] [ADDR ip:port] [SKIPME yes/no] 杀掉一个连接
CLIENT PAUSE timeout 暂停所有客户端的连接
CLIENT REPLY ON|OFF|SKIP 显示服务器回复
CONFIG GET parameter 获取指定配置项的值
CONFIG SET parameter 设置指定配置项的值
CONFIG REWRITE 将命令行中的配置写入配置文件
FLUSHDB 删除当前所在库
FLUSHALL 删除所有数据库
INFO [section] 查看当前主机的状态
-
Redis配置
通用配置
################################# GENERAL #####################################
daemonize no #是否运行为后台进程,no时运行在systemd上
supervised no #不受其他任何管理程序控制
pidfile /var/run/redis_6379.pid #守护进程PID文件
loglevel notice #日志设备
logfile /var/log/redis/redis.log #日志文件
databases 16 #数据库个数
网络配置
################################## NETWORK #####################################
bind 0.0.0.0 #监听IP
protected-mode yes #保护模式,当没有定义bind且没有设置密码时开启,开启后只能在本地登录redis
port 6379 #监听端口
tcp-backlog 511 #后援队列
# unixsocket /tmp/redis.sock #使用套接字文件进行本机通信
# unixsocketperm 700 #套接字文件权限
timeout 0 #超时时间,0为永不超时
tcp-keepalive 300 #TCP超时时长
安全配置
################################## SECURITY ###################################
requirepass linux #设置使用密码登录
rename-command FLUSHDB "b840fc02d524045429941cc15f59e41cb7be6c52" #改变操作命令。不建议在AOF或Replication环境中用
资源限制
################################### LIMITS ####################################
maxclients 10000 #最大连接数
maxmemory 536870912 #最大内存使用空间,字节
zmaxmemory-policy noeviction #淘汰策略
maxmemory-samples 5 #取样范围,10为无限接近真实,5为推荐使用
淘汰策略
volatile-lru 基于有效时间进行LRU算法淘汰
allkeys-lru 基于所有键进行LRU算法淘汰
volatile-random 基于有效时间随机淘汰
allkeys-random 基于所有键随机淘汰
volatile-ttl 基于有效时间剩余淘汰
noeviction 不淘汰任何键,没空间后禁止写操作
慢日志
################################## SLOW LOG ###################################
slowlog-log-slower-than 10000 #超过多久的访问被记录,微秒
slowlog-max-len 128 #记录多少条日志
高级配置
############################### ADVANCED CONFIG ###############################
hash-max-ziplist-entries 512 #一个字典中最多有多少个键
hash-max-ziplist-value 64 #字典中键的值的大小
list-max-ziplist-size -2 #一个列表中键值的大小,正数为键,负数为值
list-compress-depth 0 #列表压缩强度
set-max-intset-entries 512 #一个集合最大占多少空间
zset-max-ziplist-entries 128 #一个集合中最多有多少个元素
zset-max-ziplist-value 64 #每个元素最大瞻多少空间
hll-sparse-max-bytes 3000
activerehashing yes
client-output-buffer-limit normal 0 0 0 #普通客户端的缓冲池
client-output-buffer-limit slave 256mb 64mb 60 #从服务器客户端
client-output-buffer-limit pubsub 32mb 8mb 60 #消息队列客户端
# 硬限制 软限制 超限制时间
hz 10
aof-rewrite-incremental-fsync yes
-
Redis持久化
RDB 方式进行持久化:将 Redis 中的所有数据按二进制的格式直接快照进 rdb 文件中
优点:使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了redis的高性能
且执行频率比 AOF 低,而且 rdb 文件中直接存储的是 key-values 的二进制形式,对于恢复数据也快
缺点:RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候
################################ SNAPSHOTTING ################################
save 900 1 #当900秒内发生1次修改则会快照
save 300 10 #当300秒内发生10次修改则会快照
save 60 10000 #当60秒内发生10000次修改则会快照
stop-writes-on-bgsave-error yes #当快照时出现错误,是否需要停止redis
rdbcompression yes #快照完是否压缩
rdbchecksum yes #每次快照时是否进行校验
dbfilename dump.rdb #rdb文件名
dir /var/lib/redis #rdb文件存放路径
同时,也可以使用 SVAE 或者 BGSAVE 来手动执行快照
注意:SAVE 命令将会使用主进程进行操作,阻塞其他请求;而 BGSAVE 则使用独立的进程进行快照,并不会阻塞主进程
AOF(Append-only file) 方式进行持久化:将对数据的每一次修改操作追加至 aof 文件中,且 Redis 重启时会优先读取 aof 文件进行恢复
优点:可以保持更高的数据完整性,如果设置追加file的时间是1s,如果redis发生故障,最多会丢失1s的数据;且如果日志写入不完整支持redis-check-aof来进行日志修复;AOF文件没被rewrite之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的flushall)
缺点:性能较低(每一条修改操作都要追加到aof文件,执行频率较RDB要高,而且aof文件中存储的是命令,对于恢复数据来讲需要逐行执行命令,所以恢复慢)
############################## APPEND ONLY MODE ###############################
appendonly no #默认并不启动
appendfilename "appendonly.aof" #aof文件名
appendfsync everysec #每秒调用一次fsync
no-appendfsync-on-rewrite no #执行RDB时是否暂缓AOF操作
auto-aof-rewrite-percentage 100 #当新添加的操作实例个数与上一次rewrite后的实例个数相等时
auto-aof-rewrite-min-size 64mb #aof文件容量指定大小才会触发rewrite
aof-load-truncated yes #加载aof文件时是否修剪,当有错误记录时会尝试读取日志纠正
使用 BGREWRITEAOF 命令手动执行 rewrite 操作
注意:每当 Redis 执行 rewrite 操作时,会开启一个子进程,并将新接收到的命令存放在缓冲区内,而 rewrite 会新键一个空文件将当前 Redis 数据库中的操作实例保存下来。当 rewrite 子进程向 Redis 发送退出信号后,Redis 会将缓冲区内的命令添加进新的 aof 文件。