深度解析SQL和NoSQL数据库,掌握主流数据库【两万字解析】

大家好,我是辣条。

大家国庆玩的快乐否,玩开心的兄弟萌评论扣1,觉得不怎么样的兄弟萌扣2,我希望看到大家都是扣1呦,觉得这篇文章对你有帮助的话可以给辣条一个三连呦。

目录

数据库高级特性

一、存储引擎

存储引擎的选择

使用引擎

二、关系与外键

关系

外键

数据库事务及其他

一、事务

3. 语法与使用

4. 示例

二、存储过程

语法

使用

三、Python操作

四、sql注入

五、数据备份与恢复

Redis 与 MongoDB

一、NoSQL概述

二、Redis 入门

1. Redis简介

2. Redis的应用场景

3. Redis的安装和配置

4. Redis 的配置

5. Redis的服务器和客户端

6. Redis持久化

7. 在Python程序中使用Redis

三、MongoDB概述

2. MongoDB的安装和配置

3. MongoDB基本概念

4. 通过Shell操作MongoDB

5. 在Python程序中操作MongoDB

领取 


数据库高级特性


一、存储引擎

存储引擎就是如何存储数据、如何为数据建立索引和如何更新、查询数据等技术的实现方法。

MySQL 默认支持多种存储引擎,以适用于不同领域 的数据库应用需要,用户可以通过选择使用不同的存储引擎提高应用的效率,提供灵活的存储。


查看当前的存储引擎

show variables like '%storage_engine';
show engines;


MySQL 常用的存储引擎

  1. InnoDB

    事务型数据库的首选引擎,支持事务安全表(ACID),支持行锁定和外键,InnoDB是默认的MySQL引擎。

    InnoDB主要特性有:

    1. InnoDB 给 MySQL 提供了具有提交、回滚、崩溃恢复能力的事务安全存储引擎。
    2. InnoDB 是为处理巨大数据量的最大性能设计。它的 CPU 效率比其他基于磁盘的关系型数据库引擎高。
    3. InnoDB 存储引擎自带缓冲池,可以将数据和索引缓存在内存中。
    4. InnoDB 支持外键完整性约束。
    5. InnoDB 被用在众多需要高性能的大型数据库站点上
    6. InnoDB 支持行级锁
  2. MyISAM

    MyISAM 基于 ISAM 存储引擎,并对其进行扩展。它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。MyISAM 拥有较高的插入、查询速度,但不支持事物。

    MyISAM主要特性有:

    1. 大文件支持更好
    2. 当删除、更新、插入混用时,产生更少碎片。
    3. 每个 MyISAM 表最大索引数是64,这可以通过重新编译来改变。每个索引最大的列数是16
    4. 最大的键长度是1000字节。
    5. BLOB和TEXT列可以被索引
    6. NULL 被允许在索引的列中,这个值占每个键的0~1个字节
    7. 所有数字键值以高字节优先被存储以允许一个更高的索引压缩
    8. MyISAM 类型表的 AUTO_INCREMENT 列更新比 InnoDB 类型的 AUTO_INCREMENT 更快
    9. 可以把数据文件和索引文件放在不同目录
    10. 每个字符列可以有不同的字符集
    11. 有 VARCHAR 的表可以固定或动态记录长度
    12. VARCHAR 和 CHAR 列可以多达 64KB
    13. 只支持表锁
  1. MEMORY

    MEMORY 存储引擎将表中的数据存储到内存中,为查询和引用其他表数据提供快速访问。


存储引擎的选择

一般来说,对插入和并发性能要求较高的,或者需要外键,或者需要事务支持的情况下,需要选择 InnoDB,

插入较少,查询较多的场景,优先考虑 MyISAM。


使用引擎

一般在建表时添加

create table abc (
    name char(10)
) engine=MyISAM charset=utf8;
​
create table xyz (
    name char(10)
) engine=InnoDB charset=utf8;


InnoDB 和 MyISAM 在文件方面的区别

  1. InnoDB 将一张表存储为两个文件

    • demo.frm -> 存储表的结构和索引
    • demo.ibd -> 存储数据,ibd 存储是有限的, 存储不足自动创建 ibd1, ibd2
    • InnoDB 的文件创建在对应的数据库中, 不能任意的移动
  2. MyISAM 将一张表存储为三个文件

    • demo.frm -> 存储表的结构
    • demo.myd -> 存储数据
    • demo.myi -> 存储表的索引
    • MyISAM 的文件可以任意的移动


二、关系与外键


关系

  • 一对一

    • 在 A 表中有一条记录,在 B 表中同样有唯一条记录相匹配
    • 比如: 学生表和成绩表
  • 一对多 / 多对一

    • 在 A 表中有一条记录,在 B 表中有多条记录一直对应
    • 比如: 博客中的用户表和文章表
  • 多对多

    • A 表中的一条记录有多条 B 表数据对应, 同样 B 表中一条数据在 A 表中也有多条与之对应
    • 比如: 博客中的收藏表


外键

外键是一种约束。他只是保证数据的一致性,并不能给系统性能带来任何好处。

