阳江企业网站排名优化,阜城网站建设价格,seo教程正规化岚鸿,企业品牌网站建设方案文章目录 目录1、MySQLmysql索引实现mysql索引优化mysql索引失效的情况mysql 千万数据优化mysql 事务隔离级别 实现原理mysql MVCC版本链#xff08;undo log#xff09;mysql数据同步机制 主从复制 #xff08;binlog#xff09;mysql 日志数据恢复… 文章目录 目录1、MySQLmysql索引实现mysql索引优化mysql索引失效的情况mysql 千万数据优化mysql 事务隔离级别 实现原理mysql MVCC版本链undo logmysql数据同步机制 主从复制 binlogmysql 日志数据恢复redo log、缓冲池两阶段提交 2、Redisredis有哪些数据结构string类底层是怎么实现的redis效率高的原因redis缓存设计 缓存雪崩击穿穿透redis缓存过期什么策略redis数据持久化redis主从复制redis主从故障转移redis并发竞争与分布式锁 3、MongoDBNoSQL是什么MongoDB和redis的区别MongoDB是由哪种语言写的MongoDB有哪些适用场景MongoDB为啥速度快MongoDB存储引擎 索引MongoDB 集群搭建 主从复制 4、数据库原理数据库的分类sql nosql事务与隔离级别关系数据库设计理论函数依赖异常 范式关系数据库概念设计ER图关系数据库sql语法 目录
数据库专题对照MySQL Redis
1、数据结构
mysql关系型磁盘存储
B树索引选择索引优化索引失效。redis非关系缓存数据库基于内存高并发键值对
5类数据结构strhashlistsetzset缓存过期策略雪崩击穿穿透。2、数据持久化数据备份恢复主从复制
mysql3个日志undo logo redo logo binlogo
redisAOF 日志 RDB快照。3、并发竞争
mysqlinnoDB事务隔离级别MVCC加锁千万优化
redis单线程分布式锁
1、MySQL
关系MySQL非关系RedisMongoDB
mysql索引实现
说一下你知道的mysql索引 (5.5以后都用InnoDB用的是B树分为聚簇和二级增加效率占用内存二级查询的时候覆盖索引和回表前缀优化联合索引优化主键递增)
mysql索引为什么使用B树不使用B树 为什么我不使用红黑树 二叉树高度很高查找的内存IO次数大B2000w只要4次
红黑树的五个特性记得吗 根和叶是黑的每个点红或黑没有相连的两个红任意点到叶的黑数目相同
5.5以后都用InnoDB用的是B树 分为聚簇和二级 增加效率占用内存 二级查询的时候覆盖索引和回表 前缀优化联合索引优化主键递增
MySQL 的架构共分为两层服务层和逻辑索引层。
Server 层负责建立连接、分析和执行 SQL。查询缓存、解析器、预处理器、优化器、执行器等。存储引擎层负责数据的存储和提取。支持 InnoDB、MyISAM、Memory 等多个存储引擎。从 MySQL 5.5 版本开始 InnoDB 成为了 MySQL 的默认存储引擎
一条sql的查询原理
连接器建立连接管理连接、校验用户身份查询缓存查询语句如果命中查询缓存则直接返回否则继续往下执行。MySQL 8.0 已删除该模块解析 SQL通过解析器对 SQL 查询语句进行词法分析、语法分析然后构建语法树方便后续模块读取表名、字段、语句类型执行 SQL执行 SQL 共有三个阶段预处理阶段检查表或字段是否存在将 select * 中的 * 符号扩展为表上的所有列。优化阶段基于查询成本的考虑 选择查询成本最小的执行计划执行阶段根据执行计划执行 SQL 查询语句从存储引擎读取记录返回给客户端
为什么用B树
而树的高度决定于磁盘 I/O 操作的次数因为树是存储在磁盘中的访问每个节点都对应一次磁盘 I/O 操作也就是说树的高度就等于每次查询数据时磁盘 IO 操作的次数所以树的高度越高就会影响查询性能。B 树和 B 都是通过多叉树的方式会将树的高度变矮所以这两个数据结构非常适合检索存于磁盘中的数据。但是 MySQL 默认的存储引擎 InnoDB 采用的是 B 作为索引的数据结构原因有
innodb/myisam区别
都是B树但是MyISAM引擎不支持事务所以mysql5.5以后默认用innodb
mysql索引优化
什么时候建立索引
字段有唯一性限制的比如商品编码经常用于 WHERE 查询条件的字段这样能够提高整个表的查询速度如果查询条件不是一个字段可以建立联合索引。经常用于 GROUP BY 和 ORDER BY 的字段这样在查询的时候就不需要再去做一次排序了因为我们都已经知道了建立索引之后在 BTree 中的记录都是排序好的。
这里说一下几种常见优化索引的方法 1、前缀索引优化 前缀索引顾名思义就是使用某个字段中字符串的前几个字符建立索引那我们为什么需要使用前缀来建立索引呢 使用前缀索引是为了减小索引字段大小可以增加一个索引页中存储的索引值有效提高索引的查询速度。在一些大字符串的字段作为索引时使用前缀索引可以帮助我们减小索引项的大小。 不过前缀索引有一定的局限性例如 order by 就无法使用前缀索引 无法把前缀索引用作覆盖索引 2、覆盖索引优化 覆盖索引是指 SQL 中 query 的所有字段在索引 BTree 的叶子节点上都能找得到的那些索引从二级索引中查询得到记录而不需要通过聚簇索引查询获得可以避免回表的操作。 假设我们只需要查询商品的名称、价格有什么方式可以避免回表呢 我们可以建立一个联合索引即「商品ID、名称、价格」作为一个联合索引。如果索引中存在这些数据查询将不会再次检索主键索引从而避免回表。 所以使用覆盖索引的好处就是不需要查询出包含整行记录的所有信息也就减少了大量的 I/O 操作。 3、主键索引自增 我们在建表的时候都会默认将**主键索引设置为自增的**具体为什么要这样做呢又什么好处 InnoDB 创建主键索引默认为聚簇索引数据被存放在了 BTree 的叶子节点上。也就是说同一个叶子节点内的各个数据是按主键顺序存放的因此每当有一条新的数据插入时数据库会根据主键将其插入到对应的叶子节点中。 如果我们使用自增主键那么每次插入的新数据就会按顺序添加到当前索引节点的位置不需要移动已有的数据当页面写满就会自动开辟一个新页面。因为每次插入一条新记录都是追加操作不需要重新移动数据因此这种插入数据的方法效率非常高。 如果我们使用非自增主键由于每次插入主键的索引值都是随机的因此每次插入新的数据时就可能会插入到现有数据页中间的某个位置这将不得不移动其它数据来满足新数据的插入甚至需要从一个页面复制数据到另外一个页面我们通常将这种情况称为页分裂。页分裂还有可能会造成大量的内存碎片导致索引结构不紧凑从而影响查询效率。 为了更好的利用索引索引列要设置为 NOT NULL 约束。有两个原因 第一原因索引列存在 NULL 就会导致优化器在做索引选择的时候更加复杂更加难以优化因为可为 NULL 的列会使索引、索引统计和值比较都更复杂比如进行索引统计时count 会省略值为NULL 的行。 第二个原因**NULL 值是一个没意义的值但是它会占用物理空间所以会带来的存储空间的问题**因为 InnoDB 存储记录的时候如果表中存在允许为 NULL 的字段。
mysql索引失效的情况
什么时候不用索引
WHERE 条件GROUP BYORDER BY 里用不到的字段索引的价值是快速定位如果起不到定位的字段通常是不需要创建索引的因为索引是会占用物理空间的。表数据太少的时候不需要创建索引经常更新的字段不用创建索引比如不要对电商项目的用户余额建立索引因为索引字段频繁修改由于要维护 BTree的有序性那么就需要频繁的重建索引这个过程是会影响数据库性能的。字段中存在大量重复数据不需要创建索引比如性别字段只有男女如果数据库表中男女的记录分布均匀那么无论搜索哪个值都可能得到一半的数据。在这些情况下还不如不要索引因为 MySQL 还有一个查询优化器查询优化器发现某个值出现在表的数据行中的百分比很高的时候它一般会忽略索引进行全表扫描。
什么时候索引失效
当我们在查询条件中对索引列做了计算、函数、类型转换操作这些情况下都会造成索引失效
当我们在查询条件中对索引列使用函数就会导致索引失效。
当我们在查询条件中对索引列进行表达式计算也是无法走索引的。
MySQL 在遇到字符串和数字比较的时候会自动把字符串转为数字然后再进行比较。如果字符串是索引列而条件语句中的输入参数是数字的话那么索引列会发生隐式类型转换由于隐式类型转换是通过 CAST 函数实现的等同于对索引列使用了函数所以就会导致索引失效。联合索引要能正确使用需要遵循最左匹配原则也就是按照最左优先的方式进行索引的匹配否则就会导致索引失效。group by, order by
在 WHERE 子句中如果在 OR 前的条件列是索引列而在 OR 后的条件列不是索引列那么索引会失效。
当我们使用左或者左右模糊匹配的时候也就是 like %xx 或者 like %xx%这两种方式都会造成索引失效
mysql 千万数据优化
千万数据量解决方案mysql是2000w数据分库分表或按一定的规则拆分做到查询某一条数据库尽量在一个子表中。 维护合适的索引删掉不必要的。
mysql 事务隔离级别 实现原理
就比如说遇到A和B两个事务一个提交了就会导致另一个错误。
通过MVCC实现快照读就不会出现这个问题。
事务有哪些隔离级别 区别是什么
读未提交read uncommitted指一个事务还没提交时它做的变更就能被其他事务看到读提交read committed指一个事务提交之后它做的变更才能被其他事务看到可重复读repeatable read指一个事务执行过程中看到的数据一直跟这个事务启动时看到的数据是一致的MySQL InnoDB 引擎的默认隔离级别串行化serializable 会对记录加上读写锁在多个事务对这条记录进行读写操作时如果发生了读写冲突的时候后访问的事务必须等前一个事务执行完成才能继续执行
怎么实现的
针对快照读普通 select 语句是通过 MVCC 方式解决了幻读因为可重复读隔离级别下事务执行过程中看到的数据一直跟这个事务启动时看到的数据是一致的即使中途有其他事务插入了一条数据是查询不出来这条数据的所以就很好了避免幻读问题。针对当前读select … for update 等语句是通过 next-key lock记录锁间隙锁方式解决了幻读因为当执行 select … for update 语句的时候会加上 next-key lock如果有其他事务在 next-key lock 锁范围内插入了一条记录那么这个插入语句就会被阻塞无法成功插入所以就很好了避免幻读问题。
mysql MVCC版本链undo log DDL/DML/DCL DDL数据定义语言这些语句定义了不同的数据段、表、列、索引create、drop、alter等。
DML数据操纵语句包括 insert、delete、udpate 和select 等。(增添改查
DCL数据控制语句用于控制不同数据段直接的许可和访问级别的语句。用户的访问权限和安全级别。主要的语句关键字包括 grant、revoke 等。 undo log回滚日志是 Innodb 存储引擎层生成的日志实现了事务中的原子性主要用于事务回滚和 MVCC。 在发生回滚时就读取 undo log 里的数据然后做原先相反操作。比如当 delete 一条记录时undo log 中会把记录中的内容都记下来然后执行回滚操作的时候就读取 undo log 里的数据然后进行 insert 操作。 一条记录的每一次更新操作产生的 undo log 格式都有一个 roll_pointer 指针和一个 trx_id 事务id 通过 trx_id 可以知道该记录是被哪个事务修改的通过 roll_pointer 指针可以将这些 undo log 串成一个链表这个链表就被称为版本链 如果不满足可见行就会顺着 undo log 版本链里找到满足其可见性的记录从而控制并发事务访问同一个记录时的行为这就叫 MVCC多版本并发控制。 实现事务回滚保障事务的原子性。事务处理过程中如果出现了错误或者用户执 行了 ROLLBACK 语句MySQL 可以利用 undo log 中的历史数据将数据恢复到事务开始之前的状态。实现 MVCC多版本并发控制关键因素之一。MVCC 是通过 ReadView undo log 实现的。undo log 为每条记录保存多份历史数据MySQL 在执行快照读普通 select 语句的时候会根据事务的 Read View 里的信息顺着 undo log 的版本链找到满足其可见性的记录。 很多人疑问 undo log 是如何刷盘持久化到磁盘的 undo log 和数据页的刷盘策略是一样的都需要通过 redo log 保证持久化。 buffer pool 中有 undo 页对 undo 页的修改也都会记录到 redo log。redo log 会每秒刷盘提交事务时也会刷盘数据页和 undo 页都是靠这个机制保证持久化的。
mysql数据同步机制 主从复制 binlog
什么是数据同步参考
MySQL的数据同步机制可以将数据从一个主数据库同步到另一个或多个从数据库即Master-Slaves模式默认的话同步的过程是异步进行的从节点不需要与主节点建立永久型的连接。可以通过配置你可以选择复制全部的数据库或者指定的数据库亦或者是指定的某个数据库中的某些表。
在MySQL8.0中 支持三种类型的时间上的同步方式
1、异步复制asynchronous replication 这种同步方式是最为传统的复制方式也就是我们常说的基于binlog日志的同步方式当主库发生数据变化时会将数据的更改写入日志文件中异步同步给从库从库再进行对应的同步操作。2、半同步semisynchronous replication 默认情况下MySQL复制是异步的。主进程将事件写入binlog但不知道slave什么时候处理了这些事件。 使用异步复制如果主服务器崩溃它提交的事务可能没有传输到任何slave上。在这种情况下从master到slave的故障转移可能会导致数据的丢失。 而使用半同步方式在一个事务中在主库上的写入或更新的操作会在从库收到同步事件并发出确认或者超时之前一直阻塞着如果超时发生时没有任何从库确认消息则master将恢复到异步复制。当至少一个semi-sync的slave赶上时返回到半同步复制。3、延迟同步delayed replication 在MySQL8.0中也支持了延迟同步的方式可以设定从库与主库之间数据同步的延迟时长。
MySQL同步模式 在MySQL数据同步中核心的同步模式有两种。
基于SQL语句的同步的SBR(statement based replication) 该种模式下当主库发生数据变更时只将变更的SQL语句写入binlog日志中同步给从库。 主要优点 binlog记录的数据量较小占用磁盘空间小 需要同步的消息小带宽占用少 主要缺点 一些SQL 会导致主从不一致例如“INSERT … SELECT with no ORDER BY“ SELECT 返回行的顺序会不一致如果有AUTO_INCREMENT 列就会导致主从不一致 依赖机器环境信息、存储过程或触发器的语句可能在主从上的结果不一致例如依赖uuid()、now() 等。基于数据行同步的RBR(Row Based Replication) 模式该种模式下当主库发生数据变更时所有变更涉及到的数据行的变化都会被写入binlog日志中同步给从库。 主要优点 所有的写请求都能同步不会出现主从不一致的情况 锁粒度更小并发度更高 更新较少行的请求在从库上执行较快 主要缺点 更新非常多行的SQL 会写大量的日志占用磁盘资源同步时又占用网络资源 日志中没有原始的SQL 请求不利于审计同时也支持第三种模式即混合模式MBR(mixed based replication)该种模式是基于上面两种模式的MySQL会自动选择合适的同步模式进行数据同步。 在一般情况下推荐使用基于行模式RBR(Row Based Replication) binlog 归档日志是 Server 层生成的日志主要用于数据备份和主从复制 binlog 是 MySQL 的 Server 层实现的日志所有存储引擎都可以使用 redo log 是 Innodb 存储引擎实现的日志 binlog 是追加写写满一个文件就创建一个新的文件继续写不会覆盖以前的日志保存的是全量的日志。 redo log 是循环写日志空间大小是固定全部写满就从头开始保存未被刷入磁盘的脏页日志。 binlog 用于备份恢复、主从复制 redo log 用于掉电等故障恢复。 不可以使用 redo log 文件恢复只能使用 binlog 文件恢复。 因为 redo log 文件是循环写是会边写边擦除日志的只记录未被刷入磁盘的数据的物理日志已经刷入磁盘的数据都会从 redo log 文件里擦除。 binlog 文件保存的是全量的日志也就是保存了所有数据变更的情况理论上只要记录在 binlog 上的数据都可以恢复所以如果不小心整个数据库的数据被删除了得用 binlog 文件恢复数据。 从库是不是越多越好 不是的。 因为从库数量增加从库连接上来的 I/O 线程也比较多主库也要创建同样多的 log dump 线程来处理复制的请求对主库资源消耗比较高同时还受限于主库的网络带宽。 所以在实际使用中一个主库一般跟 23 个从库1 套数据库1 主 2 从 1 备主这就是一主多从的 MySQL 集群结构。
binlog文件内容
默认情况下binlog日志是二进制格式无法直接查看。可使用两种方式进行查看mysqlbinlog是MySQL官方提供的一个binlog查看工具可以查看本地的binlog MySQL 主从复制还有哪些模型 主要有三种
同步复制MySQL 主库提交事务的线程要等待所有从库的复制成功响应才返回客户端结果。这种方式在实际项目中基本上没法用原因有两个一是性能很差因为要复制到所有节点才返回响应二是可用性也很差主库和所有从库任何一个数据库出问题都会影响业务。异步复制默认模型MySQL 主库提交事务的线程并不会等待 binlog 同步到各从库就返回客户端结果。这种模式一旦主库宕机数据就会发生丢失。半同步复制MySQL 5.7 版本之后增加的一种复制方式介于两者之间事务线程不用等待所有的从库复制成功响应只要一部分复制成功响应回来就行比如一主二从的集群只要数据成功复制到任意一个从库上主库的事务线程就可以返回给客户端。这种半同步复制的方式兼顾了异步复制和同步复制的优点即使出现主库宕机至少还有一个从库有最新的数据不存在数据丢失的风险。
mysql 日志数据恢复redo log、缓冲池两阶段提交
MySQL 日志undo log、redo log、binlog redo log重做日志是 Innodb 存储引擎层生成的日志实现了事务中的持久性主要用于掉电等故障恢复 InnoDB 会把存储的数据划分为若干个「页」以页作为磁盘和内存交互的基本单位一个页的默认大小为 16KB。因此Buffer Pool 同样需要按「页」来划分。 当读取数据时如果数据存在于 Buffer Pool 中客户端就会直接读取 Buffer Pool 中的数据否则再去磁盘中读取。当修改数据时如果数据存在于 Buffer Pool 中那直接修改 Buffer Pool 中数据所在的页然后将其页设置为脏页该页的内存数据和磁盘上的数据已经不一致为了减少磁盘I/O不会立即将脏页写入磁盘后续由后台线程选择一个合适的时机将脏页写入到磁盘。 查询一条记录就只需要缓冲一条记录吗 不是的。 当我们查询一条记录时InnoDB 是会把整个页的数据加载到 Buffer Pool 中将页加载到 Buffer Pool 后再通过页里的「页目录」去定位到某条具体的记录。 Buffer Pool 是提高了读写效率没错但是问题来了Buffer Pool 是基于内存的而内存总是不可靠万一断电重启还没来得及落盘的脏页数据就会丢失。 为了防止断电导致数据丢失的问题当有一条记录需要更新的时候InnoDB 引擎就会先更新内存同时标记为脏页然后将本次对这个页的修改以 redo log 的形式记录下来这个时候更新就算完成了。 WAL 技术指的是 MySQL 的写操作并不是立刻写到磁盘上而是先写日志然后在合适的时间再写到磁盘上。 什么是 redo log redo log 是物理日志记录了某个数据页做了什么修改比如对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新每当执行一个事务就会产生这样的一条或者多条物理日志。 在事务提交时只要先将 redo log 持久化到磁盘即可可以不需要等到将缓存在 Buffer Pool 里的脏页数据持久化到磁盘。 当系统崩溃时虽然脏页数据没有持久化但是 redo log 已经持久化接着 MySQL 重启后可以根据 redo log 的内容将所有数据恢复到最新的状态。 redo log 和 undo log 区别在哪 redo log 记录了此次事务「完成后」的数据状态记录的是更新之后的值undo log 记录了此次事务「开始前」的数据状态记录的是更新之前的值事务提交之前发生了崩溃重启后会通过 undo log 回滚事务事务提交之后发生了崩溃重启后会通过 redo log 恢复事务如下图 redo log 要写到磁盘数据也要写磁盘为什么要多此一举 写入 redo log 的方式使用了追加操作 所以磁盘操作是顺序写而写入数据需要先找到写入位置然后才写到磁盘所以磁盘操作是随机写。 磁盘的「顺序写 」比「随机写」 高效的多因此 redo log 写入磁盘的开销更小。 redo log 什么时候刷盘 MySQL 正常关闭时每次事务提交时.当 redo log buffer 中记录的写入量大于 redo log buffer 内存空间的一半时会触发落盘InnoDB 的后台线程每隔 1 秒将 redo log buffer 持久化到磁盘。
事务执行过程中先把日志写到 binlog cacheServer 层的 cache事务提交的时候再把 binlog cache 写到 binlog 文件中。 三个日志讲完了至此我们可以先小结下update 语句的执行过程。 当优化器分析出成本最小的执行计划后执行器就按照执行计划开始进行更新操作。
具体更新一条记录 UPDATE t_user SET name xiaolin WHERE id 1; 的流程如下: 执行器负责具体执行会调用存储引擎的接口通过主键索引树搜索获取 id 1 这一行记录 如果 id1 这一行所在的数据页本来就在 buffer pool 中就直接返回给执行器更新如果记录不在 buffer pool将数据页从磁盘读入到 buffer pool返回记录给执行器。 执行器得到聚簇索引记录后会看一下更新前的记录和更新后的记录是否一样 如果一样的话就不进行后续更新流程如果不一样的话就把更新前的记录和更新后的记录都当作参数传给 InnoDB 层让 InnoDB 真正的执行更新记录的操作 开启事务 InnoDB 层更新记录前首先要记录相应的 undo log因为这是更新操作需要把被更新的列的旧值记下来也就是要生成一条 undo logundo log 会写入 Buffer Pool 中的 Undo 页面不过在内存修改该 Undo 页面后需要记录对应的 redo log。 InnoDB 层开始更新记录会先更新内存同时标记为脏页然后将记录写到 redo log 里面这个时候更新就算完成了。为了减少磁盘I/O不会立即将脏页写入磁盘后续由后台线程选择一个合适的时机将脏页写入到磁盘。这就是 WAL 技术MySQL 的写操作并不是立刻写到磁盘上而是先写 redo 日志然后在合适的时间再将修改的行数据写到磁盘上。 至此一条记录更新完了。 在一条更新语句执行完成后然后开始记录该语句对应的 binlog此时记录的 binlog 会被保存到 binlog cache并没有刷新到硬盘上的 binlog 文件在事务提交时才会统一将该事务运行过程中的所有 binlog 刷新到硬盘。 事务提交剩下的就是「两阶段提交」的事情了接下来就讲这个。 事务提交为了方便说明这里不说组提交的过程只说两阶段提交 两阶段提交把单个事务的提交拆分成了 2 个阶段分别是「准备Prepare阶段」和「提交Commit阶段」 prepare 阶段将 redo log 对应的事务状态设置为 prepare然后将 redo log 刷新到硬盘commit 阶段将 binlog 刷新到磁盘接着调用引擎的提交事务接口将 redo log 状态设置为 commit将事务设置为 commit 状态后刷入到磁盘 redo log 文件 至此一条更新语句执行完成。 不管是时刻 Aredo log 已经写入磁盘 binlog 还没写入磁盘还是时刻 B redo log 和 binlog 都已经写入磁盘还没写入 commit 标识崩溃此时的 redo log 都处于 prepare 状态。 可以看到对于处于 prepare 阶段的 redo log即可以提交事务也可以回滚事务这取决于是否能在 binlog 中查找到与 redo log 相同的 XID如果有就提交事务如果没有就回滚事务。这样就可以保证 redo log 和 binlog 这两份日志的一致性了。
所以说两阶段提交是以 binlog 写成功为事务提交成功的标识因为 binlog 写成功了就意味着能在 binlog 中查找到与 redo log 相同的 XID。
2、Redis
redis有哪些数据结构string类底层是怎么实现的
常见的有五种数据类型String字符串 Hash哈希List列表 Set集合、Zset有序集合
String 类型的应用场景缓存对象、常规计数、分布式锁、共享 session 信息等。 List 类型的应用场景消息队列但是有两个问题1. 生产者需要自行实现全局唯一 ID2. 不能以消费组形式消费数据等。 Hash 类型缓存对象、购物车等。 Set 类型聚合计算并集、交集、差集场景比如点赞、共同关注、抽奖活动等。 Zset 类型排序场景比如排行榜、电话和姓名排序等。
String 类型的底层的数据结构实现主要是 SDS简单动态字符串。
SDS 不仅可以保存文本数据还可以保存二进制数据。因为 SDS 使用 len 属性的值而不是空字符来判断字符串是否结束并且 SDS 的所有 API 都会以处理二进制的方式来处理 SDS 存放在 buf[] 数组里的数据。所以 SDS 不光能存放文本数据而且能保存图片、音频、视频、压缩文件这样的二进制数据。SDS 获取字符串长度的时间复杂度是 O(1)。 因为 C 语言的字符串并不记录自身长度所以获取长度的复杂度为 O(n)而 SDS 结构里用 len 属性记录了字符串长度所以复杂度为 O(1)。Redis 的 SDS API 是安全的拼接字符串不会造成缓冲区溢出。因为 SDS 在拼接字符串之前会检查 SDS 空间是否满足要求如果空间不够会自动扩容所以不会导致缓冲区溢出的问题。
List 类型的底层数据结构是由双向链表或压缩列表实现的
如果列表的元素个数小于 512 个默认值可由 list-max-ziplist-entries 配置列表每个元素的值都小于 64 字节默认值可由 list-max-ziplist-value 配置Redis 会使用压缩列表作为 List 类型的底层数据结构如果列表的元素不满足上面的条件Redis 会使用双向链表作为 List 类型的底层数据结构
Hash 类型的底层数据结构是由压缩列表或哈希表实现的
如果哈希类型元素个数小于 512 个默认值可由 hash-max-ziplist-entries 配置所有值小于 64 字节默认值可由 hash-max-ziplist-value 配置的话Redis 会使用压缩列表作为 Hash 类型的底层数据结构如果哈希类型元素不满足上面条件Redis 会使用哈希表作为 Hash 类型的底层数据结构。
Set 类型的底层数据结构是由哈希表或整数集合实现的
如果集合中的元素都是整数且元素个数小于 512 默认值set-maxintset-entries配置个Redis 会使用整数集合作为 Set 类型的底层数据结构如果集合中的元素不满足上面条件则 Redis 使用哈希表作为 Set 类型的底层数据结构。
Zset 类型的底层数据结构是由压缩列表或跳表实现的
如果有序集合的元素个数小于 128 个并且每个元素的值小于 64 字节时Redis 会使用压缩列表作为 Zset 类型的底层数据结构如果有序集合的元素不满足上面的条件Redis 会使用跳表作为 Zset 类型的底层数据结构
redis效率高的原因
为什么是redis区别有哪些。 高度优化的数据结构Redis在底层实现中使用了多种高效的数据结构以支持不同类型的值。 异步非阻塞IO模型Redis使用了异步非阻塞IO模型充分利用了操作系统提供的异步IO特性可以在单线程下处理多个并发连接。这种模型使得Redis能够处理大量的并发请求提供高吞吐量。
为什么用 Redis 作为 MySQL 的缓存
主要是因为 Redis 具备「高性能」和「高并发」两种特性。假如用户第一次访问 MySQL 中的某些数据。这个过程会比较慢因为是从硬盘上读取的。将该用户访问的数据缓存在 Redis 中这样下一次再访问这些数据的时候就可以直接从缓存中获取了操作 Redis 缓存就是直接操作内存所以速度相当快。单台设备的 Redis 的 **QPSQuery Per Second每秒钟处理完请求的次数 是 MySQL 的 10 倍**Redis 单机的 QPS 能轻松破 10w而 MySQL 单机的 QPS 很难破 1w。
redis缓存设计 缓存雪崩击穿穿透
Redis 缓存设计 通常我们为了保证缓存中的数据与数据库中的数据一致性会给 Redis 里的数据设置过期时间当缓存数据过期后用户访问的数据如果不在缓存里业务系统需要重新生成缓存因此就会访问数据库并将数据更新到 Redis 里这样后续请求都可以直接命中缓存。
缓存雪崩 那么当大量缓存数据在同一时间过期失效时如果此时有大量的用户请求都无法在 Redis 中处理于是全部请求都直接访问数据库从而导致数据库的压力骤增严重的会造成数据库宕机从而形成一系列连锁反应造成整个系统崩溃这就是缓存雪崩的问题。
将缓存失效时间随机打散 我们可以在原有的失效时间基础上增加一个随机值比如 1 到 10 分钟这样每个缓存的过期时间都不重复了也就降低了缓存集体失效的概率。设置缓存不过期 我们可以通过后台服务来更新缓存数据从而避免因为缓存失效造成的缓存雪崩也可以在一定程度上避免缓存并发问题。
如何避免缓存击穿
我们的业务通常会有几个数据会被频繁地访问比如秒杀活动这类被频地访问的数据被称为热点数据。如果缓存中的某个热点数据过期了此时大量的请求访问了该热点数据就无法从缓存中读取直接访问数据库数据库很容易就被高并发的请求冲垮这就是缓存击穿的问题。
可以发现缓存击穿跟缓存雪崩很相似你可以认为缓存击穿是缓存雪崩的一个子集。 应对缓存击穿可以采取前面说到两种方案
互斥锁方案Redis 中使用 setNX 方法设置一个状态位表示这是一种锁定状态保证同一时间只有一个业务线程请求缓存未能获取互斥锁的请求要么等待锁释放后重新读取缓存要么就返回空值或者默认值。不给热点数据设置过期时间由后台异步更新缓存或者在热点数据准备要过期前提前通知后台线程更新缓存以及重新设置过期时间
如何避免缓存穿透
当发生缓存雪崩或击穿时数据库中还是保存了应用要访问的数据一旦缓存恢复相对应的数据就可以减轻数据库的压力而缓存穿透就不一样了。当用户访问的数据既不在缓存中也不在数据库中导致请求在访问缓存时发现缓存缺失再去访问数据库时发现数据库中也没有要访问的数据没办法构建缓存数据来服务后续的请求。那么当有大量这样的请求到来时数据库的压力骤增这就是缓存穿透的问题。
缓存穿透的发生一般有这两种情况 业务误操作缓存中的数据和数据库中的数据都被误删除了所以导致缓存和数据库中都没有数据 黑客恶意攻击故意大量访问某些读取不存在数据的业务
非法请求的限制 设置空值或者默认值 使用布隆过滤器快速判断数据是否存在避免通过查询数据库来判断数据是否存在
redis缓存过期什么策略
过期策略 惰性删除定期删除
redis数据持久化
数据持久化 AOF 日志每执行一条写操作命令就把该命令以追加的方式写入到一个文件里 RDB 快照将某一时刻的内存数据以二进制的方式写入磁盘
redis主从复制
主从复制共有三种模式全量复制、基于长连接的命令传播、增量复制。
1、主从服务器第一次同步的时候就是采用全量复制此时主服务器会两个耗时的地方分别是生成 RDB 文件和传输 RDB 文件。为了避免过多的从服务器和主服务器进行全量复制可以把一部分从服务器升级为「经理角色」让它也有自己的从服务器通过这样可以分摊主服务器的压力。
2、第一次同步完成后主从服务器都会维护着一个长连接主服务器在接收到写操作命令后就会通过这个连接将写命令传播给从服务器来保证主从服务器的数据一致性。
3、如果遇到网络断开增量复制就可以上场了不过这个还跟 repl_backlog_size 这个大小有关系。
如果它配置的过小主从服务器网络恢复时可能发生「从服务器」想读的数据已经被覆盖了那么这时就会导致主服务器采用全量复制的方式。所以为了避免这种情况的频繁发生要调大这个参数的值以降低主从服务器断开后全量同步的概率。
redis主从故障转移
Redis 在 2.8 版本以后提供的哨兵*Sentinel*机制它的作用是实现主从节点故障转移。它会监测主节点是否存活如果发现主节点挂了它就会选举一个从节点切换为主节点并且把新主节点的相关信息通知给从节点和客户端。
哨兵一般是以集群的方式部署至少需要 3 个哨兵节点哨兵集群主要负责三件事情监控、选主、通知。
哨兵节点通过 Redis 的发布者/订阅者机制哨兵之间可以相互感知相互连接然后组成哨兵集群同时哨兵又通过 INFO 命令在主节点里获得了所有从节点连接信息于是就能和从节点建立连接并进行监控了。
1、第一轮投票判断主节点下线
当哨兵集群中的某个哨兵判定主节点下线主观下线后就会向其他哨兵发起命令其他哨兵收到这个命令后就会根据自身和主节点的网络状况做出赞成投票或者拒绝投票的响应。
当这个哨兵的赞同票数达到哨兵配置文件中的 quorum 配置项设定的值后这时主节点就会被该哨兵标记为「客观下线」。
2、第二轮投票选出哨兵leader
某个哨兵判定主节点客观下线后该哨兵就会发起投票告诉其他哨兵它想成为 leader想成为 leader 的哨兵节点要满足两个条件
第一拿到半数以上的赞成票第二拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值。
3、由哨兵 leader 进行主从故障转移
选举出了哨兵 leader 后就可以进行主从故障转移的过程了。该操作包含以下四个步骤
第一步在已下线主节点旧主节点属下的所有「从节点」里面挑选出一个从节点并将其转换为主节点选择的规则 过滤掉已经离线的从节点过滤掉历史网络连接状态不好的从节点将剩下的从节点进行三轮考察优先级、复制进度、ID 号。在每一轮考察过程中如果找到了一个胜出的从节点就将其作为新主节点。 第二步让已下线主节点属下的所有「从节点」修改复制目标修改为复制「新主节点」第三步将新主节点的 IP 地址和信息通过「发布者/订阅者机制」通知给客户端第四步继续监视旧主节点当这个旧主节点重新上线时将它设置为新主节点的从节点
redis并发竞争与分布式锁
首先需要澄清一个事实redis服务端是单线程处理客户端请求也就是说客户端请求在服务端是串行化执行的因此对服务端来说并不存在并发问题。但业务方却存在并发操作redis中的同一个key的情况。所以如何让A客户端知道B客户端正在操作它想操作的 key就成了必须要讨论的问题。
虽然Redis是一个单线程的系统但是它仍然可以支持分布式锁的实现。这是因为分布式锁的目的不是为了实现并发执行而是为了在分布式环境下实现资源的互斥访问。
在分布式系统中多个进程或节点可能同时访问共享资源因此需要一种机制来确保在任何时候只有一个进程或节点能够获取到资源的访问权限以避免数据竞争和冲突。写操作当多个进程需要同时对同一个数据进行写入时为了避免数据不一致和竞争条件需要使用分布式锁来保证只有一个进程能够进行写操作确保数据的正确性。
Redis在内部采用单线程模型来处理客户端请求这意味着它在任何给定的时间点只能处理一个请求。这使得Redis能够避免多个线程之间的竞争条件和数据不一致问题。
Redis锁知道吗redis有分布式锁Redis 的 SET 命令有个 NX 参数可以实现「key不存在才插入加锁成功」反之失败 SET lock_key unique_value NX PX 10000 加锁时标识客户端和设置过期时间。 问题场景多个线程同时写key 本来需求 1,2,3,4,5 最后结果为5 可能最后结果 2,1,3,5,4 最后结果为4 产生异常与预期不同。
解决1、分布式锁时间戳 解决2、消息队列操作串行化。
并发竞争key这个问题简单讲就是 同时有多个客户端去set一个key。 分布式锁是用于分布式环境下并发控制的一种机制用于控制某个资源在同一时刻只能被一个应用所使用。如下图所示
分布式锁即SET命令NX参数实现 如果 key 不存在则显示插入成功可以用来表示加锁成功 如果 key 存在则会显示插入失败可以用来表示加锁失败。 Redlock 算法的基本思路是让客户端和多个独立的 Redis 节点依次请求申请加锁如果客户端能够和半数以上的节点成功地完成加锁操作那么我们就认为客户端成功地获得分布式锁否则加锁失败。 Redlock 算法加锁三个过程 第一步是客户端获取当前时间t1。第二步是客户端按顺序依次向 N 个 Redis 节点执行加锁操作 加锁操作使用 SET 命令带上 NXEX/PX 选项以及带上客户端的唯一标识。如果某个 Redis 节点发生故障了为了保证在这种情况下Redlock 算法能够继续运行我们需要给「加锁操作」设置一个超时时间不是对「锁」设置超时时间而是对「加锁操作」设置超时时间加锁操作的超时时间需要远远地小于锁的过期时间一般也就是设置为几十毫秒。 第三步是一旦客户端从超过半数大于等于 N/21的 Redis 节点上成功获取到了锁就再次获取当前时间t2然后计算计算整个加锁过程的总耗时t2-t1。如果 t2-t1 锁的过期时间此时认为客户端加锁成功否则认为加锁失败。 可以看到加锁成功要同时满足两个条件简述如果有超过半数的 Redis 节点成功的获取到了锁并且总耗时没有超过锁的有效时间那么就是加锁成功
3、MongoDB
NoSQL是什么
NoSQL 的全称是 Not Only SQL也可以理解非关系型的数据库是一种新型的革命式的数据库设计方式不过它不是为了取代传统的关系型数据库而被设计的它们分别代表了不同的数据库设计思路。
MongoDB是一个介于关系数据库和非关系数据库之间的产品
它是一个内存数据库数据都是放在内存里面的。 对数据的操作大部分都在内存中但 MongoDB 并不是单纯的内存数据库。
非关系型数据库(nosql ),属于文档型数据库。先解释一下文档的数据库即可以存放xml、json、bson类型系那个的数据。这些数据具备自述性self-describing呈现分层的树状数据结构。数据结构由键值(keyvalue)对组成。
MongoDB和redis的区别
MongoDB和Redis都是NoSQL采用结构型数据存储。二者在使用场景中存在一定的区别这也主要由于
二者在内存映射的处理过程持久化的处理方法不同。 MongoDB建议集群部署更多的考虑到集群方案 Redis更偏重于进程顺序写入虽然支持集群也仅限于主-从模式。
MongoDB是由哪种语言写的
MongoDB用c编写的流行的开源数据库MySQL也是用C开发的。C1983年发行是一种使用广泛的计算机程序设计语言。它是一种通用程序设计语言支持多重编程模式。
MongoDB有哪些适用场景
网站数据Mongo 非常适合实时的插入更新与查询并具备网站实时数据存储所需的备份、扩容 缓存由于性能很高Mongo 也适合作为信息基础设施的缓存层在系统重启之后由Mongo搭建的持久化缓存层可以避免数据源过载 大尺寸、低价值的数据存储使用传统的关系型数据库存储一些大尺寸低价值数据时会比较浪费比如日志在此之前很多时候程序员往往会选择传统的文件进行存储 高伸缩性的场景Mongo 非常适合由数十或数百台服务器组成的数据库Mongo 的路线图中已经包含对 MapReduce 以及集群高可用的解决方案 用于对象及JSON 数据的存储Mongo 的BSON 数据格式非常适合文档化格式的存储及查询 具体的应用场景
游戏场景使用 MongoDB 存储游戏用户信息用户的装备、积分等直接以内嵌文档的形式存储经常修改方便查询、更新 物流场景使用 MongoDB 存储订单信息订单状态在运送过程中会不断更新以 MongoDB 内嵌数组的形式来存储一次查询就能将订单所有的变更读取出来 社交场景使用 MongoDB 存储用户信息以及用户发表的朋友圈信息通过地理位置索引实现附近的人、地点等功能 物联网场景使用 MongoDB 存储所有接入的智能设备信息以及设备汇报的日志信息并对这些信息进行多维度的分析 直播使用 MongoDB 存储用户信息、礼物信息变化大等
MongoDB为啥速度快
写操作MongoDB比传统数据库快的根本原因是Mongo使用的内存映射技术 写入数据时候只要在内存里完成就可以返回给应用程序这样并发量自然就很高。而保存到硬体的操作则在后台异步完成。注意MongoDB在2.4就已经是默认安全写了具体实现在驱动程序里所以楼上有同学的回答说是”默认不安全“应该是基于2.2或之前版本的。
读操作MongoDB快的原因是 1MongoDB的设计要求你常用的数据working set)可以在内存里装下。这样大部分操作只需要读内存自然很快。 2文档性模式设计一般会是的你所需要的数据都相对集中在一起内存或硬盘大家知道硬盘读写耗时最多是随机读写所产生的磁头定位时间数据集中在一起则减少了关系性数据库需要从各个地方去把数据找过来然后Join所耗费的随机读时间
另外一个就是如Mongo是分布式集群所以可以平行扩展。目前一般的百万次并发量都是通过几十上百个节点的集群同时实现。这一点MySQL基本无法做到或者要花很大定制的代价
MongoDB存储引擎 索引
MongoDB支持的引擎有WiredTigerMMAPv1和In-Memory。
从MongoDB 3.2 版本开始WiredTiger成为MongDB默认的Storage Engine用于将数据持久化存储到硬盘文件中WiredTiger提供文档级别Document-Level的并发控制检查点CheckPoint数据压缩和本地数据加密 Native Encryption等功能。
几大索引类型 单键索引 (Single Field) 过期索引TTL Time To Live 复合索引(Compound Index 多键索引Multikey indexes 地理空间索引Geospatial Index 全文索引 哈希索引 Hashed Index
既然是非sql数据库就应该好好利用其支持文本\复杂数据类型的优势通过表结构的设计保证数据库的使用者通过单条查询就能拿到数据,
而B树的遍历查询效率虽然不如B树但是由于非叶子节点直接就能拿到并返回数据因此单条查询速度是快于B树的
MongoDB 集群搭建 主从复制
MongoDB集群 MongoDB有三种集群的搭建方式
Replica Set 副本集Sharding 切片Master-Slaver 主从不推荐使用
其中Sharding集群也是三种集群中最复杂的。副本集比起主从可以实现故障转移非常使用
mongoDB目前已不推荐使用主从模式取而代之的是副本集模式。副本集其实一种互为主从的关系可理解为主主。 副本集指将数据复制多份保存不同服务器保存同一份数据在出现故障时自动切换。对应的是数据冗余、备份、镜像、读写分离、高可用性等关键词 而分片则指为处理大量数据将数据分开存储不同服务器保存不同的数据它们的数据总和即为整个数据集。追求的是高性能。
在生产环境中通常是这两种技术结合使用分片副本集。
下面来简单说一下这几种配置方法
主从复制 主从复制是MongoDB (opens new window)最常用的复制方式,也是一个简单的数据库 (opens new window)同步备份的集群技术,这种方式很灵活.可用于备份,故障恢复,读扩展等. 最基本的设置方式就是建立一个主节点和一个或多个从节点,每个从节点要知道主节点的地址。采用双机备份后主节点挂掉了后从节点可以接替主机继续服务。所以这种模式比单节点的高可用性要好很多。
实现原理
在主从结构中
主节点的操作记录成为oplogoperation log。oplog存储在一个系统数据库local的集合oplog.$main中这个集合的每个文档都代表主节点上执行的一个操作。从服务器会定期从主服务器中获取oplog记录然后在本机上执行对于存储oplog的集合MongoDB采用的是固定集合也就是说随着操作过多新的操作会覆盖旧的操作主从复制的其他设置项 –only 从节点指定复制某个数据库,默认是复制全部数据库 –slavedelay 从节点设置主数据库同步数据的延迟(单位是秒) –fastsync 从节点以主数据库的节点快照为节点启动从数据库 –autoresync 从节点如果不同步则从新同步数据库(即选择当通过热添加了一台从服务器之后从服务器选择是否更新主服务器之间的数据) –oplogSize 主节点设置oplog的大小(主节点操作记录存储到local的oplog中)
副本集 Replica Sets
mongodb 不推荐主从复制,推荐建立副本集(Replica Set)来保证1个服务挂了,可以有其他服务顶上,程序正常运行,几个服务的数据都是一样的,后台自动同步。主从复制其实就是一个单副本的应用没有很好的扩展性饿容错性。然而副本集具有多个副本保证了容错性就算一个副本挂掉了还有很多个副本存在并且解决了主节点挂掉后整个集群内会自动切换的问题。副本集比传统的Master-Slave主从复制有改进的地方就是它可以进行故障的自动转移,如果我们停掉复制集中的一个成员,那么剩余成员会再自动选举一个成员,作为主库。Replica Set 使用的是 n 个 mongod 节点,构建具备自动的容错功能(auto-failover),自动恢复的(auto-recovery)的高可用方案。使用 Replica Set 来实现读写分离。通过在连接时指定或者在主库指定 slaveOk,由Secondary 来分担读的压力,Primary 只承担写操作。对于 Replica Set 中的 secondary 节点默认是不可读的。
分片集群
Sharding cluster是一种可以水平扩展的模式,在数据量很大时特给力,实际大规模应用一般会采用这种架构去构建。sharding分片很好的解决了单台服务器磁盘空间、内存、cpu等硬件资源的限制问题把数据水平拆分出去降低单节点的访问压力。每个分片都是一个独立的数据库所有的分片组合起来构成一个逻辑上的完整的数据库。因此分片机制降低了每个分片的数据操作量及需要存储的数据量达到多台服务器来应对不断增加的负载和数据的效果。
4、数据库原理
数据库的分类sql nosql 关系数据库SQL 是创建在关系模型基础上的数据库借助于集合代数等数学概念和方法来处理数据库中的数据。现实世界中的各种实体以及实体之间的各种联系均用关系模型来表示。 数据库包括一个或多个表 表关系 Relation是以列和行的形式组织起来的数据的集合 列属性 Attribute在数据库中经常被称为字段 行值组 Tuple在数据库中经常被称为记录 我们可以理解为系型数据库是指采用了关系模型来组织数据的数据库。 关系型数据库的主要代表 SQL ServerOracleMySQLPostgreSQL。 关系型数据库优点 事务一致性通过事务处理保持数据的一致性 复杂查询支持SQL可以进行 JOIN 等复杂查询 容易理解二维表结构是非常贴近逻辑世界的一个概念关系模型相对网状、层次等其他模型来说更容易理解 使用方便通用的 SQL 语言使得操作关系型数据库非常方便 易于维护丰富的完整性(实体完整性、参照完整性和用户定义的完整性)大大减低了数据冗余和数据不一致的概率关系型数据库缺点 读写性能在数据量达到一定规模时由于关系型数据库的系统逻辑非常复杂为了维护一致性使得其非常容易发生死锁等的并发问题所以导致其读写速度下滑非常严重 表结构更新表结构可以在被定义之后更新但是如果有比较大的结构变更的话就会变得比较复杂 高并发网站的用户并发性非常高往往达到每秒上万次读写请求对于传统关系型数据库来说硬盘I/O是一个很大的瓶颈 海量数据对于关系型数据库来说在一张包含海量数据的表中查询效率是非常低的 非关系型数据库NoSQL 是对不同于传统的关系数据库的数据库管理系统的统称。 当代典型的关系数据库在一些数据敏感的应用中表现了糟糕的性能例如为巨量文档创建索引、高流量网站的网页服务以及发送流式媒体。关系型数据库的典型实现主要被调整用于执行规模小而读写频繁或者大批量极少写访问的事务。 NoSQL 的结构通常提供弱一致性的保证如最终一致性或交易仅限于单个的数据项。 NoSQL 提出另一种理念例如以键值对存储且结构不固定每一个元组可以有不一样的字段每个元组可以根据需要增加一些自己的键值对这样就不会局限于固定的结构可以减少一些时间和空间的开销。 非关系型数据库分类 由于非关系型数据库本身天然的多样性以及出现的时间较短相比关系型数据库非关系型数据库非常多并且大部分都是开源的。 非关系型数据库严格上不是一种数据库应该是一种数据结构化存储方法的集合。依据结构化方法以及应用场合的不同主要分为以下几类 面向高性能并发读写的 key-value 数据库 key-value数据库的主要特点即使具有极高的并发读写性能RedisTokyo CabinetFlare 就是这类的代表面向海量数据访问的面向文档数据库 这类数据库的特点是可以在海量的数据中快速的查询数据典型代表为 MongoDB 以及 CouchDB面向可扩展性的分布式数据库这类数据库想解决的问题就是传统数据库存在可扩展性上的缺陷这类数据库可以适应数据量的增加以及数据结构的变化
事务与隔离级别
对照一下应该都在上面的mysql里啦
一、事务 概念 ACID AUTOCOMMIT
二、并发一致性问题 丢失修改 读脏数据 不可重复读 幻影读
三、封锁 封锁粒度 封锁类型 封锁协议 MySQL 隐式与显式锁定
四、隔离级别 未提交读READ UNCOMMITTED 提交读READ COMMITTED 可重复读REPEATABLE READ 可串行化SERIALIZABLE
五、多版本并发控制 基本思想 版本号 Undo 日志 ReadView 快照读与当前读
六、Next-Key Locks Record Locks Gap Locks Next-Key Locks
关系数据库设计理论函数依赖异常 范式
函数依赖
记 A-B 表示 A 函数决定 B也可以说 B 函数依赖于 A。如果 {A1A2… An} 是关系的一个或多个属性的集合该集合函数决定了关系的其它所有属性并且是最小的那么该集合就称为键码。对于 A-B如果能找到 A 的真子集 A’使得 A’- B那么 A-B 就是部分函数依赖否则就是完全函数依赖。对于 A-BB-C则 A-C 是一个传递函数依赖。
异常
以下的学生课程关系的函数依赖为 {Sno, Cname} - {Sname, Sdept, Mname, Grade}键码为 {Sno, Cname}。也就是说确定学生和课程之后就能确定其它信息。
范式 范式理论是为了解决以上提到四种异常。 高级别范式的依赖于低级别的范式1NF 是最低级别的范式。
第一范式 (1NF) 属性不可分。第二范式 (2NF) 每个非主属性完全函数依赖于键码。 可以通过分解来满足。 第三范式 (3NF) 非主属性不传递函数依赖于键码。
关系数据库概念设计ER图
Entity-Relationship有三个组成部分实体、属性、联系。
用来进行关系型数据库系统的概念设计。
#实体的三种联系
包含一对一一对多多对多三种。如果 A 到 B 是一对多关系那么画个带箭头的线段指向 B 如果是一对一画两个带箭头的线段 如果是多对多画两个不带箭头的线段。表示出现多次的关系 一个实体在联系出现几次就要用几条线连接。
联系的多向性 虽然老师可以开设多门课并且可以教授多名学生但是对于特定的学生和课程只有一个老师教授这就构成了一个三元联系。
表示子类 用一个三角形和两条线来连接类和子类与子类有关的属性和联系都连到子类上而与父类和子类都有关的连到父类上。
1. 画ER图
2. 写关系模式
3. 分析范式
4. 分解范式E-R图
画E-R图实体关系属性
转关系模式
对于[A]-n-C-1-[B]可以将C放在A的属性中再把B的主码放在A的属性中。(1:1随意放一端)
对于[A]-n-C-m-[B]两边的主码自己的属性再开一个关系模式。
具体例子来说、
对于实体[A] 有 n1的关系需要把另一端的主码作为外码放过来还要标上关系的属性。否则就不用理会。
对于m:n的关系连三个的关系也要单独开一个自己的属性加两边的主码。求AB闭包从AB出发使用关系递推直到推不出来更多(或者全部推出来)为止。
求最小依赖集右部只有一个属性。 然后对于所有关系除本求包如果能推出右边的就可以把这个关系删掉最后左边最小化看看有没有能删的范式重要
1NF-2NF消除非主属性对码的部分依赖。比如主码(A,B)存在A-C关系。分解的时候把AC拆分成一个关系即可。
2NF-3NF消除非主属性对码的传递依赖。比如A-B,B-D。分解的时候把BD拆成一个关系。
3NF-BCNF每一个左边的决定因素都是候选码。如果有{AB,CB} {(A,B)-C, C-D}C是决定性不是候选码所以不是BCNF。 定义回答的时候不存在部分和传递依赖。几个注意点
1.第二三范式是针对非主属性的BCNF范式是针对主属性的。
一定要注意 判断部分依赖的时候 看后面的是不是 主属性元素
2.对于传递函数依赖左边的一定是码码-属性属性-另一个属性
举个例子{AB-C, CE-D} 候选码 ABE 主属性 A B E
而AB-C, CE-D, 不是传递依赖因为AB不是码 强调范式例题
如何判断候选码从这几个属性出发能推出所有的属性那么就是候选码。
写出基本函数依赖?
每个商店每个商品只在一个部门销售 ---- (商店编号商品编号)-部门编号
每个商店每个部门只有一个负责人 ---- (商店编号部门编号)-负责人(商店编号商品编号)-数量
判断第几范式
根据依赖发现没有左边单个的能唯一的决定右边的所以是2NF
然后(商店编号商品编号)-部门编号-负责人存在传递所以不是3NF
如何分解
先分解关系R2(商店编号部门编号负责人)
得出R1(商店编号商品编号数量部门编号)特殊样例
对于只有一个函数依赖的关系没有传递和部分直接3NF左边直接是码那就BCNF了。
F{Y-Z, XZ-Y}, 候选码XY和XZ, 主属性XYZ没有部分依赖没有传递依赖所以3NF。Y是决定性但不是码所以不是BCNF。占坑待填
无损分解保持依赖关系树查询有关锁的题这里ABD也是3NF范式我来解释一下
F{AB-C, AB-E, CDE-AB}
第一步候选码ABD / CDE 主属性ABCDE
第二步其实这里与其他题不同这个所有属性都是主属性而第二第三范式是对非主属性的要求所以直接就可以判断它是3NF范式以上然后再看决定性因素AB是决定性因素但不包含码所以是3NF范式
注意一点就是第二三范式 是对非主属性的 BCNF范式 是对主属性的所以这里的AB-C, AB-E 是主属性对码的部分依赖参考资料
https://www.bilibili.com/video/BV1P5411e7rU
关系数据库sql语法
插入 2 重要
insert
into student(Sno, Sname, Ssex, Sdept, Sage)
values(201215128,陈冬,男,CS,18);删除与select语句格式相同
DELETE
FROM sc
WHERE Cno 1;增加列alter对应改变table对应from改变类型
ALTER
TABLE Student
ADD Sbirthday datetime NULL;
ALTER
TABLE SC
CHANGE Grade Grade INT;更新
update sc
SET Grade 89
WHERE Cno 3 AND Sno 201215122;---------------------------------------------------------------------建视图 2与select语句一一对应
create VIEW Boy_Student AS
select * From student WHERE ssex男;创建表与创建视图一样把as_select换成()即可
CREATE TABLE sc(Sno CHAR(9) NOT NULL,Cno CHAR(4) NOT NULL, Grade SMALLINT,PRIMARY KEY(Sno,Cno),FOREIGN KEY(Sno) REFERENCES Student(Sno), FOREIGN KEY(Cno) REFERENCES Course(Cno)
);创建数据库与创建表一样
CREATE DATABASE Student_DB;
SHOW DATABASES;
USE Student_DB;---------------------------------------------------------------------单表查询条件 2
SELECT * FROM Student
WHERE Class95031 OR Ssex女平均成绩avg子查询 2
DISTINCT 可以消除重复数据可以select DISTINCT sno from score;
AVG计算的时候去掉重复数据,所以加一个distinct
SELECT AVG(DISTINCT Degree)
FROM Score
WHERE Sno IN(SELECT SnoFROM StudentWHERE Class95031
)函数查询
SELECT COUNT(DISTINCT Sno) //不能与*一起用*可以单独用
FROM Student
WHERE Class95031;分组查询 2
每组只选择一个显示, having判断是否显示该组(根据整租), where根据显示结果判断是否显示
SELECT Sno
FROM Score
GROUP BY Sno
HAVING MIN(Degree)70 AND MAX(Degree)90多表查询 4
因为可能有两个name列所以多表的时候用表名.列名区分
SELECT Teacher.Tname, Course.Cno, Score.Degree
FROM Teacher, Course, Score
WHERE Teacher.Tno Course.Tno AND Course.Cno Score.Cno AND Teacher.Tname张旭连接查询
是多表查询的一种多表基于笛卡尔积连接以一张表全部数据为根基
等值连接即内连接INNER JOIN等价于where写法
自然连接范围更大即外连接分为LEFT OUTER JOIN 和RIGHT OUTER JOIN
SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
LEFT OUTER JOIN classes c
ON s.class_id c.id;字符串查询
%能匹配任意长度的字符_只能匹配任意一个字符。
SELECT * FROM Student WHERE Sname LIKE %王%;查询空值
SELECT * FROM 表名 WHERE 字段名 IS NULL;
SELECT * FROM 表名 WHERE 字段名 IS NOT NULL;---------------------------------------------------------------------
触发器
插入学生信息后,新建默认选课并给null成绩
Create trigger t2
AFTER delete ON student FOR EACH ROW
BEGIN
UPDATE course
SET Tno NULL
WHERE Tno old.Tno;
END存储函数
本质一样存储函数的限制比较多,例如不能用临时表,只能返回一个变量,而存储过程的限制较少。
drop function if exists myfunc;
create function myfunc(a int, b int) returns int
begindeclare str char(3);declare x int default 0;declare bir datetime default null;set xab;return x;
end
select myfunc(2,3);存储过程
drop procedure if exists myproce;
Create procedure myproce()
begindeclare i int default 1;while i 10 doselect i;set i i1;end while;
end
call myproce;游标指向一个select查询结果
declare mycursor CURSOR for
SELECT Sno, Sbirthday FROM Student ORDER BY Sno;
OPEN mycursor; //打开游标
FETCH mycursor INTO na, birth; //检索游标(拿出一行把该行的各个列值保存到各个变量中)
CLOSE mycursor;