NoSql数据库
Posted coderlin_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NoSql数据库相关的知识,希望对你有一定的参考价值。
NoSql数据库,非关系型
- 易扩展,高性能,高可用
- 较容易映射复杂数据(key-value)
- 无事务特性要求(ACID特性)
NoSQL设计
- 常见的场景及设计方法(内嵌,父子引用,反范式)
- 双向关联的场景及设计原则。
内嵌
存在关联关系的文档,放在同一文档中,以数组的形式存放。
- 减少了关联查询,只需要一句sql语句就可以查询很多数据
- 适合单类需要描述的属性
- 不经常变化的属性(扩展,嵌套关联)
父子引用
- 父引用是指存在一对多的情况中,放在同一文档中,以数组的形式存放。
课程对应的多个课程ID,以数组形式存放。 - 子引用是指存在一对非常多的情况中,由于数据库文档存放限制,使用反向引用。
课程ID存储在cid上。
父子引用设计: - 引用数据内容是否非常多
- 引用数据量是否非常庞大,而且在增加
- 数据是否需要单独访问
反范式
- 范式是指按既定的用法,范式就是一种公认的模型或者模式。反范式=》不走寻常路
不创建用户表,将用户数据直接塞入users中。
反范式设计: - 是否有提升性能的区间
- 数据量的变化是否非常庞大,庞大到更新会异常低效。
- 先考虑读写比,才考虑反范式,如果需要经常读写,就不适用。
Redis数据库
高性能key-value数据库。支持数据持久化,多数据结构,list,set等的存储,支持数据备份。
特点
- 高性能,持久化
- kye-value结构,支持多种数据类型
- 支持事务,数据的原子性(要么做,要么不做)
应用场景: - 缓存(读写性能优异)
- 计数和消息系统(高并发,发布、订阅阻塞队伍功能)、
Redis存储结构
Redis以字典结存储数据,并允许其他应用通过TCP协议读写字典中的内容。
如
direct[key] = value
Rdis字典中的键值除了可以是字符串,还可以是其他数据类型,比如hash,list,Set, ZSet等
内存存储与持久化
Redis数据库中所有数据都存储在内存中,读写速度更快,通常用redis做缓存数据库。
存储在内存中可以回导致程序退出导致数据丢失,但是redis提供了持久化支持,即可以将内存中的数据异步写入到硬盘中。
功能丰富
redsi虽然作为数据库开发,但是其提供了丰富的功能。如
- 作为缓存系统。Redis可以为每个键设置生存时间,过期自动删除。可以限定数据占用最大空间,达到限制后按照一定规则自动淘汰不需要的键。
- 作为队列系统。Redis的列表类型键可以用来实现队列,支持阻塞式读取。实现一个高性能优先级队列。
- 发布订阅功能。Redis支持发布订阅的消息模式,可以基于此构建聊天室等系统。
简单稳定
对于sql语句,从posts表获取id为1的纪律
SELECT title FORM posts WHERE id=1 LIMIT 1
对于redis命令行,取键名为post:1的hash的title字段的值
HGET post:1 title
应用场景
- 缓存系统。
- 排行榜,如京东的月度销量榜单
- 计数器,比如视频网站的播放次数等等。
- 分布式会话。session不再由容器管理,而是由redis搭建的session服务及内存数据库管理。
- 分布式锁、
- 社交网络。点赞,踩等功能。
- 最新列表。LPUSH可以在列表头部插入,LTIRM可以用来限制列表数量。这样列表永远为N个,无需查询。
- 消息系统,Redis提供简单的消息队列系统。
docker安装redis
可以docker-compose.yml
version: '3'
services:
redis-test:
image: "redis"
restart: "always"
container_name: "redis-test"
ports:
- 15001:6379
volumes:
- /home/redistest:/data
command: ["redis-server", "--requirepass", "123456"]
也可以通过命令行的方式
dokcer run -itd --restart=always --name redis-test -p 15001:6379 -v /home/redistest:/data redis redis-server --requirepass=123456
Redis中的多数据库
Redis默认支持16个数据库,分别是0,1,2…15.
- Redis不支持自定义数据库名字。
- 每个数据库都以编号命名。
- 可以通过配置参数databases修改支持的数据库个数。
Redis常见cli命令行工具
Redis不是简单的键值存储,实际上,他是一个数据结构服务器,支持不同类型的值。字符串补给你可以关联字符串,还可以关联Hash,List, Set, ZSet, Bit arrays等等。
参考网站
进入reids容器
docker exec -it redis-test /bin/bash
redis-cli
密码登录
127.0.0.1:6379> auth xxx(密码)
Redis中的键
Redis密钥是二进制的,这也就意味着可以使用任何二进制序列作为key。从字符串到JPEG文件的内容,空字符串也是有效的键。
Redis的数据类型
字符串
- 可以存储任何形式字符串,包括二进制。
- 可以存储用户邮箱,JSON化的对象,甚至一张图片。
- value最多可以容纳数据大小为512MB。
- 字符串是其他四种常见数据类型的基础。比如列表类型就是以列表的形式组织字符串…
设置
SET key value //设置值
GETSET key value //设置值,并返回key旧值
SHTNX key value // 当key不存在才会创建
MSET key value [key value ...] // 同时设置多个
MSETNX key value [key value] //当指定key不存在才会创建。 支持多个。
APPEND key value
查询
get key //查看key
getrange key 0 3 //返回key字符串的0到3的字符
mget key [key..] //获取多个key
strlen key //key的长度
exists key [key..] //是否有指定的key
type key //key的类型
Redis的数字值
数字值在Redis中以字符串保存。
incr key // Key+1
incrby key 3 //Key+3
incrbyfloat key 3 //Key+3浮点数
decr key // Key-1
decrby key 3 //Key -3
哈希(HASH)
哈希类型是一种字典结构,可以存储字段和字段值的映射,但是字符值只能是字符串,不能是其他数据类型。
如
const user =
name: 'jacl',
age: 12
usr就是键,name是字段,jacl是字段值的映射。
hset key field value [field value ...] // 设置user,字段field ,值为value
hmset key field value [field value ...] // 同时设置多个
hsetnx key field value // 只有field不存在才会设置
查询
hkeys key //查看key的所有字段
hlen key // 查看key字段数量
hmget key field1 [field2...] //获取所有给定字段的值
hget key field
hgetall key //全部字段和值
hexists key field //判断key的field字段存在?
hvals key // key的所有值
hscan key cursor [MATCH pattern] [COUNT coun] //迭代哈希表中的键值对
修改
hsset key field value [field value...]
hincrby key field 3 //为key的field字段+3
删除
hdel key field [field]
DEL key [key..] //删除整个HASH
列表list
类似于数组,可以存储一个有序的字符串列表。常用的就是向列表两端添加元素。获取列表的某个字段。
内部使用双向链表实现,所有向列表两端添加元素的时间复杂度为o(1),查找两端也是非常快,很适合像新鲜事这些。
lpush key element[element ...] 插入到列表头部 比如a,b,c。那么顺序就是c b a 。 先插入的就到最后面
linsert key before|after pivot value 在某个元素前后插入元素
lpushx key value 将value插入到头部
lset key index value 通过索引设置值
rpush key value [value2] 添加多个值 往右边也就是后面插,
rpushx key value 为已存在的列表添加值。
查询
lindex key index 索引
llen key //长度
lrange key start stop 范围
删除
lpop key //从左侧删除第一个元素并返回
rpop key //删除最后一个并返回。
lrem key count 2
// count > 0,从头向尾遍历删除值为2,删除最大次数是count个。count = 0 删除所有匹配个元素。
// count < 0,从后面开始匹配删除值为2的,删除最大次数是count个
集合
集合类型和数学中的集合概念相似。集合中的元素是唯一的,无序的,简单理解就是没有顺序且不重复的列表。一个集合类型可以存储至多2的32次方-1个字符串
- 列表是有序的,集合是无序的。
- 列表数据可以重复,集合中没有重复数据。
- 集合在redis内部使用值为空的散列表(hash)实现的,操作的时间复杂度都是o(1)
- 最方便的是多个集合可以进行并集,交集和差集运算。
添加
sadd key merber1 [merber2]
查询
smebers key //返回key所有成员
scard key //成员数
sismember key member //判断member是否是key的成员
srandmember key [count] //返回集合中一个或者多个随机数。
删除
srem key member1 [member2...]
spop key //删除随机元素并返回
smove a b member //从a往b中,将member移到b
集合间运算
并集,交集,差集。
sdiff key1 [key2] // 差集
sinter key1 [key2] 交集
sunion key1 [key2] 并集
sdiffstore a key1 [key2] // 差集存储到a
sinterstore a key1 [key2] // 交集到a
sunionstore a key1 key2 //并集到a
集合使用场景
- 跟踪唯一性数据。比如访问网站的唯一ip地址信息。
- 充分利用set聚合操作方便搞笑的特性,维护数据对象之间的关联关系。如所有购买A商品的客户ID存储到指定的SET中,购买B的存储到SET2中,取交集就可以轻松查找购买A和购买B的人
有序集合
有序集合是一中类似集合和哈希之间的混合数据类型。
- 与集合一样,有序集合由唯一的非重复性字符串元素组成。
- 有序集合中的元素不排序,但是,有序集合的每个元素关联一个分数,分数是有序的。
- 虽然集合中每个元素都是不同的,但是他们的分数可以相同。如
键 ---> 张三(元素) -> 10 (分数)
---> 李四(元素) -> 15 (分数)
与列表对比:
- 两者都是有序的,两者都可以获取某一范围的元素。
- 列表类型通过链表实现,获取靠近两端的数据速度极快,访问中间就慢,适合新鲜事
- 有序集合类似使用哈希表实现的,读取中间部分的数据也很快。
- 列表中不能简单地调整某个元素的位置,但是有序集合可以。通过改变分数。
- 有序集合比列表更耗费内存。
有序集合典型场景:
- 排行榜
- 微博热搜
添加 Z开头
zadd key score member [ socre member] 向有序集合添加一个或多个成员,或者更新分数
查询
zrange key start stop WITHSCORES,读取key的start到stop位置的成员,默认分数从递到高排序,
zrevrange key start stop WITHSCORES,从高到底排序
zrangebyscore key 60 90 //查询key中分数60-90的成员 低到高
zrevragebyscore key 60 90 //高到低
zrank key a // a的排名,从低到高
zrevrank key a // 从高到低
zcard key //个数
zscore key member // 查看分数
zcount key min max //计算min到max中有多少个。
修改
zadd key score member [ score member ...]
zincrby key 3 member // 往key中的member的分数加3
删除
zrem key member
zremrangebyrank key start stop //删除指定区间从低到高排名
zremrangebyscore key min max //删除指定分数的成员
通用命令
keys *// 返回所有key
keys my* 返回以my开头的key
exists key key ... 判断存在
rename key newkey 重命名
randomkey //随即返回一个key
flushdb 清空当前数据库所有美容
flushall 清空所有数据库内容
设置过期时间
在redis中可以设置一个键的过期时间,到时间后,Redis会自动删除它。
expire key 10 // key 10s
pexire key 10 // kee 10ms
expireat key timestamp // 时间戳
pexpireat key milliseconds-timestamp
- 前三个最终都会使用pexpireat 来执行。
- 如Expire设置,调用Pexire将n转为m毫秒。获取当前的Unix时间单位,将当前的时间单位加上m毫秒传递给pexpireat
- 给键设置过期时 间,时间保存在字典了,也是键值结构。
ttl key //以秒为单位,返回剩余时间
pttl key //毫秒
如果 返回-2,过期且删除。-1,没有过期时间。
persist key //清除过期时间
使用set或者getset命令为键赋值,会同时清除键的过期时间,其他操作不会,如Incr, Lpush。
Redis事务 ACID
- A (Atomicity)原子性,事务要么全部完成,要么全部取消,如果事务崩溃,状态将回到事务之前(事务回滚)
- I (lsolation)隔离性,如果2个事务,T1和T2同时执行,事务T1 T2的最终结果是相同的,不管谁先执行完毕。
- D 持久性 一旦事务提交,不管发生什么,数据要保存在数据库。
- C 一致性 只有合法的数据,才能写入数据库。
命令
multi 标记一个事务块的开始,可以开始编写命令了
exec 执行所有事务块内的命令
127.0.0.1:6379> set jack 10
OK
127.0.0.1:6379> set rose 20
OK
127.0.0.1:6379> multi //开始事务
OK
127.0.0.1:6379(TX)> decrby jack 5 //命令入队
QUEUED
127.0.0.1:6379(TX)> incrby rose 5 //命令入队
QUEUED
127.0.0.1:6379(TX)> exec // 执行事务
1) (integer) 5
2) (integer) 25
discard 取消事务,放弃事务块内所有命令
事务中的错误处理
事务块中的命令出错
- 1 语法错误,错误之后会直接报错,有语法错误的命令,事务不会执行。
- 2 运行错误,如果一条命令运行的时候错误了,其他命令依然会执行。
事务中的WATCH命令
WATCH定义:监视一个或者多个key,在事务执行之前若这个key被其他命令所改动,那么事务将被打断。
watch key [key..]
unwatch
Redis持久化
- RDB方式的持久化通过快照完成。当符合一定条件时,Redis会自动将内存中的所有数据生成一份副本并存储在硬盘上,这个过程即为快照。
- 两个参数,时间窗口M和改动的键的个数N,当时间M内,更改键的个数大于N的时候,即符合自动快照条件。
redis的默认配置
save 900 1 //900s内之至少有1个key变化了,则写入快照
save 300 10 // 300秒内至少有10个key变化了,则写入快照
save 60 10000
dbfilename dump.rdb // 快照保存的文件名称
dir ./ //快照文件保存路径
RDB快照的运行方式是异步进行的,在保存快照期间依然能够提供客户端端请求。
fork一个子进程,原来的redis进程继续处理客户端,子进程负责将数据保存到磁盘,然后退出
AOF持久化
- 快照并不是非常持久,如果redis因为某些原因造成故障,那么服务器将丢失最近写入,且仍未保存到快照的哪些数据。
- 对于追去完全持久化的程序,快照就不太实用了。
- AOF可以将Redis执行的每一条写命令操作日志,存储到硬盘文件中,
- AOF机制对于日志的写入操作是append模式,追加模式
- 默认没有开启AOF,可以通过appendonly yes开启
- AOF文件的保存位置和RDB文件位置相同,默认是appendonly.aof,可以通过appendfilename 'xxx’来修改。
AOF三种同步策略
appendfsync always 每一次数据变化,都会同步到硬盘中。
appendfsync everysec 每一s再同步
appendfsync no 关闭AOF
默认是appendfsync everysec 。
AOF对比RDB
RDB优点:
- 文件小
- 异步备份,性能好
- 恢复大数据集速度比AOF块。
缺点:
- 数据安全性低,容易丢失。
- 数据量比较大的时候备份速度满。
AOF优点:
- 数据安全性高
- 有利于开发分析
缺点:
- 相同数据比RDB文件大。
- 根据所使用的fsync策略,AOF的速度可能会慢于RDB。
使用node.js操作redis
- ioredis库。支持集群,前哨,流,流水线等。高性能。异步API…
import Redis from 'ioredis'
// 建立连接
const redis = new Redis(
port: '15001',
host: "106.55.237.178",
password: "123456",
as any)
// 操作
// redis.set('testtest', 'hahaha', (err, result)=>
// if(err)
// console.log('操作失败');
// return
//
// console.log('操作成功');
// console.log(result);
// )
async function main()
const res = await redis.get('testtest')
console.log('res', res);
main()
管道Pipelining
如果要发送一批命令,则可以使用流水线将命令在内存中排队,然后一次全部打送到Redis。
- redis.pipeline()创建一个Pipeline实例。在实例上调用任何redis命令,这些命令会在内存中排队,哦通过调用exec方法发送给REdis。有点像事务操作,但他只是Redis提供的客户端功能。
const pipe = redis.pipeline()
pipe.set('hahah', 'fiii')
pipe.get('hahah', (err, result)=>
console.log(result);
)
pipe.exec()
// 还可以
redis.pipeline().set('test19', 'hahahah').del('hahah').get('hahah',(err, result)=>
console.log('result', result);
).exec()
事务操作
事务命令multi和exec一般与管道一起使用。因此,在调用multi的时候,默认会创建pipeline实例,因此可以像使用管道一样使用multi、
redis.multi().set('test20', 'hahahah').del('test19').get('test20',(err, result)=>
console.log('result', result);
).exec()
//result QUEUED返回值,不跟管道一样,事务每个链接的回调是将排队状态传递给回调,而不是结果、
如果有语法错误,不会执行。
- 要使用不带管道的事务,就要添加pipeline:false,那么每个命令将立即发送给Redis,无需等待exec调用。
- 内联事务
redis.pipeline().get('foo').multi().set('foo', 'bar').get('foo').exec().get('foo').exec()
错误处理
使用node操作mongodb
node操作Mongodb基本操作
const mongoose = require("mongoose");
mongoose.connect(
"mongodb://账号:密码服务器地址:27017/blob?authSource=admin",
useNewUrlParser: true
);
const User = mongoose.model("users",
name: String,
age: Number,
email: String,
);
const test = new User( name: "123", age: 12, email: "123" );
test.save().then(() =>
console.log("save Ok!");
);
以上是关于NoSql数据库的主要内容,如果未能解决你的问题,请参考以下文章