建立外键时,都会在外键列上建立对应的索引。外键的存在会在每一次数据插入、修改时进行约束检查,如果不满足外键约束,则禁止数据的插入或修改,这必然带来一个问题,就是在数据量特别大的情况下,每一次约束检查必然导致性能的下降。

出于性能的考虑,如果我们的系统对性能要求较高,那么可以考虑在生产环境中不使用外键。

  1. 构造数据

    -- 用户表
    create table `user` (
        `id` int unsigned primary key auto_increment,
        `name` char(32) not null
    ) charset=utf8;
    ​
    -- 商品表
    create table `product` (
        `id` int unsigned primary key auto_increment,
        `name` char(32) not null unique,
        `price` float
    ) charset=utf8;
    ​
    -- 用户信息表: 一对一
    create table `userinfo` (
        `id` int unsigned primary key auto_increment,
        `phone` int unsigned unique,
        `age` int unsigned,
        `location` varchar(128)
    ) charset=utf8;
    ​
    -- 用户组表: 一对多
    create table `group` (
        `id` int unsigned primary key auto_increment,
        `name` char(32) not null unique
    ) charset=utf8;
    ​
    -- 订单表: 多对多
    create table `order` (
        `id` int unsigned primary key auto_increment,
        `uid` int unsigned,
        `pid` int unsigned
    ) charset=utf8;

  2. 添加外键

     
    -- 为 user 和 userinfo 建立关联的外键
    alter table userinfo add constraint fk_user_id foreign key(id) references user(id);
    ​
    -- 建立用户与组的外键约束
    alter table `user` add `gid` int unsigned;
    alter table `user` add constraint `fk_group_id` foreign key(`gid`) references `group`(`id`);
    ​
    -- 建立用户、商品、订单的外键约束
    alter table `order` add constraint `fk_user_id` foreign key(`uid`) references `user`(`id`);
    alter table `order` add constraint `fk_prod_id` foreign key(`pid`) references `product`(`id`);

  3. 尝试插入数据后在删除,分别先对主表和子表进行一次删除。

  4. 删除外键。

    alter table `表名` drop foreign key `外键名`

深度解析SQL和NoSQL数据库,掌握主流数据库【两万字解析】_第1张图片

数据库事务及其他


一、事务


1. 事务简介

事务主要用于处理操作量大、复杂度高、并且关联性强的数据。

比如说, 在人员管理系统中, 你删除一个人员, 你即需要删除人员的基本资料, 也要删除和该人员相关的信息, 如信箱, 文章等等, 这样, 这些数据库操作语句就构成一个事务!

在 MySQL 中只有 Innodb 存储引擎支持事务。

事务处理可以用来维护数据库的完整性, 保证成批的 SQL 语句要么全部执行, 要么全部不执行。主要针对 insert, update, delete 语句而设置。


2. 事务四大特性

在写入或更新资料的过程中, 为保证事务 (transaction) 是正确可靠的, 所必须具备的四个特性 (ACID):

  1. 原子性 (Atomicity) :

    • 事务中的所有操作, 要么全部完成, 要么全部不完成, 不会结束在中间某个环节。
    • 事务在执行过程中发生错误, 会被回滚 (Rollback) 到事务开始前的状态, 就像这个事务从来没有执行过一样。
  2. 一致性 (Consistency):

    在事务开始之前和事务结束以后, 数据库的完整性没有被破坏。 这表示写入的资料必须完全符合所有的预设规则, 这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。

  3. 隔离性 (Isolation):

    并发事务之间互相影响的程度,比如一个事务会不会读取到另一个未提交的事务修改的数据。在事务并发操作时,可能出现的问题有: 脏读:事务A修改了一个数据,但未提交,事务B读到了事务A未提交的更新结果,如果事务A提交失败,事务B读到的就是脏数据。 不可重复读:在同一个事务中,对于同一份数据读取到的结果不一致。比如,事务B在事务A提交前读到的结果,和提交后读到的结果可能不同。不可重复读出现的原因就是事务并发修改记录,要避免这种情况,最简单的方法就是对要修改的记录加锁,这回导致锁竞争加剧,影响性能。另一种方法是通过MVCC可以在无锁的情况下,避免不可重复读。 幻读:在同一个事务中,同一个查询多次返回的结果不一致。事务A新增了一条记录,事务B在事务A提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录。幻读是由于并发事务增加记录导致的,这个不能像不可重复读通过记录加锁解决,因为对于新增的记录根本无法加锁。需要将事务串行化,才能避免幻读。

    事务的隔离级别从低到高有:

    1. 读取未提交 (Read uncommitted)

      • 所有事务都可以看到其他未提交事务的执行结果
      • 本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少
      • 该级别引发的问题是——脏读(Dirty Read):读取到了未提交的数据
    2. 读提交 (read committed)

      • 这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)

      • 它满足了隔离的简单定义:一个事务只能看见已经提交事务做的改变

      • 这种隔离级别出现的问题是: 不可重复读(Nonrepeatable Read):

        不可重复读意味着我们在同一个事务中执行完全相同的 select 语句时可能看到不一样的结果。

        导致这种情况的原因可能有:

        • 有一个交叉的事务有新的commit,导致了数据的改变;
        • 一个数据库被多个实例操作时,同一事务的其他实例在该实例处理其间可能会有新的commit
    3. 可重复读 (repeatable read)

      • 这是MySQL的默认事务隔离级别
      • 它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行
      • 此级别可能出现的问题: 幻读(Phantom Read):当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行
      • InnoDB 通过多版本并发控制 (MVCC,Multiversion Concurrency Control) 机制解决幻读问题;
      • InnoDB 还通过间隙锁解决幻读问题
    4. 串行化 (Serializable)

      • 这是最高的隔离级别
      • 它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。MySQL锁总结
      • 在这个级别,可能导致大量的超时现象和锁竞争
  4. 持久性 (Durability):

    事务处理结束后, 对数据的修改就是永久的, 即便系统故障也不会丢失。


