- 第一范式: 数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组等非原子数据项;
- 第二范式: 要求数据库表的每个实例或记录必须可以被唯一地区分,选取一个能区分每个实体的属性或属性组,作为实体的唯一标识;
- 第三范式: 要求一个关系中不包含已在其它关系已包含的非关键字信息.
属性 | Master/Slave | Master/Master |
---|---|---|
一致性 | Eventually:当你写入一个新值后,有可能读不出来,但在某个时间窗口之后保证最终能读出来.比如:DNS,电子邮件,Amazon S3,Google搜索引擎这样的系统. | 与M/S一样 |
事务 | 完整 | 本地 |
延迟 | 低延迟 | 低延迟 |
吞吐 | 高吞吐 | 高吞吐 |
数据丢失 | 部分丢失 | 部分丢失 |
熔断 | 只读 | 读/写 |
索引是一种用空间换时间的优化策略,把随机io转为顺序io,加快检索效率; 不过索引数量过多会降低写入的性能,因为在表中的数据更改时,所有的索引都将必须进行适当的调整.
在实际操作过程中,设计索引要考虑如下情况:
- 选择唯一性索引;
- 为经常需要排序、分组和联合操作的字段建立索引;
- 为常作为查询条件的字段建立索引;
- 限制索引的数目;
- 尽量使用数据量少的索引;
- 尽量使用前缀来索引;
- 删除不再使用或者很少使用的索引.
- 网络问题
- 任务跑不完,超过了driver的默认链接超时时间(如30s)
- Mongodb宕机了
- 超过了连接空闲时间(connection idle time)被断开
- fd不够用(ulimit设置)
- mongodb最大连接数不够用(可能是连接未复用导致)
- 开发阶段使用explain在写代码的阶段做性能分析;
- profile检测性能慢的语句,便于线上产品问题定位;
无论使用哪种方式定位出来问题,解决办法:
- 根据业务调整schema结构;
- 优化索引以及表分区
Mongoose是mongodb的文档映射模型,主要由Schema,Model和Instance三个方面组成,Schema就是定义数据类型,Model就是把Schema和js类绑定到一起,Instance就是一个对象实例.常见Mongoose操作有save,update,find,findOne,findById,static方法等.
以一个实际的场景来分析:
从A账号中把余额读出来->对A账号做减法操作->把结果写回A账号中; 从B账号中把余额读出来->对B账号做加法操作->把结果写回B账号中;
针对上面这个case,为了数据的一致性,A账号和B账号的处理流程要么都成功执行,要么都失败,而且这个操作的过程中,对A、B账号的其它访问必须锁死,所谓锁死就是要排除其它的读写操作,否则就会出现脏数据(即数据一致性问题).
这个问题并不仅仅出现在数据库操作中,普通的并发以及并行操作都可能导致出现脏数据,避免出现脏数据通常是从架构上避免或者采用事务的思想处理.
- 区别一: 网络IO模型
Memcached是多线程,非阻塞IO复用的网络模型; Redis使用单线程的IO复用模型,自己封装了一个简单的AeEvent事件处理框架,主要实现了epoll、kqueue和select.
- 区别二:内存管理方面
Memcached使用预分配的内存池的方式,使用slab和大小不同的chunk来管理内存; Redis使用现场申请内存的方式来存储数据,并且使用free-list等方式来优化内存分配.
- 区别三:数据一致性问题
Memcached提供了cas命令,保证多并发时操作同一份数据的一致性; Readis:采用事务,保证一串命令的原子性,中间不会被任何操作打断.
主要包括:set/get, hset/hget, publish/subscribe, expire
演示一段redis简单的应用代码:
const redis = require("redis");
const client = redis.createClient();
client.set("foo","some fantastic value");
client.get("foo", function(err, reply) {
console.log(reply.toString());
});
client.end();
以express框架为例:
app.use(mySession({
connection: {host: "127.0.0.1", port: 6379}, // Redis 连接信息maxAge: 3600, // session 的有效期
sessionId: "my.sid" // session ID的cookie名称
}));
// 使用时直接req.session上添加或删除属性即可
const orm = new MyORM({
// mysql 连接信息
connection: {host: "127.0.0.1", port: 3306, user: "root", password: "",
databse: "test"}
});
// 查询
orm.table("xxx").find(query).skip(0).limit(20).then(list => console.log("results", list);)
.catch(err=>console.log(err));
// 更新
orm.table("xxx").update(query, update).then(ret => console.log(ret))
.catch(err => console.log(err));
-
数据结构采用的是B+Tree:
- 每个节点的指针上限为2d而不是2d+1;
- 内节点不存储data,只存储key,叶子节点不存储指针;
mysql对B+Tree进行了优化,增加了顺序访问指针.在B+树的每个叶子节点增加一个指向相邻叶子节点的指针,就形成了带有顺序访问指针的B+Tree.这个目的是为了提高区间访问的性能.
-
索引算法实现
- MyISAM中索引检索的算法:首先按照B+Tree搜索算法进行搜索,如果指定的Key存在,则取出其data域值,然后以data域的值为地址, 读取相应数据记录;
- InnoDB,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录.这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引.因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键.