成都公司的网站制作,网站设计多少钱,wordpress 入侵视频,大学生创意电子产品设计文章目录 一条sql语句的查询流程有哪些数据库存储引擎#xff0c;各自的区别数据库的三大范式事务的四大特性#xff08;含隔离级别#xff09;MySQL四种隔离机制的底层实现#xff08;如何解决幻读 #xff09;MySQL有哪几种锁#xff0c;分别怎么实现数据库中有哪些索引… 文章目录 一条sql语句的查询流程有哪些数据库存储引擎各自的区别数据库的三大范式事务的四大特性含隔离级别MySQL四种隔离机制的底层实现如何解决幻读 MySQL有哪几种锁分别怎么实现数据库中有哪些索引类型以及数据结构? 最左匹配原则?最左匹配前缀原则索引失效的情况hash索引与B树如何选用怎么对一条查询语句包括索引调优优化数据库的方案MySQL主从复制的原理MySQL如何保证事务的特性MySQL和Redis的区别各自使用的场景以及原因redis的五大数据结构redis为什么快Redis雪崩、击穿、穿透Redis缓存淘汰策略Redis持久化策略mongo原理 一条sql语句的查询流程
基本查询流程
1客户端通过数据库连接池发送一条查询给服务器。 2服务器先检查缓存如果命中缓存则立刻返回缓存结果。否则进入下一阶段。 3服务器端的分析器进行语法验证、SQL解析、预处理 4优化器依据成本最小原则来选择索引生成对应的执行计划。 5MySQL根据优化器生成的执行计划再调用存储引擎的API来执行查询。 6将查询结果返回给客户端。
缓冲池
每次查询的结果会存到缓冲池 Buffer Pool 中这样后面再有请求的时候就会先从缓冲池中去查询如果没有再去磁盘中查找然后在放到 Buffer Pool 中。
优化器
MySQL使用基于成本的查询优化器(Cost-Based OptimizerCBO)。它会尝试预测一个查询使用某种执行计划时的成本并选择其中成本IO CPU 成本最少的一个IO 成本即从磁盘把数据加载到内存的成本和页大小有关CPU 成本和读取数据行数有关。
优化器会根据优化规则对关系表达式进行转换这里的转换是说一个关系表达式经过优化规则后会生成另外一个关系表达式同时原有表达式也会保留经过一系列转换后会生成多个执行计划然后CBO会根据统计信息和代价模型(Cost Model)计算每个执行计划的Cost从中挑选Cost最小的执行计划。
sql查询语句详解 每一个复杂sql语句执行都会拆分成若干步骤并且都是从 FROM 开始执行的。每个步骤都会为下一个步骤生成一个虚拟表这个虚拟表将作为下一个执行步骤的输入。以下面的语句为例执行步骤如序号所示
序号sql命令描述1FORM对FROM的左边的表和右边的表计算笛卡尔积。产生虚表VT12ON对虚表VT1进行ON筛选只有那些符合的行才会被记录在虚表VT2中。3JOIN如果指定了OUTER JOIN比如left join、 right join那么保留表中未匹配的行就会作为外部行添加到虚拟表VT2中产生虚拟表VT3, 如果 from子句中包含两个以上的表的话那么就会对上一个join连接产生的结果VT3和下一个表重复执行步骤1~3这三个步骤一直到处理完所有的表为止。4WHERE对虚拟表VT3进行WHERE条件过滤。只有符合的记录才会被插入到虚拟表VT4中。5GROUP BY根据group by子句中的列对VT4中的记录进行分组操作产生VT5.with CUBE ROLLUP: 对表VT5进行cube或者rollup操作产生表VT6.6HAVING对虚拟表VT6应用having过滤只有符合的记录才会被 插入到虚拟表VT7中。7SELECT执行select操作选择指定的列插入到虚拟表VT8中。8DISTINCT对VT8中的记录进行去重。产生虚拟表VT9.9ORDER BY将虚拟表VT9中的记录按照order_by_list进行排序操作产生虚拟表VT10.10LIMIT取出指定行的记录产生虚拟表VT11, 并将结果返回。 链接 https://juejin.cn/post/6844903655439597582 https://www.nowcoder.com/discuss/353157489787084800 有哪些数据库存储引擎各自的区别
引擎查看命令
SHOW ENGINES
SHOW VARIABLES LIKE storage_engine;输出
MyISAM存储引擎
1锁级别为表级锁 MyISAM的表锁有两种方式一种是读共享锁另一种是写独占锁读共享锁是指当一个请求读取一个表时也允许其他请求读取这个表而写独占锁的就是指当一个请求是修改一个表时阻塞其他请求读取和修改这个表。
2不支持事务和全文索引
3索引为非聚簇索引 聚簇索引索引所采用的树结构的叶子节点储存的数据 非聚簇索引索引所采用的树结构的叶子节点储存的不是数据而是数据的地址 4适合查询比较频繁的场景插入更改比较少无需事务的场景
InnoDB存储引擎
1锁级别为行级锁 InnoDB的锁最高级别为行级锁但同时支持表级锁和行级锁InnoDB的行级锁是通过给索引项加锁来实现的当不通过索引时InnoDB还是启用的表级锁行级锁的获取锁和释放锁的过程都会占用很多的资源所以InnoDB不适合查询过于频繁的场所。
2支持事务和外键5.6.4版本之后支持全文索引FullText InnoDB的设计是为了适应高并发的场景所以InnoDB提供了具有提交、回滚和崩溃恢复能力的事务同时支持外键。
// content列添加全文索引
create table test (id int(11) unsigned not null auto_increment,content text not null,primary key(id),fulltext key content_index(content)
) engineMyISAM default charsetutf8;// target为检索文本注意target有最小/大搜索长度限制
select * from fulltext_test where match(content,tag) against(target);3索引为聚簇索引 InnoDB在索引上进行了很大的优化将数据直接放在了主索引叶子上这样主索引便可以直接找到对应的数据当调用辅助索引的时候会产生回表操作辅助索引树的叶子节点储存的是主键所以会再次会到主索引上再次查找最终获取到数据。 4适合并发性较高更改表比较频繁的安全要求较高的场景
MEMORY存储引擎
1MEMORY存储引擎将表中的数据存储到内存中为查询和引用其他表数据提供快速访问。但如果mysqld进程发生异常、重启或计算机关机等等都会造成这些数据的消失所以这种存储引擎中的表的生命周期很短一般只使用一次。 2如果只是临时存放数据数据量不大并且不需要较高的数据安全性可以选择将数据保存在内存中的Memory引擎MySQL中使用该引擎作为临时表存放查询的中间结果。
总结 链接 https://www.nowcoder.com/discuss/353157391682314240?sourceSSRusers https://developer.aliyun.com/article/636314 https://www.nowcoder.com/discuss/353157479020306432 数据库的三大范式
教材中范式的定义符合某一种级别的关系模式的集合表示一个关系内部各属性之间的联系的合理化程度。看不懂~~
第一范式1NF (确保每列保持原子性)
是最基本的范式符合1NF的关系中的每个属性都不可再分或者理解为每个字段不需要再次拆分以达到更好的数据库性能。以下关系违反1NF。 仅仅符合1NF的设计仍然会存在数据冗余过大插入异常删除异常修改异常的问题。
以学校场景为例一张学生表找不到任何一条记录它们的学号相同而对应的姓名不同。所以我们可以说姓名函数依赖于学号写作 学号 → 姓名。
第二范式2NF (确保表中的每列都和主键相关)
在第一范式的基础上更进一步需要确保数据库表中的每列均与主键相关而不能只与主键的某一部分相关主要针对联合主键称为部分函数依赖。即 在一个数据库表中一个表中只能保存一种数据不能将多种数据保存在同一张数据库表中。消除这些部分函数依赖只有一个办法就是将大数据表拆分成两个或者更多个更小的数据表在拆分的过程中要达到更高一级范式的要求这个过程叫做”模式分解“。 稍微补充两个概念明确2NF和3NF的区别。
码关系中的某个属性或者某几个属性的组合用于区分每个元组可以把“元组”理解为一张表中的每条记录也就是每一行。
设 K 为某表中的一个属性或属性组假如当 K 确定的情况下该表除 K 之外的所有属性的值也就随之确定那么 K 就是码。
第三范式3NF
需要确保数据表中的每一列数据都和主键直接相关而不能间接相关。如果有可以拆分成多张表并用外键连接。3NF在2NF的基础之上消除了非主属性对于码的传递函数依赖直接相关。
消除了学号对系主任的传递函数依赖。 链接 https://www.zhihu.com/question/24696366强推 https://zhuanlan.zhihu.com/p/63146817 事务的四大特性含隔离级别
ACID四大特性
1原子性Atomicity
原子性是指事务包含的所有操作要么全部成功要么全部失败回滚因此事务的操作如果成功就必须要完全应用到数据库如果操作失败则不能对数据库有任何影响。
2一致性Consistency
一致性是指一个事务执行之前和执行之后数据库都必须处于一致性状态比如在转账中不能存在A减少B没增加的情况即要求 数据能够保持一致多个事务对统一数据读取的结果相同。
3隔离性Isolation
隔离性是当多个用户并发访问数据库时比如操作同一张表时数据库为每一个用户开启的事务不能被其他事务的操作所干扰多个并发事务之间要相互隔离。
3持久性Durability
持久性是指一个事务一旦被提交了那么对数据库中的数据的改变就是永久性的即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
隔离级别
1Read-Uncommited读未提交
读未提交最低的隔离级别允许读取尚未提交的数据变更可能导致脏读、幻读或不可重复读。
脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。当一个事务正在多次修改某个数据而在这个事务中这多次的修改都还未提交这时一个并发的事务来访问该数据就会造成两个事务得到的数据不一致。
2READ-COMMITTED读取已提交
允许读取并发事务已经提交的数据能够阻止脏读但可能导致幻读或不可重复读。
不可重复读是指在对于数据库中的某个数据一个事务范围内多次查询却返回了不同的数据值这是由于在查询间隔被另一个事务修改并提交了。实质就是在同一事务内对同一个数据的查询可能返回多个不同的结果这些结果可能是其他事务操作提交更改的。
//1.准备两个终端在此命名为 mysql 终端 1 和 mysql 终端 2再准备一张测试表 test 写入一条测试数据并调整隔离级别为 READ COMMITTED 任意一个终端执行即可。
SET session.transaction_isolation READ-COMMITTED;
create database test;
use test;
create table test(id int primary key);
insert into test(id) values(1);//2.登录 mysql 终端 1开启一个事务将 ID 为 1 的记录更新为 2 并确认记录数变更过来。
begin;
update test set id 2 where id 1;
select * from test; -- 此时看到一条记录为 2//3.登录 mysql 终端 2开启一个事务后查看表中的数据。
use test;
begin;
select * from test; -- 此时看一条 ID 为 1 的记录//4.登录 mysql 终端 1提交事务。
commit;//5.切换到 mysql 终端 2。
select * from test; -- 此时看到一条 ID 为 2 的记录READ-COMMITTED是 Oracle 和 SQL Server 的默认隔离级别。
注意区分脏读是指read uncommited data 而不可重复读是指read different data。
3REPEATABLE-READ可重复读
对同一字段的多次读取结果时一致的除非数据是被本身事务自己所修改能够阻止脏读和不可重复读但可能导致幻读。
该隔离级别是 MySQL 默认的隔离级别在同一个事务里 select 的结果是事务开始时间点的状态具体与MVCC相关后文有详述因此同样的 select 操作读到的结果会是一致的。
这就说明一个事务只能读到另一个已经提交的事务修改过的数据但是第一次读过某条记录后即使其他事务修改了该记录的值并且提交该事务之后再读该条记录时读到的仍是第一次读到的值而不是每次都读到不同的数据。
幻读是指当事务不是独立执行时发生的一种现象例如第一个事务对一个表中的数据进行了修改比如这种修改涉及到表中的“全部数据行”。同时第二个事务也修改这个表中的数据这种修改是向表中插入“一行新数据”。那么以后就会发生操作第一个事务的用户发现表中还存在没有修改的数据行就好象发生了幻觉一样。一般解决幻读的方法是增加范围锁锁定检索范围为只读这样就避免了幻读。MySQL 的 InnoDB 引擎可以通过 next-key locks、间隙锁机制来避免幻读。
注意区分不可重复读的重点是修改同样的条件, 你读取过的数据, 再次读取出来发现值不一样了而幻读的重点在于新增或者删除同样的条件, 第1次和第2次读出来的记录数不一样。
4SERIALIZABLE可串行化
最高的隔离级别完全服从 ACID 的隔离级别所有事务依次逐个执行这样事务之间就完全不可能产生干扰能够防止脏读、幻读以及不可重复读、
MySQL 数据库的 InnoDB 引擎会给读操作隐式加一把读共享锁以上3种隔离级别都允许对同一条记录进行读-读、读-写、写-读的并发操作SERIALIZABLE不允许读-写、写-读但可以读-读的并发操作。
MySQL四种隔离机制的底层实现如何解决幻读
理解InnoDB的四种隔离机制的底层实现需要先弄清楚三个完全陌生的概念undo log日志、版本链和readview。
对于InnoDB存储引擎聚簇索引记录中都包含两个“隐藏”的字段事务id(trx_id)和版本指针roll_pointertrx_id每次对某条聚簇索引记录进行改动时都会把对应的事务id赋值给trx_id隐藏列。roll_pointer每次对某条聚簇索引记录进行改动时都会把旧的版本写入到undo日志中然后这个pointer隐藏列就相当于一个指针可以通过它来找到该记录修改前的信息如此这些undo日志就可以形成一条记录链如下图即为版本链。ReadView中包含当前系统中所有活跃uncommited的读写事务id的列表把这个列表命名为为m_ids。
对于使用READ UNCOMMITTED隔离级别的事务来说直接读取记录的最新版本
对于使用SERIALIZABLE隔离级别的事务来说使用加锁的方式来访问记录。
对于使用READ COMMITTED 和 REPEATABLE READ隔离级别的事务来说就需要用到我们上边所说的版本链。
在事务生成readview时会把当前系统中正在执行的读写事务写入到m_ids列表中另外还会存储两个值min_trx_id该值代表生成readview时m_ids中的最小值。max_trx_id该值代表生成readview时系统中应该分配给下一个事务的id值。
注意max_trx_id并不是m_ids中的最大值事务id是递增分配的。比方说现在有id为123这三个事务之后id为3的记录提交了。那么一个新的读事务在生成readview时m_ids就包括1和2min_trx_id的值就是1max_trx_id的值就是4。 如果记录的trx_id列小于min_trx_id表明生成该版本的事务在生成ReadView前已经提交所以该版本可以被当前事务访问。 如果记录的trx_id列大于max_trx_id表明生成该版本的事务在生成ReadView后才生成所以该版本不可以被当前事务访问。 如果记录的trx_id列在min_trx_id和max_trx_id之间那就需要判断一下trx_id属性值是不是在m_ids列表中如果在说明创建ReadView时生成该版本的事务还是活跃的该版本不可以被访问如果不在说明创建ReadView时生成该版本的事务已经被提交该版本可以被访问。
根据以上判定规则READ COMMITTED和REPEATABLE READ机制的区别就在于何时生成ReadViewREAD COMMITTED 在每次读取数据前都生成一个ReadView当前读而REPEATABLE READ只有第一次读取数据时生成一个ReadView快照读这样可以保证每次查询版本链和ReadView(m_ids)比较得到的可见性事务基本没变化可重复读。
综上所谓的MVCCMulti-Version Concurrency Control 多版本并发控制指的就是在使用READ COMMITTD、REPEATABLE READ这两种隔离级别的事务在执行普通的SELECT操作时访问记录的版本链的过程这样子可以使不同事务的读-写、写-读操作并发执行从而提升系统性能。READ COMMITTD、REPEATABLE READ这两个隔离级别的一个很大不同就是生成ReadView的时机不同READ COMMITTD在每一次进行普通SELECT操作前都会生成一个ReadView而REPEATABLE READ只在第一次进行普通SELECT操作前生成一个ReadView之后的查询操作都重复这个ReadView就好了。 链接 https://juejin.cn/post/6844903808376504327强推 https://juejin.cn/post/6844903815863336973勘误 MySQL有哪几种锁分别怎么实现
表级锁与行级锁
1表级锁
开销小加锁快不会出现死锁锁定粒度大发生锁冲突的概率最高并发度最低。
MyISAM 和 MEMORY 存储引擎采用的是表级锁table-level locking
2行级锁
开销大加锁慢会出现死锁锁定粒度最小发生锁冲突的概率最低并发度也最高。最大程度的支持并发同时也带来了最大的锁开销。
InnoDB 存储引擎既支持行级锁row-level locking也支持表级锁但默认情况下是采用行级锁。InnoDB 行锁是通过给索引上的索引项加锁来实现的这一点 MySQL 与 Oracle 不同后者是通过在数据块中对相应数据行加锁来实现的。InnoDB 这种行锁实现特点意味着只有通过索引条件检索数据InnoDB 才使用行级锁当索引失效时InnoDB 将使用表锁
Record Lock(行锁)
单个行记录上的锁。锁定一个记录上的索引而不是记录本身。 如果表没有设置索引InnoDB 会自动在主键上创建隐藏的聚簇索引因此 Record Locks 依然可以使用(锁表了)。
Gap Lock间隙锁
间隙锁锁定一个范围但不包括记录本身。GAP锁的目的是为了防止同一事务的两次当前读出现幻读的情况。
当我们用范围条件而不是相等条件检索数据并请求共享或排他锁时InnoDB会给符合条件的已有数据记录的索引项加锁对于键值在条件范围内但并不存在的记录叫做“间隙GAP)”InnoDB也会对这个“间隙”加锁这种锁机制就是所谓的间隙锁间隙锁会锁定索引之间的间隙但是不包含索引本身。举例来说 假如 user 表中只有 101 条记录 其 userid 的值分别是 1,2,…,100,101 下面的 SQL
Select * from user where userid 100 for update;是一个范围条件的检索InnoDB 不仅会对符合条件的 userid 值为 101 的记录加锁也会对userid 大于 101但是这些记录并不存在的“间隙”加锁防止其它事务在表的末尾增加数据。在执行这个 select 查询语句的时候会将对应的索引访问条目进行上排他锁。
InnoDB 使用间隙锁的目的为了防止幻读以满足串行化隔离级别的要求对于上面的例子要是不使用间隙锁如果其他事务插入了 userid 大于 100 的任何记录那么本事务如果再次执行上述语句就会发生幻读。
使用场景为了让自己查到的数据确保是最新数据并且查到后的数据只允许自己来修改的时候需要用到 for update 子句。
Select * from user where userid 100 lock in share mode ;in share mode 子句的作用就是将查找到的数据加上一个 share 锁这个就是表示其他的事务只能对这些数据进行简单的select 操作并不能够进行 DML 操作允许其他的事务也对该数据上S锁但是不能够对该数据进行修改。
使用场景为了确保自己查到的数据没有 其他的事务正在修改也就是说确保查到的数据是最新的数据并且不允许其他人来修改数据。但是自己也不一定能够修改数据因为有可能其他的事务也对这些数据 使用了 in share mode 的方式上了 S 锁。
for update上的是排他锁X 锁一旦一个事务获取了这个锁其他的事务是没法在这些数据上执行 for update lock in share mode上的是共享锁多个事务可以同时的对相同数据执行 lock in share mode。
Next-Key Lock临键锁
Record Lock Gap Lock锁定一个范围并且锁定记录本身。对于行的查询都是采用该方法MVCC 的方式虽然能解决快照读的不可重复与幻读问题但不能解决当前读的。Next-Key Lock可以解决幻读的问题。
注意当查询的索引含有唯一属性的时候Next-Key Lock 会进行优化将其降级为Record Lock即仅锁住索引本身而不是范围。 由于索引唯一该值只能是一个所以不可能再插入一个同样的数据。
# Transaction-A
mysql set autocommit 0;
mysql update innodb_lock set v1001 where id1;
mysql commit;# Transaction-B
mysql update innodb_lock set v2001 where id2;
Query OK, 1 row affected (0.37 sec)
mysql update innodb_lock set v1002 where id1; //行锁影响
Query OK, 1 row affected (37.51 sec)3页面锁
开销和加锁时间界于表锁和行锁之间会出现死锁锁定粒度界于表锁和行锁之间并发度一般。 链接 https://juejin.cn/post/6844903974282362887 https://zhuanlan.zhihu.com/p/29150809 乐观锁与悲观锁
1乐观锁
总是假设最好的情况每次去拿数据的时候都认为别人不会修改所以不会上锁但是在更新的时候会判断一下在此期间别人有没有去更新这个数据可以使用版本号机制和CAS算法实现。适用于多读
版本号机制提交版本必须大于记录当前版本才能执行更新的乐观锁策略。CAS算法是一种无锁算法基于硬件原语实现能够在不使用锁的情况下实现多线程之间的变量同步。jdk中的java.util.concurrent.atomic包中的原子类就是通过CAS来实现了乐观锁。
2悲观锁
总是假设最坏的情况每次去拿数据的时候都认为别人会修改所以每次在拿数据的时候都会上锁这样别人想拿这个数据就会阻塞直到它拿到锁。适用于多写
共享锁和排他锁(读锁和写锁)
共享锁和排它锁是悲观锁的不同的实现都属于悲观锁的范畴。
1共享锁 又叫读锁 (read lock)是读取操作创建的锁。其他用户可以并发读取数据也可以重复上共享锁但任何事务都不能对数据进行修改获取数据上的排他锁直到所有共享锁已释放。
2排他锁
又称写锁 (write lock) 若事务 1 对数据对象A加上排他锁事务 1 可以读A也可以修改A其他事务不能再对A加任何锁直到事物 1 释放A上的锁。这保证了其他事务在事务1 释放A上的锁之前不能再读取和修改A。排它锁会阻塞所有的排它锁和共享锁。 3意向共享锁读锁 IS
事务想要获取一张表的几行数据的共享锁事务在给一个数据行加共享锁前必须先取得该表的IS锁。
4意向排他锁写锁 IX
事务想要获取一张表中几行数据的排它锁事务在给一个数据行加排他锁前必须先取得该表的IX锁。
意向锁是一种表锁主要用途是为了表达某个事务正在锁定一行或者将要锁定一行数据。比如事务A要对一行记录r上X锁那么InnoDB会先申请表的IX锁再锁定记录r的X锁。在事务A完成之前事务B想要来个全表操作此时直接在表级别的IX就告诉事务B需要等待而不需要在表上判断每一行是否有锁。意向排它锁存在的价值在于节约InnoDB对于锁的定位和处理性能。另外注意了除了全表扫描以外意向锁都不会阻塞。
数据库中有哪些索引类型以及数据结构? 最左匹配原则?
索引
普通索引包含一个表中列的值和值所在行数据的物理地址并且这些值存储在一个数据结构此索引中将无序数据变为有序的查询索引的本质是一种适应特定应用场景的数据结构根据索引查询数据的步骤如下
1将创建了索引的列的内容进行排序 2对排序结果生成倒排表 3在倒排内容上拼上数据地址链 4在查询时先拿到倒排表内容然后取出数据地址链从而取出具体数据
比如以二叉树作为索引的数据库可以用下图示意。需要注意的是索引本身也很大不可能全部存储在内存中一般以索引文件的形式存储在磁盘上。 关于索引的命令
CREATE [UNIQUE] INDEX indexName ON mytable(username(length));
//创建索引,如果是CHARVARCHAR类型length可以小于字段实际长度如果是BLOB和TEXT类型必须指定 length。
ALTER table tableName ADD [UNIQUE] INDEX indexName(columnName)
//修改表结构(添加索引)
DROP INDEX [indexName] ON mytable;
//删除
SHOW INDEX FROM table_name\G
//查看,可以通过添加 \G 来格式化输出信息。
EXPLAIN SELECT * FROM t_mobilesms_11 WHERE userid2222
//查看执行计划包括索引key利用情况作用
使用索引的意义在于通过缩小一张表中需要查询的记录/行的数目来加快搜索的速度。
分类
1从物理存储角度分
聚集索引
正文内容按照一个特定维度排序存储这个特定的维度就是聚集索引。聚集索引的作用对象是一张表数据的物理地址聚集索引使得数据按照物理地址顺序的存储在存储介质中数据的物理地址也是连续的因此聚集索引是查询速度快但插入效率较慢。注意一张表只有一个聚集索引。
聚集索引一般是表中的主键索引如果表中没有显示指定主键则会选择表中的第一个不允许为NULL的唯一索引如果还是没有的话就采用Innodb存储引擎为每行数据内置的6字节ROWID作为聚集索引。聚集索引是索引和数据的集合体不但决定了数据的顺序在叶节点还存储数据行。
非聚集索引
非聚集索引索引项顺序存储但索引项对应的内容却是随机存储的。非聚集索引在物理地址上不相邻更像是一个数据字典索引。通过非聚集索引查找的过程是先找到该索引key对应的聚集索引的key然后再拿聚集索引的key到主键索引树上查找对应的数据这个过程称为回表。所以非聚集索引速度比聚集索引慢但是一个表中非聚集可以建立多个。
聚集索引和非聚集索引的根本区别是表中记录的物理顺序和索引的排列顺序是否一致。
提一个问题聚集索引是否一定比非聚集索引效率高呢比如想查询学分在60-90之间的学生的学分以及姓名在学分上创建聚集索引是否是最优的呢
答否。既然只输出两列我们可以在学分以及学生姓名上创建联合非聚集索引此时的索引就形成了覆盖索引即索引所存储的内容就是最终输出的数据这种索引在比以学分为聚集索引做查询性能更好。
所以建立两列以上的联合索引即可查询复合索引里的列的数据而不需要进行回表二次查询。当sql语句的所求查询字段select列和查询条件字段where子句全都包含在一个索引中 联合索引可以直接使用索引查询而不需要回表查询覆盖索引。
2从逻辑角度分
主键索引值唯一不能为空普通索引唯一索引值唯一可以为空联合索引
主键索引与唯一索引区别
主键列在创建时已经默认为空值 唯一索引了。主键可以被其他表引用为外键而唯一索引不能。一个表最多只能创建一个主键但可以创建多个唯一索引。
普通索引与唯一索引区别
先介绍一个 change buffer的概念每当需要更新一个数据页时如果数据页在内存中就直接更新而如果这个数据页还没有在内存中的话在不影响数据一致性的前提下InnoDB 会将这些更新操作缓存在 change buffer 中这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候将数据页读入内存然后执行 change buffer 中与这个页有关的操作。通过这种方式就能保证这个数据逻辑的正确性。
虽然名字叫作 change buffer实际上它是可以持久化的数据。也就是说change buffer 在内存中有拷贝也会被写入到磁盘上。
将 change buffer 中的操作应用到原数据页得到最新结果的过程称为 merge。除了访问这个数据页会触发 merge 外系统有后台线程会定期 merge。在数据库正常关闭shutdown的过程中也会执行 merge 操作。
显然如果能够将更新操作先记录在 change buffer减少读磁盘语句的执行速度会得到明显的提升。而且数据读入内存是需要占用 buffer pool 的所以这种方式还能够避免占用内存提高内存利用率。change buffer 用的是 buffer pool 里的内存因此不能无限增大。change buffer 的大小可以通过参数 innodb_change_buffer_max_size 来动态设置。这个参数设置为 50 的时候表示 change buffer 的大小最多只能占用 buffer pool 的 50%。
对于唯一索引来说所有的更新操作都要先判断这个操作是否违反唯一性约束。而这必须要将数据页读入内存才能判断。如果都已经读入到内存了那直接更新内存会更快就没必要使用 change buffer 了。
对于普通索引和唯一索引来说查询性能无差别。对于普通索引来说查找到满足条件的第一个记录 (5,张三) 后需要查找下一个记录直到碰到第一个不满足 k5 条件的记录。对于唯一索引来说由于索引定义了唯一性查找到第一个满足条件的记录后就会停止继续检索。从更新过程来说由于change buffer的加成当要更新的目标页不在内存中时普通索引因为减少随机 IO 的访问性能更优。大部分场景普通索引都优于唯一索引在业务代码保证唯一性的前提下但如果业务模式是更新完后马上查询此时普通索引change buffer会起副作用。
补充一下change buffer 的使用场景
对于写多读少的业务来说页面在写完以后马上被访问到的概率比较小此时 change buffer 的使用效果最好。这种业务模型常见的就是账单类、日志类的系统。
因为 merge 的时候是真正进行数据更新的时刻而 change buffer 的主要目的就是将记录的变更动作缓存下来所以在一个数据页做 merge 之前change buffer 记录的变更越多也就是这个页面上要更新的次数越多收益就越大。
多个单列索引和一个联合索引的性能比较
利用索引中的附加列您可以缩小搜索的范围但使用一个具有两列的索引 不同于使用两个单独的索引。复合索引的结构与电话簿类似人名由姓和名构成电话簿首先按姓氏对进行排序然后按名字对有相同姓氏的人进行排序。如果您知道姓电话簿将非常有用如果您知道姓和名电话簿则更为有用但如果您只知道名不姓电话簿将没有用处。
所以说创建复合索引时应该仔细考虑列的顺序。对索引中的所有列执行搜索或仅对前几列执行搜索时复合索引非常有用仅对后面的任意列执行搜索时复合索引则没有用处。 多个单列索引在多条件查询时优化器会选择最优索引策略可能只用一个索引也可能将多个索引全用上 但多个单列索引底层会建立多个B索引树比较占用空间也会浪费一定搜索效率故如果只有多条件联合查询时最好建联合索引
可以简单理解为当创建(a,b,c)联合索引时相当于创建了(a)单列索引(a,b)联合索引以及(a,b,c)联合索引。想要索引生效的话,只能使用 a和a,b和a,b,c三种组合当然a,c组合也可以但实际上只用到了a的索引c并没有用到 链接 https://blog.csdn.net/Abysscarry/article/details/80792876 3从底层实现角度分 - B树索引- Hash索引- Full-Text全文索引- R-Tree索引 链接 https://blog.csdn.net/weixin_41896265/article/details/108427213 https://zhuanlan.zhihu.com/p/27700617 最左匹配前缀原则
即当你创建了一个联合索引该索引的任何最左前缀都可以用于查询。比如当你有一个联合索引 (name,age,sex)该索引的所有前缀为(name)、(name,age)、(name,age,sex)包含这些列的所有查询都会使用该索引进行查询。
以底层B树为例当树节点的数据项是复合的数据结构比如查询条件是(name,age,sex)的时候b树是按照从左到右的顺序来建立搜索树的此时对于每一个数据项包含多个关键字并且对于单一的key后面的key是有序的。
比如当(张三,20,F)这样的数据来检索的时候b树会优先比较name来确定下一步的搜索方向如果name相同再依次比较age和sex最后得到检索的数据但当(20,F)这样的没有name的数据来的时候b树就不知道下一步该查哪个节点因为建立搜索树的时候name就是第一个比较因子必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时b树可以用name来指定搜索方向但下一个字段age的缺失所以只能把名字等于张三的数据都找到然后再匹配性别是F的数据了 这个是非常重要的性质即索引的最左匹配特性。
建议:
1、需要加索引的字段要在where条件中 2、数据量少的字段不需要加索引因为建索引有一定开销如果数据量小则没必要建索引速度反而慢 3、避免在where子句中使用or来连接条件,因为如果俩个字段中有一个没有索引的话,引擎会放弃索引而产生全表扫描。 4、联合索引比对每个列分别建索引更有优势因为索引建立得越多就越占磁盘空间在更新数据的时候速度会更慢。另外建立多列索引时顺序也是需要注意的应该将筛选条件严格的索引放在前面这样筛选力度会更大效率更高。
索引失效的情况
用or连接的其中一个查询字段无索引或者联合索引中使用or 以%开头的like模糊查询 如果列类型是字符串那一定要在条件中将数据使用单引号引用起来否则不会使用索引 优化器评估如果MySQL预计使用全表扫描要比使用索引快则不使用索引 判断索引列是否不等于某个值时。‘!’操作符。
select * from SoftWareDetailInfo where SoftUseLine ! 0对索引列进行运算。这里运算包括-*/等运算。也包括使用函数。
select * from SoftWareDetailInfo where SoftUseLine 0 0select * from SoftWareDetailInfo where count(SoftUseLine) 0索引字段进行判空查询时。也就是对索引字段判断是否为NULL时。语句为is null 或 is not null。
select * from SoftWareDetailInfo where CreateTime is null范围列可以用到索引联合索引必须是最左前缀但是范围列后面的列无法用到索引
hash索引与B树如何选用
hash索引基于哈希表实现只有精确匹配索引所有列的查询才有效。Memory引擎默认使用的是此种索引。
存储引擎对所有的索引列计算出一个哈希码将哈希码存储在索引中同时哈希表中保存每个数据行的指针。
hash索引的特点
hash索引中只有hash值和行数的指针因此无法直接使用索引来避免读取行但是因为这种索引读取快性能影响不明显。hash索引不是按照索引值顺序存储无法使用于排序。不支持部分列匹配查找这里面是使用索引列的全部内容来计算哈希值例如(A,B)两列一起建索引单纯使用A一列那么就无法使用索引B-Tree索引的话因为支持匹配最左前缀所以这种情况适用性偏好。哈希索引只支持等值查询包括、in()、不支持where age 10 这种范围查询。哈希冲突很多的话维护索引操作的代价也很高
B树索引和哈希索引的明显区别是 如果是等值查询那么哈希索引明显有绝对优势因为只需要经过一次算法即可找到相应的键值当然了这个前提是键值都是唯一的。如果键值不是唯一的就需要先找到该键所在位置然后再根据链表往后扫描直到找到相应的数据 从示意图中也能看到如果是范围查询检索这时候哈希索引就毫无用武之地了因为原先是有序的键值经过哈希算法后有可能变成不连续的了就没办法再利用索引完成范围查询检索 同理哈希索引也没办法利用索引完成排序以及like ‘xxx%’ 这样的部分模糊查询这种部分模糊查询其实本质上也是范围查询 哈希索引也不支持多列联合索引的最左匹配规则 B树索引的关键字检索效率稳定不像B树那样波动幅度大在有大量重复键值情况下哈希索引的效率也是极低的因为存在所谓的哈希碰撞问题。
怎么对一条查询语句包括索引调优
在经常需要进行检索的字段上创建索引避免在索引上使用计算使用预编译查询
数据库在接受到sql语句之后需要词法和语义解析优化sql语句制定执行计划。这需要花费一些时间。但是很多情况我们的一条sql语句可能会反复执行或者每次执行的时候只有个别的值不同。如果每次都需要经过上面的词法语义解析、语句优化、制定执行计划等则效率就明显不行了。为了解决上面的问题于是就有了预编译预编译语句就是将这类语句中的值用占位符替代可以视为将sql语句模板化或者说参数化。一次编译、多次运行省去了解析优化等过程。
优点是预编译阶段可以优化 sql 的执行并且可以防止SQL注入问题。
调整Where子句中的位置尽量将多条SQL语句压缩到一句SQL中用where字句替换HAVING字句having一般用在分组后使用表的别名用union all替换union (对两个结果集进行并集操作保留重复行不进行排序)只在必要的情况下才使用事务begin tran —— commit用变长字段varchar/nvarchar 代替 char/nchar
以下对SELECT语句
任何地方都不要使用 select * from t 用具体的字段列表代替“*”不要返回用不到的任何字段应尽量避免在 where 子句中对字段进行 null 值判断否则将导致引擎放弃使用索引而进行全表扫描别用like的前置%别用in
以下非select语句
如果只更改1、2个字段不要Update全部字段否则频繁调用会引起明显的性能消耗同时带来大量日志使用了ROWID高效删除重复记录
DELETE FROM EMP E WHERE E.ROWID (SELECT MIN(X.ROWID) FROM EMP X WHERE X.EMP_NO E.EMP_NO);链接https://blog.csdn.net/u010520146/article/details/81161762 优化数据库的方案
SQL语句优化
1应尽量避免在 where 子句中使用!或操作符否则将引擎放弃使用索引而进行全表扫描。
2应尽量避免在 where 子句中对字段进行 null 值判断否则将导致引擎放弃使用索引而进行全表扫描如
select id from t where num is null可以在num上设置默认值0确保表中num列没有null值然后这样查询
select id from t where num03很多时候用 exists 代替 in 是一个好的选择。
4用Where子句替换HAVING 子句因为HAVING只会在检索出所有记录之后才对结果集进行过滤。
5编程手段防止SQL注入使用JDBC PreparedStatement按位插入或查询正则表达式过滤非法字符串过滤
索引优化
查看索引优化部分。
补充下索引下推的概念了解即可
索引下推index condition pushdown 简称ICP在Mysql5.6的版本上推出用于优化查询。 在不使用ICP的情况下在使用非主键索引进行查询时存储引擎通过索引检索到数据然后返回给MySQL服务器由服务器判断数据是否符合条件 。 在使用ICP的情况下如果存在被索引的列的判断条件时MySQL服务器将这一部分判断条件传递给存储引擎然后由存储引擎通过判断索引是否符合MySQL服务器传递的条件只有当索引符合条件时才会将数据检索出来返回给MySQL服务器 。
索引条件下推优化可以减少存储引擎查询基础表的次数也可以减少MySQL服务器从存储引擎接收数据的次数。
SELECT * from user where name like 陈% and age205.6之前的版本是没有索引下推这个优化的会忽略age这个字段直接通过name进行查询在(name,age)这课树上查找到了两个结果id分别为2,1然后拿着取到的id值一次次的回表查询因此这个过程需要回表两次。 5.6版本添加了索引下推这个优化InnoDB并没有忽略age这个字段而是在索引内部就判断了age是否等于20对于不等于20的记录直接跳过因此在(name,age)这棵索引树中只匹配到了一个记录此时拿着这个id去主键索引树中回表查询全部数据这个过程只需要回表一次。
根据explain解析结果可以看出Extra的值为Using index condition表示已经使用了索引下推。
数据库结构优化
1范式优化 比如消除冗余节省空间…
2反范式优化比如适当加冗余等减少join
3拆分表 分区将数据在物理上分隔开不同分区的数据可以制定保存在处于不同磁盘上的数据文件里。这样当对这个表进行查询时只需要在表分区中进行扫描而不必进行全表扫描明显缩短了查询时间另外处于不同磁盘的分区也将对这个表的数据传输分散在不同的磁盘I/O一个精心设置的分区可以将数据传输对磁盘I/O竞争均匀地分散开。对数据量大的时时表可采取此方法。可按月自动建表分区。
4拆分其实又分垂直拆分和水平拆分
案例 简单购物系统暂设涉及如下表 1.产品表数据量10w稳定 2.订单表数据量200w且有增长趋势 3.用户表 数据量100w且有增长趋势 以mysql为例讲述下水平拆分和垂直拆分mysql能容忍的数量级在百万静态数据可以到千万 垂直拆分 解决问题表与表之间的io竞争 不解决问题单表中数据量增长出现的压力 方案 把产品表和用户表放到一个server上 订单表单独放到一个server上 水平拆分 解决问题单表中数据量增长出现的压力 不解决问题表与表之间的io争夺 方案 用户表通过性别拆分为男用户表和女用户表 订单表通过已完成和完成中拆分为已完成订单和未完成订单 产品表 未完成订单放一个server上 已完成订单表和男用户表放一个server上 女用户表放一个server上。
服务器硬件优化
提升数据库服务器硬件配置提升硬盘配置换SSD固态硬盘或者搭建数据库集群。 链接https://blog.csdn.net/baidu_37107022/article/details/77460464 MySQL主从复制的原理
master主机开启bin-log功能日志文件用于记录数据库的读写增删。需要开启3个线程master IO线程slave开启 IO线程 SQL线程。 基本流程 1Slave上面的IO进程连接上Master并请求从指定日志文件的指定位置之后的日志内容。 2Master接收到来自Slave的IO进程的请求后负责复制的IO进程会根据请求信息读取日志指定位置之后的日志信息返回给Slave的IO进程。返回信息中除了日志所包含的信息之外还包括本次返回的信息所读到的Master端的bin-log文件的名称以及bin-log的位置。 3Slave的IO进程接收到信息后将接收到的日志内容依次添加到Slave端的relay-log文件的最末端并将读取到的Master端的bin-log的文件名和位置记录到master-info文件中以便在下一次读取的时候能够清楚的告诉Master从何处开始读取日志。 4Slave的Sql进程检测到relay-log中新增加了内容后会马上解析relay-log的内容成为在Master端真实执行时候的那些可执行的内容并在自身执行。 Bin Log 日志格式
1STATEMENT 格式语句模式出现在 MySQL 5.1 之前在这种格式下binlog 记录的是执行的 SQL 语句的文本。
优点日志文件通常较小复制效率较高。缺点在某些情况下由于数据库环境的差异如表结构、字符集等在从服务器上重放这些 SQL 语句可能会导致不一致的结果。例如获取当前时间的函数或存储过程等可能会导致数据不一致。
2ROW 格式行模式诞生于 MySQL 5.1在这种格式下binlog 记录的是每一行数据更改的具体内容。
优点能够精确地记录数据的变化避免了 STATEMENT 格式中的环境依赖问题提供了更强的一致性保证。缺点日志文件可能会比 STATEMENT 格式大因为记录了每一行的详细变化。此外ROW 格式的日志在进行大量数据更新时可能会导致更高的 I/O 开销。
在MySQL主从复制架构中读操作可以在所有的服务器上面进行而写操作只能在主服务器上面进行。主从复制架构虽然给读操作提供了扩展可如果写操作也比较多的话多台从服务器还要从主服务器上面同步数据单主模型的复制中主服务器势必会成为性能瓶颈。
主从复制模式
1异步复制MySQL 主从复制中最常见和默认的模式。在异步复制模式中主服务器将数据修改操作记录到二进制日志Binary Log中并将日志传输给从服务器。从服务器接收到二进制日志后会异步地应用这些日志进行数据复制。
优点它的优点是及时响应给使用者主服务器不会受到从服务器的影响而等待确认可以提高主服务器的性能。缺点由于是异步复制可能存在数据传输的延迟且从服务器上的复制过程是不可靠的。如果主服务器故障尚未应用到从服务器的数据可能会丢失。
2半同步复制在半同步复制模式中主服务器将数据修改操作记录到二进制日志并等待至少一个从服务器确认已接收到并应用了这些日志后才继续执行后续操作即在主库commit之前需要先将binlog同步到从库主库可以设置同步binlog的过期时间在binlog复制到从库之后从库后续会自行重放中继日志。
优点可以提供更高的数据一致性和可靠性确保至少一个从服务器与主服务器保持同步。如果主服务器故障已经确认接收并应用到从服务器的数据不会丢失。缺点由于半同步复制需要等待从服务器的确认因此相对于异步复制会增加一定的延迟可能会影响主服务器的性能。 链接https://www.cnblogs.com/vipstone/p/17934625.html MySQL如何保证事务的特性
redo log重做日志用来保证事务的持久性undo log回滚日志保证事务的原子性undo logredo log保证事务的一致性锁共享、排他用来保证事务的隔离性ACID其他的三个属性都是为了保证一致性而存在。
redo log
InnoDB通过Force Log at Commit机制来实现持久性当commit时必须先将事务的所有日志写到重做日志文件进行持久化待commit操作完成才算完成。为了确保每次日志都写入重做日志文件在每次将日志缓冲写入重做日志文件后InnoDB存储引擎默认都需要调用一次fsync刷入磁盘操作。
undo log
为了满足事务的原子性在操作任何数据之前首先将数据备份到一个地方这个存储数据备份的地方称为Undo Log然后进行数据的修改。如果出现了错误或者用户执行了 ROLLBACK语句系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态。前面我们学过undo log实现多版本并发控制MVCC来辅助保证事务的隔离性。
所以如果整个事务执行的过程系统崩溃或者断电了在系统重启的时候恢复机制会将redo log中已提交的事务重做保证事务的持久性而undo log中未提交的事务进行回滚保证事务的原子性。
总结
回滚日志不同于重做日志它是逻辑日志对数据库的修改都逻辑的取消了。当事务回滚时它实际上做的是与先前相反的工作。对于每个INSERTInnoDB存储引擎都会完成一个DELETE对于每个UPDATEInnoDB存储引擎都会执行一个相反的UPDATE。
另一个注意点是当记录的是对 delete和 update操作产生的 undo logupdate undo log时该undo log可能需要提供MVCC机制因此不能在事务提交时就进行删除。提交时放入undo log链表等待 purge线程进行最后的删除。当记录的是对 insert操作产生的 undo log时可以提交后删除。 链接https://juejin.cn/post/6844903959677632519 MySQL和Redis的区别各自使用的场景以及原因
类型上
从类型上来说mysql是关系型数据库redis是缓存数据库
作用上
mysql用于持久化的存储数据到硬盘功能强大但是速度较慢redis用于存储使用较为频繁的数据到缓存中读取速度快
需求上
mysql和redis因为需求的不同一般都是配合使用。
存放位置
MySQL数据放在磁盘Redis数据放在内存
5适合存放数据类型
Redis适合放一些频繁使用比较热的数据因为是放在内存中读写速度都非常快一般会应用在下面一些场景排行榜sorted set、计数器、消息队列推送、好友关注、粉丝.
关于redis事务的补充
一次性一次执行多个redis操作命令相当于打包的批量执行脚本顺序性顺序执行排他性在事务执行过程其他客户端提交的命令请求不会插入到事务执行命令序列中。
但是redis事务不具备原子性即收到 EXEC 命令后进入事务执行事务中任意命令执行失败其余的命令依然被执行并且无隔离级别。
redis的五大数据结构
Redis有5个基本数据结构string动态字符数组、list双向链表、hash、set和zset。
简单介绍下常用命令。
1数据库操作
SELECT 3 #选择第几个redis数据库
DBSIZE #查看数据库容量
keys * #查看所有redis键名
flushdb #清空当前数据库
flushall #清空所有数据库
EXISTS keyname #判断是否存在
EXPIRE keyname 10 #设置十秒后过期
type keyname
expire ireader 60 # 1表示设置成功0表示变量ireader不存在
ttl ireader # 还有50秒的寿命返回-2表示变量不存在-1表示没有设置过期时间
del ireader # 删除成功返回12redis事务
multi
set a aaa、
exec //取消DISCARD WATCH可监控key值若改变则取消执行EXEC3string必知命令
set money 100
get money
setrange ireader 28 wooxian #指定位置替换
getrange ireader 28 34
strlen ireader
append ireader .hao
incrby money 20
decrby money 20
incr money
decr money
setex keyname 30 hello techguide #带过期时间设置
setnx keyname “hello techguide” #set if not exist
set lock 4854857 ex 3000 nx #redis分布式锁使用的加锁语句
mset k1 k2 k3 v1 v2 v3
mget k1 k2 k3
getset k1 v44List必知命令
lpush list one
rpush list two
lpop list
rpop list
lrange list 0 -1 #取出全部内容
lindex list -1 #下标取值
lrem list 1 one #移除一个one
ltrim list 1 3 #截取并保留的范围
linsert list before one three #one前插入three
llen list5hash
原理
在实现结构上hash使用二维结构第一维是数组第二维是链表hash的内容key和value存放在链表中数组里存放的是链表的头指针。通过key查找元素时先计算key的hashcode然后用hashcode对数组的长度进行取模定位到链表的表头再对链表进行遍历获取到相应的value值。
扩容
当hash内部的元素比较拥挤时(hash碰撞比较频繁)就需要进行扩容。扩容需要申请新的两倍大小的数组然后将所有的键值对重新分配到新的数组下标对应的链表中(rehash)。如果hash结构很大比如有上百万个键值对那么一次完整rehash的过程就会耗时很长。这对于单线程的Redis里来说有点压力山大。所以Redis采用了渐进式rehash的方案。它会同时保留两个新旧hash结构在后续的定时任务以及hash结构的读写指令中将旧结构的元素逐渐迁移到新的结构中。这样就可以避免因扩容导致的线程卡顿现象。
缩容
Redis的hash结构不但有扩容还有缩容从这一点出发它要比Java的HashMap要厉害一些Java的HashMap只有扩容。缩容的原理和扩容是一致的只不过新的数组大小要比旧数组小一倍。
hset ireader go fast
hmset ireader java fast python slow #一次设置多个键值对
hget ireader go
hmget ireader go python
hgetall ireader
hkeys ireader
hvals ireader
hdel ireader java #可删除多个
hexists ireader go 6set和sorted set
HashSet的内部实现使用的是HashMap只不过所有的value都指向同一个对象。Redis的set结构也是一样它的内部也使用hash结构所有的value都指向同一个内部值。
sorted set(zset)底层实现hash和跳跃列表。其中每一个元素value有一个权重score内部的元素会按照权重score进行排序可以得到每个元素的名次还可以通过score的范围来获取元素的列表。
sadd set 666
smembers set #全部元素
sismember set 666 #判断是否有
scard set #元素个数
srem set 666
spop set #随机弹出zadd key score member
zadd ireader 4.0 python #通过zadd指令可以增加一到多个value/score对score放在前面
zcard ireader
zrem ireader go pytho #可删除多个zscore ireader python
5zrangebyscore ireader 0 5
1) go
2) java
3) pythonzrangebyscore ireader -inf inf withscores
1) go
2) 1
3) java
4) 4
5) python
6) 5有序集合对象的编码可以是ziplist或者skiplist。同时满足以下条件时使用ziplist编码
元素数量小于128个所有member的长度都小于64字节
ziplist编码的有序集合使用紧挨在一起的压缩列表节点来保存第一个节点保存member第二个保存score。ziplist内的集合元素按score从小到大排序score较小的排在表头位置。
skiplist编码的有序集合底层是一个命名为zset的结构体而一个zset结构同时包含一个字典和一个跳表。跳表按score从小到大保存所有集合元素。而字典则保存着从member到score的映射这样就可以用O(1)的复杂度来查找member对应的score值。虽然同时使用两种结构但它们会通过指针来共享相同元素的member和score因此不会浪费额外的内存。
跳表是可以实现二分查找的有序链表。每个元素插入时随机生成它的level并且每往上一层概率减半最底层是一条包含所有元素的单向链表如果一个元素出现在level(x)那么它肯定出现在x以下的level中每个索引节点包含两个指针一个向下一个向右。
跳表查询、插入、删除的时间复杂度为O(log n)与平衡二叉树接近。 链接 https://juejin.cn/post/6844903644798664712 https://www.jianshu.com/p/9d8296562806 redis为什么快
多路复用io阻塞机制
I/O多路复用
Redis 是跑在单线程中的所有的操作都是按照顺序线性执行的但是由于读写操作等待用户输入或输出都是阻塞的所以 I/O 操作在一般情况下往往不能直接返回这会导致某一文件的 I/O 阻塞导致整个进程无法对其它客户提供服务而 I/O 多路复用就是为了解决这个问题而出现的。
Reactor模型事件驱动事件处理器。以select为例select的调用会阻塞到有文件描述符可以进行IO操作或被信号打断或者超时才会返回。
select将监听的文件描述符分为三组每一组监听不同的需要进行的IO操作。readfds是需要进行读操作的文件描述符writefds是需要进行写操作的文件描述符exceptfds是需要进行异常事件处理的文件描述符。这三个参数可以用NULL来表示对应的事件不需要监听。
当select返回时每组文件描述符会被select过滤只留下可以进行对应IO操作的文件描述符。
总结以上就是操作系统为你提供了一个功能当你的某个socket可读或者可写的时候它可以给你一个通知。这样当配合非阻塞的socket使用时只有当系统通知我哪个描述符可读了我才去执行read操作可以保证每次read都能读到有效数据而不做纯返回-1和EAGAIN的无用功。写操作类似。操作系统的这个功能通过select/poll/epoll/kqueue之类的系统调用函数来使用这些函数都可以同时监视多个描述符的读写就绪状况这样多个描述符的I/O操作都能在一个线程内并发交替地顺序完成这就叫I/O多路复用这里的“复用”指的是复用同一个线程。 纯内存操作 单线程
单线程容易实现而且CPU不会成为瓶颈那就顺理成章地采用单线程的方案避免了CPU不必要的上下文切换和竞争锁的消耗。
简单高效的数据结构 链接 https://juejin.cn/post/6978280894704386079 https://xie.infoq.cn/article/b3816e9fe3ac77684b4f29348 https://draveness.me/redis-io-multiplexing/ Redis雪崩、击穿、穿透 介绍下布隆过滤器
布隆过滤器由「初始值都为 0 的位图数组」和「 N 个哈希函数」两部分组成。当我们在写入数据库数据时在布隆过滤器里做个标记这样下次查询数据是否在数据库时只需要查询布隆过滤器如果查询到数据没有被标记说明不在数据库中。 当应用要查询数据 x 是否数据库时通过布隆过滤器只要查到位图数组的第 1、4、6 位置的值是否全为 1只要有一个为 0就认为数据 x 不在数据库中。
由于哈希冲突x和y可能有相同的位置所以布隆过滤器有可能误判即查询布隆过滤器说数据存在并不一定证明数据库中存在这个数据但是查询到数据不存在数据库中一定就不存在这个数据但后者这个特性就足以解决缓存穿透的问题了。 链接https://xiaolincoding.com/redis/cluster/cache_problem.html Redis缓存淘汰策略
当内存空间不够时redis会根据设置的淘汰策略将一部分数据删除淘汰策略大致可以作用于全范围数据或者仅作用于设置了过期时间的数据。
淘汰策略描述volatile-ttl表示在设置可过期时间的键值对中根据过期时间的先后进行淘汰数据越早被过期的数据越先被淘汰。volatile-random在设置了过期时间的键值对中随机淘汰数据。volatile-lru会根据lru算法进行数据的淘汰volatile-lfu会根据lfu算法进行数据的淘汰allkeys-random在全部的键值对数据中进行数据的随机淘汰。allkeys-lru在全部的键值对数据中根据lru算法进行数据的淘汰。allkeys-lfu在全部的键值对数据中根据lfu算法进行数据的淘汰。
redis在实际删除失效主键时又有两种方式
消极方法passive way
在主键被访问时如果发现它已经失效那么就删除它。redis在实现GET、MGET、HGET、LRANGE等所有涉及到读取数据的命令时都会调用 expireIfNeeded它存在的意义就是在读取数据之前先检查一下它有没有失效如果失效了就删除它。
积极方法active way
周期性地探测发现失效就删除。消极方法的缺点是如果key 迟迟不被访问就会占用很多内存空间所以才有积极方式。
主动删除
当内存超过maxmemory限定时触发主动清理策略该策略由启动参数的配置决定
Redis持久化策略
RDB 持久化
RDB 持久化也称作快照持久化是指将内存中的数据生成快照保存到磁盘里面保存的文件后缀是 .rdb。rdb 文件是一个经过压缩的二进制文件当 Redis 重新启动时可以读取 rdb 快照文件恢复数据。
RDB 文件是一个单文件的全量数据很适合数据的容灾备份与恢复通过 RDB 文件恢复数据库耗时较短通常 1G 的快照文件载入内存只需 20s 左右。
优点
是一个压缩过的非常紧凑的文件保存着某个时间点的数据集适合做数据的备份、灾难恢复可以最大化 Redis 的性能在保存 RDB 文件服务器进程只需 fork 一个子进程来完成 RDB 文件的创建父进程不需要做 IO 操作与 AOF 持久化方式相比恢复大数据集的时候会更快
缺点
RDB 的数据安全性是不如 AOF 的保存整个数据集是个重量级的过程根据配置可能要几分钟才进行一次持久化如果服务器宕机那么就可能丢失几分钟的数据Redis 数据集较大时fork 的子进程要完成快照会比较耗费 CPU 和时间
AOF 持久化
AOF 会把 Redis 服务器每次执行的写命令记录到一个日志文件中当服务器重启时再次执行 AOF 文件中的命令来恢复数据。
默认情况下 AOF 功能是关闭的Redis 只会通过 RDB 完成数据持久化的。开启 AOF 功能需要 redis.conf 文件中将 appendonly 配置项修改为 yes
AOF 文件的写入流程可以分为以下 3 个步骤
1命令追加append将 Redis 执行的写命令追加到 AOF 的缓冲区 aof_buf 2文件写入write和文件同步fsyncAOF 根据对应的策略将 aof_buf 的数据同步到硬盘 3文件重写rewrite定期对 AOF 进行重写从而实现对写命令的压缩。 手动调用重写日志bgrewriteaof 。
优点
数据更完整安全性更高秒级数据丢失取决于 fsync 策略如果是 everysec最多丢失 1 秒的数据AOF 文件是一个只进行追加的命令文件且写入操作是以 Redis 协议的格式保存的内容是可读的适合误删紧急恢复
缺点
对于相同的数据集AOF 文件的体积要远远大于 RDB 文件数据恢复也会比较慢根据所使用的 fsync 策略AOF 的速度可能会慢于 RDB。不过在一般情况下 每秒 fsync 的性能依然非常高
mongo原理
MongoDB是基于分布式文件存储的数据库由C语言编写。MongodDB是一个介于关系数据库与非关系数据库之间的产品是非关系型数据库中功能最丰富最像关系数据库。 面向集合(Collection)和文档(document)的存储以JSON格式的文档保存数据。 高性能支持Document中嵌入Document减少了数据库系统上的I/O操作以及具有完整的索引支持支持快速查询 高效的传统存储方式支持二进制数据及大型对象 高可用性数据复制集MongoDB 数据库支持服务器之间的数据复制来提供自动故障转移automatic failover 高可扩展性分片(sharding)将数据分布在多个数据中心,MongoDB支持基于分片键创建数据区域. 丰富的查询功能, 聚合管道(Aggregation Pipeline)、全文搜索(Text Search)以及地理空间查询(Geospatial Queries) 支持多个存储引擎WiredTiger存储引擎B tree、In-Memory存储引擎 链接 https://javaguide.cn/database/mongodb/mongodb-questions-01.html 底层是b树https://zhuanlan.zhihu.com/p/519658576 牛客收藏夹https://www.nowcoder.com/users/565006049/collects