3. 语法与使用

  • 开启事务: BEGINSTART TRANSACTION

  • 提交事务: COMMIT, 提交会让所有修改生效

  • 回滚: ROLLBACK, 撤销正在进行的所有未提交的修改

  • 创建保存点: SAVEPOINT identifier

  • 删除保存点: RELEASE SAVEPOINT identifier

  • 把事务回滚到保存点: ROLLBACK TO identifier

  • 查询事务的隔离级别: show variables like '%isolation%';

  • 设置事务的隔离级别: SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}

    InnoDB 提供的隔离级别有

    • READ
    • UNCOMMITTED
    • READ COMMITTED
    • REPEATABLE READ
    • SERIALIZABLE


4. 示例



create table `abc` (
    id int unsigned primary key auto_increment,
    name varchar(32) unique,
    age int unsigned
) charset=utf8;
​
begin;
insert into abc (name, age) values ('aa', 11);
insert into abc (name, age) values ('bb', 22);
-- 在事务中查看一下数据
-- 同时另开一个窗口,连接到 MySQL 查看一下数据是否一样
select * from abc;
commit;
​
begin;
insert into abc (name, age) values ('cc', 33);
insert into abc (name, age) values ('dd', 44);
update abc set age=77 where name='aa';
-- 在事务中查看一下数据
select * from abc;
rollback;
​
select * from abc;  -- 事务结束后在查看一下数据

深度解析SQL和NoSQL数据库,掌握主流数据库【两万字解析】_第2张图片

 
  


二、存储过程

存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。

存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。

存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。

  1. 优点

    • 存储过程可封装,并隐藏复杂的商业逻辑。
    • 存储过程可以回传值,并可以接受参数。
    • 存储过程无法使用 SELECT 指令来运行,因为它是子程序,与查看表,数据表或用户定义函数不同。
    • 存储过程可以用在数据检验,强制实行商业逻辑等。
  2. 缺点

    • 存储过程,往往定制化于特定的数据库上,因为支持的编程语言不同。当切换到其他厂商的数据库系统时,需要重写原有的存储过程。
    • 存储过程的性能调校与撰写,受限于各种数据库系统。


语法

  1. 声明语句结束符,可以自定义:

    存储过程中有很多的SQL语句,SQL语句的后面为了保证语法结构必须要有分号(;),但是默认情况下分号表示客户端代码发送到服务器执行。必须更改结束符

    DELIMITER $$
    -- 或者
    DELIMITER //

  2. 声明存储过程:

     
    CREATE PROCEDURE demo_in_parameter(IN p_in int)

  3. 存储过程开始和结束符号:

     
    BEGIN .... END

  4. 变量赋值:

    SET @p_in=1

  5. 变量定义:

    DECLARE l_int int unsigned default 4000000;

  6. 创建mysql存储过程、存储函数:

    create procedure 存储过程名(参数)

  7. 存储过程体:

    create function 存储函数名(参数)


使用

  1. 简单用法

    -- 定义
    -- 如果存储过程中就一条SQL语句,begin…end两个关键字可以省略
    create procedure get_info()
    select * from student;
    ​
    -- 调用
    call get_info();

  2. 复杂一点的 (备注:只能在标准 mysql 客户端中执行,mycli 无法识别)

    delimiter // -- 定义前,将分隔符改成 //
    create procedure foo(in uid int)
    begin
    select * from student where `id`=uid;
    update student set `city`='北京' where `id`=uid;
    end//
    delimiter ;  -- 定义完以后可以将分隔符改回 分号
    ​
    call foo(3);

  3. 查看存储过程

    show procedure status like "%foo%";
    show create procedure foo;

  4. 删除存储过程

    
    drop procedure foo;


三、Python操作

  1. 安装:

     pip install pymysql

  2. 使用

    import pymysql
    ​
    db = pymysql.connect(host='localhost',
                         user='user',
                         password='passwd',
                         db='db',
                         charset='utf8')
    ​
    try:
        with db.cursor() as cursor:
            # 插入
            sql = "INSERT INTO `users` (`email`, `password`) VALUES (%s, %s)"
            cursor.execute(sql, ('webmaster@python.org', 'very-secret'))
        # 需要手动提交才会执行
        db.commit()
    ​
        with db.cursor() as cursor:
            # 读取记录
            sql = "SELECT `id`, `password` FROM `users` WHERE `email`=%s"
            cursor.execute(sql, ('webmaster@python.org',))
            result = cursor.fetchone()
            print(result)
    finally:
        db.close()


