大数据时代要求:
- 三V:Volume海量、Velocity实时、Variety多样;
- 三高:高并发、高可扩、高性能
高并发操作不建议使用关联查询,而使用冗余数据,分布式系统支持不了太多的并发。
横向 VS 纵向:
- 纵向有极限,好比一台服务器存储资源总是有限度的。
- 横向无极限,好比一群服务器集群形成一个庞大的网络,资源不足时只需要继续增加新的服务器即可。
NoSQL是什么?
Not Only SQL,不仅仅是SQL,泛指非关系型数据库,这种类型数据库存储不需要固定的关系模式,数据之间没有直接关系,更易于横向扩展。由于它的无关系性,数据库结构简单,此类数据库即使是在大数据量的情况下也具有非常高的性能。
NoSQL 与 SQL
- NoSQL无需为要存储的数据建立字段,SQL则需要事先建立不利于修改表结构
- NoSQL以key-value形式存储数据,SQL有固定的表结构
- NoSQL没有预定义查询语言,没有预定义模式
- NoSQL采用CAP原理和最终一致性,SQL则追求ACID属性必须保持严格的一致性
NoSQL数据模型:聚合模型,不同于SQL的ER图表之间的一对多等关系
- KV键值对
- Bson:类似Json的二进制形式的存储格式,支持内嵌的文档及数组对象
- 列族
- 图形
NoSQL数据库四大分类如下图
类别 | 典型应用 | 应用场景 | 数据模型 | 优点 | 缺点 |
---|---|---|---|---|---|
KV键值对 | Redis、Memcache | 内容缓存;大数据量的高负载访问 | k-v(Hashtable) | 查询速度快 | 数据无结构,通常只被当作字符串或者二进制数据 |
文档型数据库 | MongoDB | web应用,基于分布式文件存储的数据库,由C++编写,旨在为web应用提供可扩展的高性能数据存储解决方案,是NoSQL中功能最丰富最像SQL的。 | 通常是Bson,与k-v类似,但value是结构化的数据,不同的是数据库能够了解value的内容 | 数据结构要求不严格;表结构可变,不需要预先定义表结构 | 查询性能不高;缺乏统一的查询语法 |
列存储数据库 | HBase | 分布式文件系统 | 列簇式存储,将同一列数据存在一起 | 查询速度快;可扩展性强;更容易进行分布式扩展 | 功能相对较局限 |
图关系数据库 | Neo4J | 社交网络,专注于构建关系图谱 | 图结构 | 利用图结构相关算法,如最短路径寻址 | 很多时候都需要对整个图做计算才能得到需要的数据 |
分布式数据库原理特性
SQL数据库基本特性ACID
- 原子性:Atomicity,事务里的所有操作要么全部做完,要么都不做
- 一致性:Consistency,数据库要一直处于一致的状态,事务的运行不会改变数据库原本的一致性约束
- 独立性:Isolation,并发的事务之间不会互相影响,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交,它所访问的数据就不受未提交事务的影响
- 持久性:Durability,一旦事务提交后,它所做的修改将会永久的保存在数据库上,即使出现宕机也不会丢失
NoSQL基本特性CAP
- 强一致性:Consistency
- 可用性:Availablity
- 分区容错性:Partition tolerance
- CAP的3进2:在分布式系统中,最多只能实现CAP原理中的两点,而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容错性是必须要实现的。
- CA:传统Oracle、MySQL数据库,可扩展性不高
- AP:大多数网站架构的选择,大多数web应用并不需要强一致性,而需要考虑可用性
- CP:Redis、MongoDB,性能不是特别高
- 经典CAP理论图
- BASE:
- Basically Avaliable:基本可用
- Soft state:软状态
- Eventually consistent:最终一致性
- 它是为了解决关系数据库强一致性引起的问题而再次引起的可用性降低的问题而提出的解决方案,它的思想是通过让系统放松对某一时刻数据一致性的要求来换取系统整体伸缩性和性能上改观。
- 分布式:不同的多台服务器上部署不同的服务模块,通过Rpc/Rmi进行通信和调用,对外提供服务和组内协作
- 集群:不同的多台服务器上部署相同的服务模块,通过分布式调度软件进行统一的调度,对外提供服务和访问
Redis:REmote DIctionary Server(远程字典服务器)
完全开源免费的,使用C语言开发,遵守BSD协议
一个高性能的分布式内存数据库,基于内存运行并支持持久化的NoSQL,也被称为数据结构服务器
特点
- 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启后可以再次加载使用
- 不仅仅支持k-v类型的数据,同时提供list、set、zset、hash等数据结构的存储
- 支持数据的备份,即master-slave模式的数据备份
linux版安装与启动:
- 下载redis包解压,修改其中的redis.conf配置文件里的daemonize为yes,使得服务能在后台启动运行,建议拷贝一份这个文件。然后在redis根目录下执行make命令,需要安装gcc即c语言编译程序,redis使用c语言写的嘛,报错如Jemalloc/jemalloc.h:没有那个文件或目录的话就执行make disclean命令然后再make,完毕之后make test可以不用执行,然后执行make install命令,默认安装目录在/usr/local/bin下,如下图,执行redis-server [redis.conf路径]启动服务,运行ps -ef|grep redis命令查看redis服务是否启动,可以执行redis-benchmark进行性能测试,执行redis-cli -p 6379进入redis客户端,输入ping回车显示pong则成功。关闭单个实例使用redis-cli shutdown,多实例关闭需要指定端口号:redis-cli -p 6379 shutdown。
Redis是单进程的,处理速度完全取决于主线程的执行效率,默认16个数据库,统一密码管理,使用select 切换,下标从0开始,使用dbsize查看当前数据库数量,使用Flushdb清空当前库,Flushall清空所有库。
五大数据类型
- String:字符串,redis中最基本的类型,是二进制安全的,即可以包含图片或者序列化对象等任意对象,最多支持512M大小。
- Hash:哈希,是一个键值对集合,但V是一个键值对类似于java里的Map<String, Map<String,Object>>
- List:列表,是简单的String列表,按照插入顺序排序,底层是链表
- 如果键不存在,则创建新的链表;
- 如果键已存在,则新增内容;
- 如果键全部移除,对应的键也就消失了
- 链表的头尾操作效率都很高,但操作中间元素的效率就比较低了。
- Set:集合,无序的,是通过Hashtable实现的
- ZSet(sorted set):有序集合,不同于set的是每个元素前都会带有一个double类型的可重复的score,根据score进行排序。
Redis常用命令操作:http://redisdoc.com
Key
- keys *:查询所有key
- exists [key]:判断key是否存在
- move [key] [dbid]:将key移动到其它库
- expire [key] [time]:为指定的key设置过期时间,单位秒
- ttl [key]:查看key剩余多少秒过期,-1表示永不过期,-2表示已过期(包括不存在的key)
- type [key]:查看key的类型
String
- set/get/del/append/strlen:设置、获取、删除、添加、求长度
- Incr/decr/incrby/decrby:数值加减1或指定数值
- getrange/setrange:类似于between ... and,0到-1表示全部,set设置指定下标开始的值
- setex(set with expire):设置值时同时设置过期时间,键秒值
- setnx(set if not exist):不存在才设置新值
- mset/mget/msetnx:同时设置一个或多个k-v对
- getset(先get再set):设置新值返回旧值
Hash
- hset/hget/hmset/hmget/hgetall/hdel/hlen
- hexists key 在key里面的某个值的key
- hkeys/hvals
- hincrby/hincrbyfloat
- hsetnx
List
- lpush/rpush/lrange
- lpop/rpop
- lindex,按照索引下标获得元素(从上到下)
- llen
- lrem key 删N个value
- ltrim key 开始index 结束index,截取指定范围的值后再赋值给key
- rpoplpush 源列表 目的列表:移除列表的最后一个元素,并将该元素添加到另一个列表并返回
- lset key index value
- linsert key before/after v1 v2:在list某个已有值的前后再添加具体值
Set
- sadd/smembers/sismember
- scard,获取集合里面的元素个数
- srem key value 删除集合中元素
- srandmember key 某个整数(随机出几个数)
- spop key 随机出栈
- smove key1 key2 在key1里某个值 作用是将key1里的某个值赋给key2
- 差集:sdiff;交集:sinter;并集:sunion
Zset
- zadd/zrange 0 -1 withscores
- zrangebyscore key 开始score 结束score
- zrem key 某score下对应的value值,作用是删除元素
- zcard/zcount key score区间/zrank key values值,作用是获得下标值/zscore key 对应值,获得分数
- zrevrank key values值,作用是逆序获得下标值
- zrevrange
- zrevrangebyscore key 结束score 开始score
配置文件
Units:单位,配置大小单位,只支持bytes,不支持bit,忽略大小写
INCLUDES:可以包含其它的配置文件
GENERAL:通用
- Daemonize
- Pidfile
- Port
- Tcp-backlog
- Timeout
- Bind
- Tcp-keepalive
- Loglevel
- Logfile
- Syslog-enabled
- Syslog-ident
- Syslog-facility
- Databases
SNAPSHOTTING:RDB文件的快照
- Save,是1分钟内改了1万次,或5分钟内改了10次,或15分钟内改了1次,不设置任何save指令或空串即可禁用
- Stop-writes-on-bgsave-error,配置成no说明和略数据不一致
- rdbcompression
- rdbchecksum
- dbfilename
- dir
REPLICATION:复制
SECURITY安全:访问密码的查看、设置和取消
LIMITS限制:
- Maxclients,默认10000个客户端
- Maxmemory,最大内存
- Maxmemory-policy,6种移除规则
- Maxmemory-samples,样本数量
APPEND ONLY MODE:追加
- appendonly,默认为no,改为yes就打开AOF持久化
- appendfilename,AOF文件名
- Appendfsync
- No-appendfsync-on-rewrite:重写时是否可以运用Appendfsync,用默认no即可,保证数据安全性。
- Auto-aof-rewrite-min-size:设置重写的基准值
- Auto-aof-rewrite-percentage:设置重写的基准值
持久化
RDB:
- Redis Database,指在指定的时间间隔内将内存中的数据集快照写入磁盘,即Snapshot快照,恢复时直接将快照文件读到内存中。Redis会创建(Fork)一个独立的子进程来进行持久化操作,先将数据写入到临时文件持久化结束后进行替换,整个过程中,主进程不需要进行人和IO操作,确保了性能。
- Fork:就是复制一个和当前主进程完全一样的子进程,子进程的所有数据都和原进程一致,消耗的资源也会翻倍。
- RDB默认保存的是dump.rdb文件,恢复只需将文件放进redis安装目录并启动服务即可
- 触发条件:
- 根据配置文件中的规则进行
- 使用命令save阻塞其它一切操作,或bgsave进行异步操作并可以通过lastsave获取最后一个快照执行成功时间
- 执行flushall命令,但内容为空毫无意义
- 动态停止:redis-cli config set save ""
- 优点:
- 适合大规模的数据恢复,速度较快
- 对数据完整性和一致性要求不高
- 缺点:
- 在一定时间间隔内备份一次,如果服务意外停止会丢失最后一次快照后的所有修改
- Fork的时候,内存中的数据被克隆了一份,2倍的资源膨胀性需要考虑
AOF:
- Append Only File,以日志的形式记录所有写操作,只允许追加文件appendonly.conf,redis启动时会加载此文件重新构建数据,如有异常则无法启动,需要先进行修复,redis-check-aof --fix。
- Rewrite,redis为防止文件过大增加的重写机制,aof文件大小超出阈值时进行压缩只保留可以恢复数据的最小指令集,可以使用命令bgrewriteaof,重写类似于快照,也是Fork出一个新的进程,遍历内存数据重写AOF文件
- 优点:
- 每修改同步:appendfsync always,同步持久化,每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好
- 每秒同步:appendfsync everysec,异步操作,每秒记录,如果一秒内宕机,有数据丢失
- 不同步:appendfsync no,从不同步
- 缺点:
- 相同数据集的数据而言AOF文件要远大于RDB文件,恢复速度慢于RDB
- AOF运行效率要慢于RDB,每秒同步策略效率较好,不同步效率和RDB相同
Which One?
- 如果只做缓存,即只希望数据在服务器运行的时候存在,也可以不使用任何持久化方式。
- 同时开启两种持久化方式,会优先载入AOF文件,因为数据更完整,但也要保留RDB,因为备份还原更快,以防万一,因此不需要那么多的保存规则,只保留15分钟备份一次就够了。
- 如果不Enable AOF ,仅靠Master-Slave Replication 实现高可用性也可以。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。
事务
开启事务:MULTI
入队:将执行的操作放入队列queue中,执行时按队列顺序地串行化执行而不会被其它命令插入,不许加塞
执行种类:
- 正常执行:EXEC
- 放弃事务:DISCARD
- 全体连坐:开启事务后,执行命令出错则在提交事务时会报错
- 冤头债主:开启事务后,执行命令时不报错但不符合语法导致提交事务时该语句报错,不会影响其它操作
- WATCH [key] 监控:
- 悲观锁:就是很悲观,认为获取数据时一定会被别人修改,就对数据加锁,如传统数据库的表锁、行锁、读锁、写锁等,都是在操作之前上锁
- 乐观锁:就是很乐观,认为不会有人修改自己正在查询的数据,不会加锁,但在更新时会去匹配数据的版本号,新提交的版本号必须大于当前版本才能执行更新
- 使用WATCH命令对key进行监控,类似于乐观锁,如果key被别的事务修改了,则当前事务执行失败返回nil(Nullmulti-bulk),使用UNWATCH取消监控,执行EXEC后会取消之前的监控锁
特性:
- 单独的隔离操作,所有命令都会被序列化按照队列顺序执行,不会被其它客户端发来的请求打断
- 没有隔离级别的概念,事务提交之前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题
- 不保证原子性,事务执行时如果有一条命令失败,其它命令仍然会被执行,没有回滚操作
复制:Master/Slave,主从复制、读写分离
主机数据更新后根据配置和策略自动同步数据到从机上,Master以写为主,Slave以都为主
原理:Slave启动成功连接到Master后会发送一个sync指令,master接收到启动后台的存盘进程,同时收集所有修改数据命令,执行完毕后将传送给Slave,完成一次全量复制,之后进行增量复制,同步master收到的修改命令,但只要重新连接master则就会自动执行一次全量复制。
使用:
- 配从(库)不配主(库)
- 从库配置:slaveof [主库ip] [主库端口],每次与master断开连接后都需要重新连接,除非修改redis.conf
- 使用info replication查看复制信息
- 一主二从:一个Master两个Slave,主机断开连接后从机等待主机,主机重启后从机继续复制
- 薪火相传:上一个Slave可以是下一个Slave的Master,中途变更转向则会清楚之前数据重新复制新的
- 反客为主:slaveof no one,使当前数据库停止与其它数据库同步,转成主数据库
- 哨兵模式:反客为主的自动版,自动监控主机状态,一旦挂机就根据投票数选择一个从库转为主库,能够监控多个master
- 在redis.conf所在目录下新建sectinel.conf文件,配置如下信息:
- sentinel monitor [随意起个被监控数据库名字] [ip] [port] 1
- 最后一个数字1表示主机挂掉后投票选择让谁接替成为主机
- 启动哨兵:redis-sentinel [sectinel.conf路径]
- 在redis.conf所在目录下新建sectinel.conf文件,配置如下信息:
复制缺点:复制延时,由于所有写操作都在Master上操作,再同步到Slave,当主机繁忙时,延迟会更加严重,Slave机器数量的增加也会使延时问题更加严重。
修改bin文件夹的权限,否则无法生存dump.rdb文件
修改 /etc/sysctl.conf 文件 添加 vm.overcommit_memory=1然后重启或者执行命令:sysctl vm.overcommit_memory=1
执行命令:sysctl -w fs.file-max=100000,调整最大文件数量
使用jedis操作redis:
import redis.clients.jedis.*;
public class TestRedis {
@Test
public void testTx() throws InterruptedException {
Jedis jedis = new Jedis("192.168.43.178",6379);
jedis.set("balance", "100");
jedis.set("debt", "0");
jedis.watch("balance");
// Thread.sleep(5000);
/**/
if (Integer.parseInt(jedis.get("balance")) < 50) {
System.out.println("error");
jedis.unwatch();
return;
} else {
Transaction tx = jedis.multi();
tx.incrBy("debt", 50);
tx.decrBy("balance", 50);
tx.exec();
System.out.println("success");
}
System.out.println(jedis.get("balance"));
System.out.println(jedis.get("debt"));
}
@Test
public void testMasterSlaver() throws InterruptedException {
Jedis jedis_m = new Jedis("192.168.43.178",6379);
Jedis jedis_s = new Jedis("192.168.43.178",6380);
jedis_s.slaveof("192.168.43.178", 6379);
jedis_m.set("kkk", "master");
Thread.sleep(1000);
System.out.println(jedis_s.get("kkk"));
}
@Test
public void testPool() throws InterruptedException {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(1000);
jedisPoolConfig.setMaxIdle(50);
jedisPoolConfig.setMaxWaitMillis(60 * 1000);
jedisPoolConfig.setTestOnBorrow(true);
JedisPool jedisPool = new JedisPool(jedisPoolConfig, "192.168.43.178",6379);
Jedis jedis = jedisPool.getResource();
try {
jedis.set("pool", "cool");
System.out.println(jedis.get("pool"));
} catch (Exception e) {
e.printStackTrace();
} finally {
jedis.close();
jedisPool.returnResourceObject(jedis);
}
}
}
原文地址:https://www.cnblogs.com/mabaoqing/p/10368944.html