四、sql注入

什么是SQL注入?

SQL注入(SQLi)是一种注入攻击,,可以执行恶意SQL语句。它通过将任意SQL代码插入数据库查询,使攻击者能够完全控制Web应用程序后面的数据库服务器。攻击者可以使用SQL注入漏洞绕过应用程序安全措施;可以绕过网页或Web应用程序的身份验证和授权,并检索整个SQL数据库的内容;还可以使用SQL注入来添加,修改和删除数据库中的记录。

思考:下面的代码有没有问题?

import pymysql
db = pymysql.connect(host='localhost',user='root',password='abcd1234',db='test',charset='utf8')
​
name=input('请输入用户名:')
password=input('请输入密码:')
try:
    with db.cursor() as cursor:
        sql = 'select * from user where name="%s" and password="%s"' %(name,password)
        print(sql)
        cursor.execute(sql)
        print(cursor.fetchone())
    db.commit()
finally:
    db.close()


五、数据备份与恢复

  1. 备份

    mysqldump -h localhost -u root -p dbname > dbname.sql

  2. 恢复

    mysql -h localhost -u root -p123456 dbname < ./dbname.sql

深度解析SQL和NoSQL数据库,掌握主流数据库【两万字解析】_第3张图片

Redis 与 MongoDB


一、NoSQL概述

如今,大多数的计算机系统(包括服务器、PC、移动设备等)都会产生庞大的数据量。其实,早在2012年的时候,全世界每天产生的数据量就达到了2.5EB(艾字节)。这些数据有很大一部分是由关系型数据库来存储和管理的。实践证明,关系型数据库是实现数据持久化最为重要的方式,它也是大多数应用在选择持久化方案时的首选技术。

NoSQL 是一项全新的数据库革命性运动,虽然它的历史可以追溯到1998年,但是NoSQL真正深入人心并得到广泛的应用是在进入大数据时候以后,业界普遍认为NoSQL是更适合大数据存储的技术方案,这才使得NoSQL的发展达到了前所未有的高度。2012年《纽约时报》的一篇专栏中写到,大数据时代已经降临,在商业、经济及其他领域中,决策将不再基于经验和直觉而是基于数据和分析而作出。事实上,在天文学、气象学、基因组学、生物学、社会学、互联网搜索引擎、金融、医疗、社交网络、电子商务等诸多领域,由于数据过于密集和庞大,在数据的分析和处理上也遇到了前所未有的限制和阻碍,这一切都使得对大数据处理技术的研究被提升到了新的高度,也使得各种NoSQL的技术方案进入到了公众的视野。

NoSQL数据库按照其存储类型可以大致分为以下几类:

类型 部分代表 特点
列族数据库 HBase
Cassandra
Hypertable
顾名思义是按列存储数据的。最大的特点是方便存储结构化和半结构化数据,方便做数据压缩,对针对某一列或者某几列的查询有非常大的I/O优势,适合于批量数据处理和即时查询。
文档数据库 MongoDB
CouchDB
ElasticSearch
文档数据库一般用类JSON格式存储数据,存储的内容是文档型的。这样也就有机会对某些字段建立索引,实现关系数据库的某些功能,但不提供对参照完整性和分布事务的支持。
KV数据库 DynamoDB
Redis
LevelDB
可以通过key快速查询到其value,有基于内存和基于磁盘两种实现方案。
图数据库 Neo4J
FlockDB
JanusGraph
使用图结构进行语义查询的数据库,它使用节点、边和属性来表示和存储数据。图数据库从设计上,就可以简单快速的检索难以在关系系统中建模的复杂层次结构。
对象数据库 db4o
Versant
通过类似面向对象语言的语法操作数据库,通过对象的方式存取数据。


二、Redis 入门

Redis 是一种基于键值对的NoSQL数据库,它提供了对多种数据类型(字符串、哈希、列表、集合、有序集合、位图等)的支持,能够满足很多应用场景的需求。Redis将数据放在内存中,因此读写性能是非常惊人的。与此同时,Redis也提供了持久化机制,能够将内存中的数据保存到硬盘上,在发生意外状况时数据也不会丢掉。此外,Redis还支持键过期、地理信息运算、发布订阅、事务、管道、Lua脚本扩展等功能,总而言之,Redis的功能和性能都非常强大,如果项目中要实现高速缓存和消息队列这样的服务,直接交给Redis就可以了。目前,国内外很多著名的企业和商业项目都使用了Redis,包括:Twitter、Github、StackOverflow、新浪微博、百度、优酷土豆、美团、小米、唯品会等。


1. Redis简介

2008年,一个名为Salvatore Sanfilippo的程序员为他开发的LLOOGG项目定制了专属的数据库(因为之前他无论怎样优化MySQL,系统性能已经无法再提升了),这项工作的成果就是Redis的初始版本。后来他将Redis的代码放到了全球最大的代码托管平台Github,从那以后,Redis引发了大量开发者的好评和关注,继而有数百人参与了Redis的开发和维护,这使得Redis的功能越来越强大和性能越来越好。

Redis是 remote dictionary server 的缩写,它是一个用 ANSI C 编写的高性能的key-value存储系统,与其他的key-value存储系统相比,Redis有以下一些特点(也是优点):

  • Redis的读写性能极高,并且有丰富的特性(发布/订阅、事务、通知等)。
  • Redis支持数据的持久化(RDB和AOF两种方式),可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
  • Redis支持多种数据类型,包括:string、hash、list、set,zset、bitmap、hyperloglog等。
  • Redis支持主从复制(实现读写分离)以及哨兵模式(监控master是否宕机并自动调整配置)。
  • Redis支持分布式集群,可以很容易的通过水平扩展来提升系统的整体性能。
  • Redis基于TCP提供的可靠传输服务进行通信,很多编程语言都提供了Redis客户端支持。


2. Redis的应用场景

  1. 高速缓存 - 将不常变化但又经常被访问的热点数据放到Redis数据库中,可以大大降低关系型数据库的压力,从而提升系统的响应性能。
  2. 排行榜 - 很多网站都有排行榜功能,利用Redis中的列表和有序集合可以非常方便的构造各种排行榜系统。
  3. 商品秒杀/投票点赞 - Redis提供了对计数操作的支持,网站上常见的秒杀、点赞等功能都可以利用Redis的计数器通过+1或-1的操作来实现,从而避免了使用关系型数据的update操作。
  4. 分布式锁 - 利用Redis可以跨多台服务器实现分布式锁(类似于线程锁,但是能够被多台机器上的多个线程或进程共享)的功能,用于实现一个阻塞式操作。
  5. 消息队列 - 消息队列和高速缓存一样,是一个大型网站不可缺少的基础服务,可以实现业务解耦和非实时业务削峰等特性,这些我们都会在后面的项目中为大家展示。


3. Redis的安装和配置

可以通过在Redis的 官方网站 下载 Redis 的源代码,解压缩解归档之后通过 make 工具对源代码进行构建并安装。

wget http://101.44.1.120/files/318700000890F623/download.redis.io/releases/redis-5.0.8.tar.gz
tar -zxvf redis-5.0.8.tar.gz
cd redis-5.0.8
sudo make && sudo make install


4. Redis 的配置

在 redis 源代码目录下有一个名为redis.conf的配置文件,我们可以先查看一下该文件: vim redis.conf

  1. 配置将 Redis 服务绑定到指定的IP地址和端口。

    bind 127.0.0.1
    port 6379

  2. 设置后台运行 (以守护进程方式运行)

    daemonize yes

  3. 设置日志级别, 可选值: (debug: 调试, verbose: 详细, notice: 通知, warning: 警告)

    loglevel warning

  4. 配置数据库的数量, 默认为 16 个

    databases 16

  5. 配置数据写入规则

    save 900 1     # 900 秒 (15 分钟) 内修改过 1 个 key, , 写入一次数据库
    save 300 10    # 300 秒 (5 分钟) 内修改过 10 个 key, 写入一次数据库
    save 60 10000  # 60 秒 (1 分钟) 内修改过 10000 个 key, 写入一次数据库

  6. 配置Redis的持久化机制 - RDB。

     
    rdbcompression yes   # 压缩 RDB 文件
    rdbchecksum yes      # 对 RDB 文件进行校验
    dbfilename dump.rdb  # RDB 数据库文件的文件名
    dir /var/local/redis               # RDB 文件保存的目录

  7. 配置Redis的持久化机制 - AOF。

    appendonly no
    appendfilename "appendonly.aof"

  8. 配置Redis的主从复制,通过主从复制可以实现读写分离。

    # Master-Replica replication. Use replicaof to make a Redis instance a copy of
    # another Redis server. A few things to understand ASAP about Redis replication.
    #
    #   +------------------+      +---------------+
    #   |      Master      | ---> |    Replica    |
    #   | (receive writes) |      |  (exact copy) |
    #   +------------------+      +---------------+
    #
    # 1) Redis replication is asynchronous, but you can configure a master to
    #    stop accepting writes if it appears to be not connected with at least
    #    a given number of replicas.
    # 2) Redis replicas are able to perform a partial resynchronization with the
    #    master if the replication link is lost for a relatively small amount of
    #    time. You may want to configure the replication backlog size (see the next
    #    sections of this file) with a sensible value depending on your needs.
    # 3) Replication is automatic and does not need user intervention. After a
    #    network partition replicas automatically try to reconnect to masters
    #    and resynchronize with them.
    #
    replicaof 主机IP地址 主机端口

  9. 配置慢查询。

    slowlog-log-slower-than 10000  # 一次操作超过 10000 毫秒被视作一次慢查询
    slowlog-max-len 128            # 最多纪录 128 次满查询


5. Redis的服务器和客户端

接下来启动 Redis 服务器,下面的方式将以指定的配置文件启动 Redis 服务。

redis-server redis.conf

接下来用 Redis 客户端去连接服务器。

redis-cli -h localhost -p 6379

Redis有着非常丰富的数据类型,也有很多的命令来操作这些数据,具体的内容可以查看Redis命令参考,在这个网站上,除了Redis的命令参考,还有Redis的详细文档,其中包括了通知、事务、主从复制、持久化、哨兵、集群等内容。

127.0.0.1:6379> set username admin
OK
127.0.0.1:6379> get username
"admin"
127.0.0.1:6379> set password "123456" ex 300
OK
127.0.0.1:6379> get password
"123456"
127.0.0.1:6379> ttl username
(integer) -1
127.0.0.1:6379> ttl password
(integer) 286
127.0.0.1:6379> hset stu1 name hao
(integer) 0
127.0.0.1:6379> hset stu1 age 38
(integer) 1
127.0.0.1:6379> hset stu1 gender male
(integer) 1
127.0.0.1:6379> hgetall stu1
1) "name"
2) "hao"
3) "age"
4) "38"
5) "gender"
6) "male"
127.0.0.1:6379> hvals stu1
1) "hao"
2) "38"
3) "male"
127.0.0.1:6379> hmset stu2 name wang age 18 gender female tel 13566778899
OK
127.0.0.1:6379> hgetall stu2
1) "name"
2) "wang"
3) "age"
4) "18"
5) "gender"
6) "female"
7) "tel"
8) "13566778899"
127.0.0.1:6379> lpush nums 1 2 3 4 5
(integer) 5
127.0.0.1:6379> lrange nums 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
127.0.0.1:6379> lpop nums
"5"
127.0.0.1:6379> lpop nums
"4"
127.0.0.1:6379> rpop nums
"1"
127.0.0.1:6379> rpop nums
"2"
127.0.0.1:6379> sadd fruits apple banana orange apple grape grape
(integer) 4
127.0.0.1:6379> scard fruits
(integer) 4
127.0.0.1:6379> smembers fruits
1) "grape"
2) "orange"
3) "banana"
4) "apple"
127.0.0.1:6379> sismember fruits apple
(integer) 1
127.0.0.1:6379> sismember fruits durian
(integer) 0
127.0.0.1:6379> sadd nums1 1 2 3 4 5
(integer) 5
127.0.0.1:6379> sadd nums2 2 4 6 8
(integer) 4
127.0.0.1:6379> sinter nums1 nums2
1) "2"
2) "4"
127.0.0.1:6379> sunion nums1 nums2
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "8"
127.0.0.1:6379> sdiff nums1 nums2
1) "1"
2) "3"
3) "5"
127.0.0.1:6379> zadd topsinger 5234 zhangxy 1978 chenyx 2235 zhoujl 3520 xuezq
(integer) 4
127.0.0.1:6379> zrange topsinger 0 -1 withscores
1) "chenyx"
2) "1978"
3) "zhoujl"
4) "2235"
5) "xuezq"
6) "3520"
7) "zhangxy"
8) "5234"
127.0.0.1:6379> zrevrange topsinger 0 -1
1) "zhangxy"
2) "xuezq"
3) "zhoujl"
4) "chenyx"
127.0.0.1:6379> geoadd pois 116.39738549206541 39.90862689286386 tiananmen 116.27172936413572 39.99
135172904494 yiheyuan 117.27766503308104 40.65332064313784 gubeishuizhen
(integer) 3
127.0.0.1:6379> geodist pois tiananmen gubeishuizhen km
"111.5333"
127.0.0.1:6379> geodist pois tiananmen yiheyuan km
"14.1230"
127.0.0.1:6379> georadius pois 116.86499108288572 40.40149669363615 50 km withdist
1) 1) "gubeishuizhen"
   2) "44.7408"


6. Redis持久化

Redis在运行时,所有的数据都保存在内存里,进程结束以后,会将数据写入到硬盘中。启动时,会读取硬盘里的内容,并将内容全部加载到内存中(会大量的占用内存)。

Redis的持久化有两种形式:RDB和AOF


6.1 RDB

默认的持久化方式,是对内存中的数据进行镜像,并以二进制的形式保存到dump.rdb文件中。会根据配置文件的时间节点对文件进行持久化。

save 900 1
save 300 10
save 60 10000

优点:速度快,直接镜像内存里的数据,文件小。

缺点:数据有可能会丢失,在两次保存间隔内的数据,有可能会丢失。


6.2 AOF

AOF(Append only file)持久化,将修改的每一条指令记录进appendonly.aof中,需要修改配置文件,来打开aof功能。

appendfsync always:每次有新命令追加到aof文件时就执行一个持久化,非常慢但是安全
appendfsync everysec:每秒执行一次持久化,足够快(和使用rdb持久化差不多)并且在故障时只会丢失1秒钟的数据
appendfsync no:从不持久化,将数据交给操作系统来处理。redis处理命令速度加快但是不安全。

优点:适合保存增量数据,数据不丢失。

缺点:文件体积大,恢复时间长


7. 在Python程序中使用Redis

可以使用pip安装redis模块。redis模块的核心是名为Redis的类,该类的对象代表一个Redis客户端,通过该客户端可以向Redis服务器发送命令并获取执行的结果。上面我们在Redis客户端中使用的命令基本上就是Redis对象可以接收的消息,所以如果了解了Redis的命令就可以在Python中玩转Redis。

>>> import redis
>>> client = redis.Redis(host='1.2.3.4', port=6379, password='1qaz2wsx')
>>> client.set('username', 'admin')
True
>>> client.hset('student', 'name', 'hao')
1
>>> client.hset('student', 'age', 38)
1
>>> client.keys('*')
[b'username', b'student']
>>> client.get('username')
b'admin'
>>> client.hgetall('student')
{b'name': b'hao', b'age': b'38'}

深度解析SQL和NoSQL数据库,掌握主流数据库【两万字解析】_第4张图片


三、MongoDB概述


1. MongoDB简介

MongoDB是2009年问世的一个面向文档的数据库管理系统,由 C++ 语言编写,旨在为Web应用提供可扩展的高性能数据存储解决方案。虽然在划分类别的时候后,MongoDB被认为是NoSQL的产品,但是它更像一个介于关系数据库和非关系数据库之间的产品,在非关系数据库中它功能最丰富,最像关系数据库。

MongoDB将数据存储为一个文档,一个文档由一系列的“键值对”组成,其文档类似于JSON对象,但是MongoDB对JSON进行了二进制处理(能够更快的定位key和value),因此其文档的存储格式称为BSON。关于JSON和BSON的差别大家可以看看MongoDB官方网站的文章《JSON and BSON》。

目前,MongoDB已经提供了对Windows、MacOS、Linux、Solaris等多个平台的支持,而且也提供了多种开发语言的驱动程序,Python当然是其中之一。


2. MongoDB的安装和配置

可以从MongoDB的官方下载链接下载MongoDB,而Linux和MacOS则提供了压缩文件。也可以使用yum命令直接安装MongoDB服务端和客户端。


sudo yum install mongodb-server  # 安装MongoDB服务端
sudo yum install mongodb   # 安装MongoDB客户端
sudo mongod -f /etc/mongod.conf # 加载配置项,启动mongodb服务器

说明:上面的操作中,export命令是设置PATH环境变量,这样可以在任意路径下执行mongod来启动MongoDB服务器。MongoDB默认保存数据的路径是/data/db目录,为此要提前创建该目录。此外,在使用mongod启动MongoDB服务器时,--bind_ip参数用来将服务绑定到指定的IP地址,也可以用--port参数来指定端口,默认端口为27017。


3. MongoDB基本概念

我们通过与关系型数据库进行对照的方式来说明MongoDB中的一些概念。

SQL MongoDB 解释(SQL/MongoDB)
database database 数据库/数据库
table collection 二维表/集合
row document 记录(行)/文档
column field 字段(列)/域
index index 索引/索引
table joins --- 表连接/嵌套文档
primary key primary key 主键/主键(_id字段)


4. 通过Shell操作MongoDB

启动服务器后可以使用交互式环境跟服务器通信,如下所示。

mongo
  1. 查看、创建和删除数据库。

    
    > // 显示所有数据库
    > show dbs
    admin   0.000GB
    config  0.000GB
    local   0.000GB
    > // 创建并切换到school数据库
    > use school
    switched to db school
    > // 删除当前数据库
    > db.dropDatabase()
    { "ok" : 1 }
    >

  2. 创建、删除和查看集合。

    > // 创建并切换到school数据库
    > use school
    switched to db school
    > // 创建colleges集合
    > db.createCollection('colleges')
    { "ok" : 1 }
    > // 创建students集合
    > db.createCollection('students')
    { "ok" : 1 }
    > // 查看所有集合
    > show collections
    colleges
    students
    > // 删除colleges集合
    > db.colleges.drop()
    true
    >

    说明:在MongoDB中插入文档时如果集合不存在会自动创建集合,所以也可以按照下面的方式通过创建文档来创建集合。

  3. 文档的CRUD操作。

    > // 向students集合插入文档
    > db.students.insert({stuid: 1001, name: '张三', age: 38})
    WriteResult({ "nInserted" : 1 })
    > // 向students集合插入文档
    > db.students.save({stuid: 1002, name: '王大锤', tel: '13012345678', gender: '男'})
    WriteResult({ "nInserted" : 1 })
    > // 查看所有文档
    > db.students.find()
    { "_id" : ObjectId("5b13c72e006ad854460ee70b"), "stuid" : 1001, "name" : "张三", "age" : 38 }
    { "_id" : ObjectId("5b13c790006ad854460ee70c"), "stuid" : 1002, "name" : "王大锤", "tel" : "13012345678", "gender" : "男" }
    > // 更新stuid为1001的文档
    > db.students.update({stuid: 1001}, {'$set': {tel: '13566778899', gender: '男'}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    > // 插入或更新stuid为1003的文档
    > db.students.update({stuid: 1003}, {'$set': {name: '白元芳', tel: '13022223333', gender: '男'}},  upsert=true)
    WriteResult({
            "nMatched" : 0,
            "nUpserted" : 1,
            "nModified" : 0,
            "_id" : ObjectId("5b13c92dd185894d7283efab")
    })
    > // 查询所有文档
    > db.students.find().pretty()
    {
            "_id" : ObjectId("5b13c72e006ad854460ee70b"),
            "stuid" : 1001,
            "name" : "张三",
            "age" : 38,
            "gender" : "男",
            "tel" : "13566778899"
    }
    {
            "_id" : ObjectId("5b13c790006ad854460ee70c"),
            "stuid" : 1002,
            "name" : "王大锤",
            "tel" : "13012345678",
            "gender" : "男"
    }
    {
            "_id" : ObjectId("5b13c92dd185894d7283efab"),
            "stuid" : 1003,
            "gender" : "男",
            "name" : "白元芳",
            "tel" : "13022223333"
    }
    > // 查询stuid大于1001的文档
    > db.students.find({stuid: {'$gt': 1001}}).pretty()
    {
            "_id" : ObjectId("5b13c790006ad854460ee70c"),
            "stuid" : 1002,
            "name" : "王大锤",
            "tel" : "13012345678",
            "gender" : "男"
    }
    {
            "_id" : ObjectId("5b13c92dd185894d7283efab"),
            "stuid" : 1003,
            "gender" : "男",
            "name" : "白元芳",
            "tel" : "13022223333"
    }
    > // 查询stuid大于1001的文档只显示name和tel字段
    > db.students.find({stuid: {'$gt': 1001}}, {_id: 0, name: 1, tel: 1}).pretty()
    { "name" : "王大锤", "tel" : "13012345678" }
    { "name" : "白元芳", "tel" : "13022223333" }
    > // 查询name为“张三”或者tel为“13022223333”的文档
    > db.students.find({'$or': [{name: '张三'}, {tel: '13022223333'}]}, {_id: 0, name: 1, tel: 1}).pretty()
    { "name" : "张三", "tel" : "13566778899" }
    { "name" : "白元芳", "tel" : "13022223333" }
    > // 查询学生文档跳过第1条文档只查1条文档
    > db.students.find().skip(1).limit(1).pretty()
    {
            "_id" : ObjectId("5b13c790006ad854460ee70c"),
            "stuid" : 1002,
            "name" : "王大锤",
            "tel" : "13012345678",
            "gender" : "男"
    }
    > // 对查询结果进行排序(1表示升序,-1表示降序)
    > db.students.find({}, {_id: 0, stuid: 1, name: 1}).sort({stuid: -1})
    { "stuid" : 1003, "name" : "白元芳" }
    { "stuid" : 1002, "name" : "王大锤" }
    { "stuid" : 1001, "name" : "张三" }
    > // 在指定的一个或多个字段上创建索引
    > db.students.ensureIndex({name: 1})
    {
            "createdCollectionAutomatically" : false,
            "numIndexesBefore" : 1,
            "numIndexesAfter" : 2,
            "ok" : 1
    }
    >

使用MongoDB可以非常方便的配置数据复制,通过冗余数据来实现数据的高可用以及灾难恢复,也可以通过数据分片来应对数据量迅速增长的需求。关于MongoDB更多的操作可以查阅官方文档 ,同时推荐大家阅读Kristina Chodorow写的《MongoDB权威指南》。


5. 在Python程序中操作MongoDB

可以通过pip安装pymongo来实现对MongoDB的操作。

pip3 install pymongo
python3

>>> from pymongo import MongoClient
>>> client = MongoClient('mongodb://127.0.0.1:27017')
>>> db = client.school
>>> for student in db.students.find():
...     print('学号:', student['stuid'])
...     print('姓名:', student['name'])
...     print('电话:', student['tel'])
...
学号: 1001.0
姓名: 张三
电话: 13566778899
学号: 1002.0
姓名: 王大锤
电话: 13012345678
学号: 1003.0
姓名: 白元芳
电话: 13022223333
>>> db.students.find().count()
3
>>> db.students.remove()
{'n': 3, 'ok': 1.0}
>>> db.students.find().count()
0
>>> coll = db.students
>>> from pymongo import ASCENDING
>>> coll.create_index([('name', ASCENDING)], unique=True)
'name_1'
>>> coll.insert_one({'stuid': int(1001), 'name': '张三', 'gender': True})

>>> coll.insert_many([{'stuid': int(1002), 'name': '王大锤', 'gender': False}, {'stuid': int(1003), 'name': '白元芳', 'gender': True}])

>>> for student in coll.find({'gender': True}):
...     print('学号:', student['stuid'])
...     print('姓名:', student['name'])
...     print('性别:', '男' if student['gender'] else '女')
...
学号: 1001
姓名: 张三
性别: 男
学号: 1003
姓名: 白元芳
性别: 男
>>>

深度解析SQL和NoSQL数据库,掌握主流数据库【两万字解析】_第5张图片

行业资料:添加即可领取PPT模板、简历模板、行业经典书籍PDF。
面试题库:历年经典,热乎的大厂面试真题,持续更新中,添加获取。
学习资料:含Python、爬虫、数据分析、算法等学习视频和文档,添加获取
交流加群:大佬指点迷津,你的问题往往有人遇到过,技术互助交流。

领取 

深度解析SQL和NoSQL数据库,掌握主流数据库【两万字解析】_第6张图片

你可能感兴趣的