Java面试题集锦------数据库

 

https://blog.csdn.net/qq_22222499/article/details/79060495

事务四大特性(ACID)

原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)
事务前后数据的完整性必须保持一致。
隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

事务隔离级别(5种)

(1)脏读:事务A读取了事务B更新的数据,然后B回滚操作或者未提交,那么A读取到的数据是脏数据

(2)不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时结果不一致。

(3)幻读:幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。。

例如:事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作 这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。 而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有跟没有修改一样,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。当在事务处理过程中执行两个相同的查询,并且第二个查询返回的行集合与第一个查询不同时

小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改(行),幻读侧重于新增或删除(多行集合)。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。

解决读问题:设置事务隔离级别(5种)
DEFAULT这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别;
未提交读(read uncommited):脏读,不可重复读,幻读都有可能发生
已提交读(read commited):避免脏读。但是不可重复读和幻读都有可能发生;
可重复读(repeatable read):避免脏读和不可重复读,但是幻读有可能发生;
串行化的(serializable):避免以上所有读问题。

事务的隔离级别要得到底层数据库引擎的支持, 而不是应用程序或者框架的支持.

MySQL默认:可重复读
Oracle默认:已提交读

MySQL 支持 4 中事务隔离级别.
Oracle 支持的 2 种事务隔离级别:READ_COMMITED , SERIALIZABLE
补充

SQL规范所规定的标准,不同的数据库具体的实现可能会有些差异
MySQL中默认事务隔离级别是“可重复读”时并不会锁住读取到的行
事务隔离级别:未提交读时,写数据只会锁住相应的行。
事务隔离级别为:可重复读时,写数据会锁住整张表。
事务隔离级别为:串行化时,读写数据都会锁住整张表。

事务传播行为

Spring定义了七种传播行为:

这里写图片描述

数据库索引

    •索引概念:

        数据列的值进行结构化排序的一个东西,SQL的主流索引结构有B+树以及Hash结构。

        聚集索引(主键索引,只有一个):索引的逻辑顺序与数据行的物理存储顺序相同。

        非聚集索引(可以有多个):索引的逻辑顺序与 数据行的物理存储顺序不同。

聚集索引和非聚集索引的根本区别是表记录的排列顺序和与索引的排列顺序是否一致

    •常见索引:

       •1、普通索引:不附加任何限制条件,其值是否唯一和非空由字段本身的完整性约束条件决定            (INDEX)

       •2、唯一索引:索引列的值必须唯一,允许有空值(UNIQUE)

       •3、主键索引:一种特殊的唯一索引,一个表只能一个主键,不允许有空值。一般是在建表的时候同时创建主键索引                    (PRIMARY KEY )

       •4、组合索引:多个字段上创建的索引,前提第一个索引被使用才能有效(INDEX ),遵循”最左前缀“原则

       •5、全文索引:建立在char、varchar,text类型上的索引,MyIsAM支持(FULLTEXT )

•注意事项:

•1、索引应该建立在经常select的字段上,过多索引占磁盘,经常insert、update和delete的

          不适合,因为需要维护索引文件,效率低

•2、索引不应包含含有NULL值的列,最好不要让字段默认为null

               1️⃣复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。

               2️⃣MySQL难以优化引用了可空列的查询,它会使索引、索引统计和值更加复杂。

                  可空列需要更多的储存空间,还需要在MySQL内部进行特殊处理

  • MySQL只对一下操作符才使用索引:<,<=,=,>,>=,between,in 以及某些时候的like(不以通配符%或_开头的情形)。

•3、索引会无效的情况:

(1)、like模糊查询不以字母开头的,如like‘%han%’

(2)、在列上进行运算,如YEAR(column_name)<2018;

(3)、使用NOT IN 、<>、!=操作

(4)、字符串与数字比较不使用索引; CREATE TABLE `a` (`a` char(10)); 

                EXPLAIN SELECT * FROM `a` WHERE `a`="1" -- 走索引

                EXPLAIN SELECT * FROM `a` WHERE `a`=1 -- 不走索引

(5)、join操作中主键和外健数据类型不同

(6)、mysql估计使用全表扫描要比使用索引快的情况

MySQL B+Tree索引和Hash索引的区别

Hash索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位;
B+树索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问;
那为什么大家不都用Hash索引而还要使用B+树索引呢?

 Hash索引

(1)Hash索引仅仅能满足"=","IN"和"<=>"查询,不能使用范围查询,因为经过相应的Hash算法处理之后的Hash值的大小关系,并不能保证和Hash运算前完全一样;

(2)Hash索引无法被用来避免数据的排序操作,因为Hash值的大小关系并不一定和Hash运算前的键值完全一样;

(3)Hash索引不能利用部分索引键查询,对于组合索引,Hash索引在计算Hash值的时候是组合索引键合并后再一起计算Hash值,而不是单独计算Hash值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash索引也无法被利用;

(4)Hash索引在任何时候都不能避免表扫描,由于不同索引键存在相同Hash值,所以即使取满足某个Hash键值的数据的记录条数,也无法从Hash索引中直接完成查询,还是要回表查询数据;

(5)Hash索引遇到大量Hash值相等的情况后的性能并不一定就会比B+树索引高。

 B+Tree索引

MySQL中,只有HEAP/MEMORY引擎才显示支持Hash索引。

常用的InnoDB引擎中默认使用的是B+树索引,它会实时监控表上索引的使用情况,如果认为建立哈希索引可以提高查询效率,则自动在内存中的“自适应哈希索引缓冲区”建立哈希索引(在InnoDB中默认开启自适应哈希索引),通过观察搜索模式,MySQL会利用index key的前缀建立哈希索引,如果一个表几乎大部分都在缓冲池中,那么建立一个哈希索引能够加快等值查询。

 B+树索引和哈希索引的明显区别是:

如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;

如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;

同理,哈希索引没办法利用索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);

哈希索引也不支持多列联合索引的最左匹配规则;

B+树索引的关键字检索效率比较平均,不像B树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题。

在大多数场景下,都会有范围查询、排序、分组等查询特征,用B+树索引就可以了。

数据库优化:

     (1)将相对固定的连接信息写入配置文件中,根据测试和生产等环境的不同,准备不同的参数配置文件;

     (2)用preparedstatement以批处理的方式操作数据库--addBatch(),一般每批500-1000条语句;通过preparedstatement还可以防止sql注入;

     (3)使用连接池c3p0;

     (4)建立索引---聚集&非聚集,B-Tree索引,位图索引,全文索引;

     (5)sql优化;

     (6)合理建表,分库分表;

     (7)数据库引擎的选择(针对MySQL):

         InnoDB:

             1、ACID事务的支持,实现了四种隔离级别,提供行级锁和外键约束,但是行锁只是在WHERE的主键是有效的,非主键的WHERE都会锁全表的。

              2、不保存表的行数,需要扫描一遍整个表来计算有多少行

              3、不支持FULLTEXT类型的索引

              4、从MySQL5.5.5以后,InnoDB是默认引擎

                应用场景:在应用中执行大量insert和update操作

         MyISAM:

            1、不支持事务、行级锁和外键 ,insert和update会锁全表,效率较低 

             2、存储了表的行数,计数很快。

             3、拥有全文索引的功能,这可以极大地优化LIKE查询的效率

             4、索引和数据是分开的,而且其索引是压缩的,可以更好地利用内存。

               应用场景:大量select操作 

sql表和语句优化:

一、语句相关优化:

1.limit 分布优化,先利用ID定位,再分页
2.or条件优化,多个or条件可以用union all对结果进行合并(union all结果可能重复)
3.不必要的排序
4.where代替having,having 检索完所有记录,才进行过滤
5.对多个字段进行等值查询时,联合索引

6、in和 exist区别和应用场景

   select * from A where id in(select A_id from B);

   explain select * from A where exists(select B.A_id  from B  where B.A_id= A.id);

  注: in先执行内表后执行外表,外表有索引,适合内表小,外表大的。 exist相反

            而且在使用not in的时候会使得索引失效,而not exist不会

7、能用between就不要用in:

        如果是连续数值,between在找到第一个匹配值后,则直接从该地址往后搜索,直到最后一个元素为止。

        这样就不会对后续值进行索引扫描,因此速度快了。 对于in操作可能会造成全索引进行扫描。

8、使用连接查询代替子查询

      子查询笛卡尔积重新组合后的过滤性能消耗高,使用连接查询代替

9、分组查询可以禁止排序:

      mysql在使用group by的时候字段默认会对分组字段重新排序,可以加order by null 禁止排序

10、可以考虑使用索引的主要有两种类型的列:在where子句中出现的列,在join子句中出现的列。
考虑列中值的分布,索引的列的基数越大,索引的效果越好。
使用短索引,如果对字符串列进行索引,应该指定一个前缀长度,可节省大量索引空间,提升查询速度。
11、利用左匹配原则。其是针对索引的
举例来说:两个字段(name,age)建立联合索引,如果where age=12这样的话,是没有利用到索引的,
这里我们可以简单的理解为先是对name字段的值排序,然后对age的数据排序,如果直接查age的话,这时就没有利用到索引了,
查询条件where name=‘xxx’ and age=xx 这时的话,就利用到索引了,再来思考下where age=xx and name=’xxx‘ 这个sql会利用索引吗,按照正常的原则来讲是不会利用到的,但是优化器会进行优化,把位置交换下。这个sql也能利用到索引了

12、不要过度建索引,只保持所需的索引。每个额外的索引都要占用额外的磁盘空间,并降低写操作的性能。
在修改表的内容时,索引必须进行更新,有时可能需要重构,因此,索引越多,所花的时间越长。

二、表相关优化:

1、保证update条件建立值唯一的索引

         update时如果where条件字段没有建立唯一索引,mysql会锁住整张表,其他线程会等待,所

         以我们在update时需要给where条件建立索引,提高并发能力。

         Lock in share mode(共享锁), for update(排它锁)

2、尽量使用数字型字段.

       字符型会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接会逐个比较字符串

       中每一个字符,而对于数字型而言只需要比较一次就够了。

3、固定长度的表会更快 

      固定的长度是很容易计算下一个数据的偏移量,读取快,如果字段不是定长的,那么,每一次要找下一条

      的话,需先找到主键 ,固定长度的表也更容易被缓存和重建 ,不过定长的会浪费空间。

4、ENUM代替VARCHAR
      ENUM类型实际保存的是TINYINT,如果字段的取值是有限而且固定的,可以使用ENUM提高性能

乐观锁悲观锁,select 时怎么加排它锁

  悲观锁(Pessimistic Lock)

悲观锁的特点是先获取锁,再进行业务操作,即“悲观”的认为获取锁是非常有可能失败的,因此要先确保获取锁成功再进行业务操作。通常所说的“一锁二查三更新”即指的是使用悲观锁。通常来讲在数据库上的悲观锁需要数据库本身提供支持,即通过常用的select … for update操作来实现悲观锁。当数据库执行select for update时会获取被select中的数据行的行锁,因此其他并发执行的select for update如果试图选中同一行则会发生排斥(需要等待行锁被释放),因此达到锁的效果。select for update获取的行锁会在当前事务结束时自动释放,因此必须在事务中使用。

这里需要注意的一点是不同的数据库对select for update的实现和支持都是有所区别的,例如oracle支持select for update no wait,表示如果拿不到锁立刻报错,而不是等待,MySQL就没有no wait这个选项。另外MySQL还有个问题是select for update语句执行中所有扫描过的行都会被锁上,这一点很容易造成问题。因此如果在MySQL中用悲观锁务必要确定走了索引,而不是全表扫描。

  乐观锁(Optimistic Lock)

乐观锁,也叫乐观并发控制,它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,那么当前正在提交的事务会进行回滚。

乐观锁的特点先进行业务操作,不到万不得已不去拿锁。即“乐观”的认为拿锁多半是会成功的,因此在进行完业务操作需要实际更新数据的最后一步再去拿一下锁就好。

乐观锁在数据库上的实现完全是逻辑的,不需要数据库提供特殊的支持。一般的做法是在需要锁的数据上增加一个版本号,或者时间戳,然后按照如下方式实现:

乐观锁(给表加一个版本号字段) 这个并不是乐观锁的定义,给表加版本号,是数据库实现乐观锁的一种方式。

(1) SELECT data AS old_data, version AS old_version FROM …;

(2)根据获取的数据进行业务操作,得到new_data和new_version

(3)UPDATE SET data = new_data, version = new_version WHERE version = old_version

if (updated row > 0) {

    // 乐观锁获取成功,操作完成

} else {

    // 乐观锁获取失败,回滚并重试

}

乐观锁在不发生取锁失败的情况下开销比悲观锁小,但是一旦发生失败回滚开销则比较大,因此适合用在取锁失败概率比较小的场景,可以提升系统并发性能

乐观锁还适用于一些比较特殊的场景,例如在业务操作过程中无法和数据库保持连接等悲观锁无法适用的地方。

  总结

悲观锁和乐观锁是数据库用来保证数据并发安全防止更新丢失的两种方法,例子在select ... for update前加个事务就可以防止更新丢失。悲观锁和乐观锁大部分场景下差异不大,一些独特场景下有一些差别,一般我们可以从如下几个方面来判断。

响应速度:如果需要非常高的响应速度,建议采用乐观锁方案,成功就执行,不成功就失败,不需要等待其他并发去释放锁。
冲突频率:如果冲突频率非常高,建议采用悲观锁,保证成功率,如果冲突频率大,乐观锁会需要多次重试才能成功,代价比较大。
重试代价:如果重试代价大,建议采用悲观锁。

性能调优-执行计划explain

Explain 执行计划包含字段信息如下:分别是 idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra 12个字段。

MySQL.5.7版本以前想要显示filtered需要使用explain extended命令。MySQL.5.7后,默认explain直接显示partitionsfiltered的信息。

mysql> explain select * from (select * from ( select * from user where id=2602) a) b;

 

有两个参数很重要“Type”和“Extra”

1、“Type”:它描述了找到所需数据使用的扫描方式

最为常见的扫描方式有,扫描方式由快到慢:

 1、system:系统表,少量数据,往往不需要进行磁盘IO;
 2、const:常量连接;
 3、eq_ref:主键索引(primary key)或者非空唯一索引(unique not null)等值扫描;
 4、ref:非主键非唯一索引等值扫描;
 5、range:范围扫描,查询性能一般要保证这个级别;
 6、index:索引树扫描;
 7、ALL:全表扫描(full table scan);

1.1 system

当表仅有一行记录时(系统表),数据量很少,往往不需要进行磁盘IO,速度非常快

1.2 const
const扫描的条件为:
(1)命中主键(primary key)或者唯一(unique)索引;
(2)被连接的部分是一个常量(const)值;

1.3 eq_ref
eq_ref扫描的条件为:对于前表的每一行(row),后表只有一行被扫描。

1.4 ref
对于前表的每一行(row),后表可能有会找到多个符合条件的行。

1.5 range
range类型,它是索引上的范围查询,它会在索引上扫码特定范围内的值。在where语句中使用 bettween...and<><=in 等条件查询 type 都是 range。只有对设置了索引的字段,做范围检索 type 才是 range

1.6 index

Index 与ALL 其实都是读全表,区别在于index是遍历索引树读取,而ALL是从硬盘中读取。

1.7 ALL

将遍历全表以找到匹配的行,性能最差。

1.8总结
system最快:不进行磁盘IO
const:PK或者unique上的等值查询
eq_ref:PK或者unique上的join查询,等值匹配,对于前表的每一行(row),后表只有一行命中
ref:非唯一索引,等值匹配,可能有多行命中
range:索引上的范围扫描,例如:between/in/>
index:索引上的全集扫描
ALL最慢:全表扫描(full table scan)

2、“Extra”:值有NULL、Using index、Using where、Using index condition、Using filesort、Using temporary

2.1 Using where
explain select * from account_user_base where id > 4;
Extra为Using where说明,SQL使用了where条件过滤数据。

2.2 Using index
explain select id from account_user_base;
Extra为Using index说明,SQL所需要返回的所有列数据均在一棵索引树上,而无需访问实际的行记录。

2.3 Using index condition
explain select * from account_user_security t1, account_user_base t2 where t1.user_id = t2.id;
Extra为Using index condition说明,确实命中了索引,但不是所有的列数据都在索引树上,还需要访问实际的行记录。

2.4 Using filesort
explain select id from account_user_base order by nick_name;
Extra为Using filesort说明,得到所需结果集,需要对所有记录进行文件排序。
典型的,在一个没有建立索引的列上进行了order by,就会触发filesort,常见的优化方案是,在order by的列上添加索引,避免每次查询都全量排序。

2.5 Using temporary
explain select nick_name, COUNT(*) from account_user_base GROUP BY nick_name order by nick_name;
Extra为Using temporary说明,需要建立临时表(temporary table)来暂存中间结果。一般在排序或者分组查询时用到。
这类SQL语句性能较低,往往也需要进行优化。典型的,group by和order by同时存在,且作用于不同的字段时,就会建立临时表,以便计算出最终的结果集。

3、其他字段

id:id相同时,执行顺序由上至下;如果包含子查询,id的序号会递增,id值越大越先被执行;

select_type:查询类型,取决于sql的查询结构,比如是否包含子查询,或者关联查询等

SIMPLE; PRIMARY; UNION; DEPENDENT UNION; UNION RESULT;SUBQUERY;DEPENDENT SUBQUERY;DERIVED; UNCACHEABLE SUBQUERY 包含这些枚举

partitions:查询时匹配到的分区信息,对于非分区表值为NULL,当查询的是分区表时,partitions显示分区表命中的分区情况。

table:这个很明显,这条数据来自哪张表,但是有时候不是一张真实表名,derived这个可以理解为临时表,为什么会有临时表,待会儿说。

possible_keys表示可能用到索引,但这个索引并不定一会是最终查询数据时所被用到的索引如果这个字段值是空的,那就得检查where句子是否有可用的

key 表示实际用到的索引

key_len 表示索引的字节长度,并非实际使用长度。以前我的理解是数据量其实是错误的,这个值也是引擎计算的,该值越小性能越好。key_len只计算where条件中用到的索引长度。

ref 表示索引的那一列被使用了,换句话说index_A 然后又用A 作为条件检索了数据。表示哪些列或常量被用于查找索引列上的值

rows: 需要扫描的行数,很直观的显示 SQL 性能的好坏,一般情况下 rows 值越小越好。

Filtered:表示返回结果的行数占需读取行数的百分比,Filter列的值越大越好

关系型数据库和非关系型数据库

关系型数据库:MySQL、Oracle、SQL Server、SQLite、MariaDB、PostgreSQL

非关系型数据库:Redis、MongoDB、HBase、Noe4j、Cassandra、CouchDB

关系型数据库

优点

1、容易理解:二维表结构是非常贴近逻辑世界一个概念,关系模型相对网状、层次等其他模型来说更容易理解;

2、使用方便:通用的SQL语言使得操作关系型数据库非常方便;
3、易于维护:都是使用表结构,格式一致,丰富的完整性(实体完整性、参照完整性和用户定义的完整性)大大减低了数据冗余和数据不一致的概率;
4、支持SQL,可用于复杂的查询。
5. 支持事务

缺点
1、读写性能比较差,尤其是海量数据的高效率读写;;
2、固定的表结构,灵活度稍欠;
3、不支持高并发读写需求和海量数据的高效率读写;
非关系型数据库

1、使用键值对存储数据;
2、分布式;
优点
1. 无需经过sql层的解析,读写性能很高。nosql可以使用硬盘或者随机存储器作为载体
2. 基于键值对,数据没有耦合性,容易扩展
3. 格式灵活:存储数据的格式多样。可以是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等

4. 成本低:nosql数据库部署简单,基本都是开源软件。
缺点缺点:
1、不提供sql支持,学习和使用成本较高;
2、无事务处理;
3、数据结构相对复杂,复杂查询方面稍欠。

1、Redis(Remote Dictionary Server

 是一个key_value型单线程数据库,可以用作数据库、缓存和消息的中间件。支持复杂的数据结构,支持持久化,支持主从集群,支持高可用

  • 数据类型

五种数据类型:String字符、Hash散列、List列表、Set集合、SortedSet或者叫zset有序集合。

String字符串:缓存、限流、计数器、分布式锁、分布式 Session

格式: set key value

string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。

string类型是Redis最基本的数据类型,一个键最大能存储512MB。

Hash(哈希):存储用户信息、用户主页访问量、组合查询

格式: hmset name  key1 value1 key2 value2

Redis hash 是一个键值(key=>value)对集合。

Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。

List(列表):微博关注人时间轴列表、简单队列

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边),通过list可以做简单的消息列队功能

格式: lpush  name  value

在 key 对应 list 的头部添加字符串元素

格式: rpush  name  value

在 key 对应 list 的尾部添加字符串元素

格式: lrem name  index

key 对应 list 中删除 count 个和 value 相同的元素

格式: llen name  

返回 key 对应 list 的长度

Set(集合):踩、赞、标签、好友关系

格式: sadd  name  value

Redis的Set是string类型的无序集合,集合中的数据不能重复出现,所以可以用来做全局的去重功能

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

zset(sorted set:有序集合):排行榜

格式: zadd  name score value

Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

zset的成员是唯一的,但分数(score)却可以重复。

速度解析:

  • Redis 是纯内存操作,并且异步持久化到硬盘中。
  • Redis 是单线程,从而避免了多线程中上下文频繁切换的问题。
  • Redis 底层数据结构简单,对数据的操作也简单。
  • Redis 使用非阻塞 I/O多路复用模型,效率高。

4Redis 的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此 Redis 适合的场景主要局限在较小数据量的高性能操作和运算上。

什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?

持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。

Redis 提供了两种持久化方式:RDB(默认) 和AOF 

RDB:

rdb是Redis DataBase缩写

功能核心函数rdbSave(生成RDB文件)和rdbLoad(从文件加载内存)两个函数

AOF:

Aof是Append-only file缩写

每当执行服务器(定时)任务或者函数时flushAppendOnlyFile 函数都会被调用, 这个函数执行以下两个工作

aof写入保存:

WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件

SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。

比较

1、aof文件比rdb更新频率高,优先使用aof还原数据。

2、aof比rdb更安全也更大

3、rdb性能比aof好

4、如果两个都配了优先加载AOF

过期删除策略:

在Redis内,我们可以使用EXPIRE或EXPIREAT设置key的过期时间,Redis内存达到maxmemory限制后,Redis内存就会施行过期数据淘汰策略。过期删除策略通常有三种:定时删除、惰性删除、定期删除,目前Redis使用的过期键删除策略为惰性删除和定期删除,两种策略配合使用。

惰性删除,指当某个key值过期之后,该key值不会马上被删除,只有当读或者写一个已经过期的key时,才会触发惰性删除策略,此时该key完成删除。

定期删除,指每隔一段时间就会扫描一定数量数据库的expires字典内一定数量的key,并删除里面过期的key。

由于惰性删除无法保证过期数据被及时删除掉,所以Redis采用惰性删除,定期删除相结合的方法,可以帮助实现定期主动淘汰已过期的key,从而使cpu和内存资源达到最优的平衡效果。

Redis 架构模式

单机版

 

特点:简单

问题:

1、内存容量有限 2、处理能力有限 3、无法高可用。

主从复制

Redis 的复制(replication)功能允许用户根据一个 Redis 服务器来创建任意多个该服务器的复制品,其中被复制的服务器为主服务器(master),而通过复制创建出来的服务器复制品则为从服务器(slave)。 只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,主服务器就会一直将发生在自己身上的数据更新同步给从服务器,从而一直保证主从服务器的数据相同。

特点:

1、master/slave 角色

2、master/slave 数据相同

3、降低 master 读压力在转交从库

问题:

无法保证高可用

没有解决 master 写的压力

哨兵

 

Redis sentinel 是一个分布式系统中监控 redis 主从服务器,并在主服务器下线时自动进行故障转移。其中三个特性:

监控(Monitoring):    Sentinel  会不断地检查你的主服务器和从服务器是否运作正常。

提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。

自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作。

特点:

1、保证高可用

2、监控各个节点

3、自动故障迁移

缺点:主从模式,切换需要时间丢数据

没有解决 master 写的压力

集群(proxy 型):

Twemproxy 是一个 Twitter 开源的一个 redis 和 memcache 快速/轻量级代理服务器; Twemproxy 是一个快速的单线程代理程序,支持 Memcached ASCII 协议和 redis 协议。

特点:1、多种 hash 算法:MD5、CRC16、CRC32、CRC32a、hsieh、murmur、Jenkins 

2、支持失败节点自动删除

3、后端 Sharding 分片逻辑对业务透明,业务方的读写方式和操作单个 Redis 一致

缺点:增加了新的 proxy,需要维护其高可用。

failover 逻辑需要自己实现,其本身不能支持故障的自动转移可扩展性差,进行扩缩容都需要手动干预

集群(直连型):

 

从redis 3.0之后版本支持redis-cluster集群,Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。

特点:

1、无中心架构(不存在哪个节点影响性能瓶颈),少了 proxy 层。

2、数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布。

3、可扩展性,可线性扩展到 1000 个节点,节点可动态添加或删除。

4、高可用性,部分节点不可用时,集群仍可用。通过增加 Slave 做备份数据副本

5、实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave到 Master 的角色提升。

缺点:

1、资源隔离性较差,容易出现相互影响的情况。

2、数据通过异步复制,不保证数据的强一致性

Redis 事务相关的命令有哪几个?

MULTI、EXEC、DISCARD、WATCH

Redis key 的过期时间和永久有效分别怎么设置?

EXPIRE 和 PERSIST 命令

Redis 如何做内存优化?

尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。

比如你的 web 系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的 key,而是应该把这个用户的所有信息存储到一张散列表里面。

缓存穿透

一般,都是按key去缓存查询,如果没查到,就去数据库查找。如果遇到大量请求查询永远不存在的key,就会都是数据库访问,这就会对数据库产生很大的压力,这种情况就是缓存穿透。

我们可以通过以下方式来防止:

(1)把所有可能会查询的key,放在一个bitmap中,查询前先校验bitmap中是否存在,避免查询本来不存在的数据请求。

(2)对查询结果为空的情况也进行缓存,有效时间设置短一点,在该key被插入后清除缓存,这样可以避免每次不存在的key都会访问数据库。

缓存雪崩

缓存重启或大量缓存同时段失效,需要都从数据库查,导致的系统崩溃,就是缓存雪崩。

我们可以通过下面方式尽量避免缓存雪崩:

(1)给不同的key,设置不同的过期时间,尽量合理,让缓存失效的时间点均匀分布。

(2)做两级缓存,第一级设置为短期缓存,第二级设置为长期缓存;保证一级缓存失效,可以去二级缓存查找,都找不到,则去数据库查找。

(3)对数据库访问做处理,就是在缓存失效后,通过一定手段控制从数据库查找数据并写入缓存的线程数据,如对查数据库,写入缓存这两部分加锁。

缓存击穿

和缓存雪崩不同之处是,单个缓存有效期到了,而同一时间点有大量请求进行查这个key,导致都访问到数据库了。

我们可以通过下面方式尽量避免缓存击穿:

(1)使用redis的setnx互斥锁。获取到锁,则查询缓存/数据库,否则就处于等待状态中,保证不会大并发操作数据库。

怎么保证缓存中的数据和数据库中的数据库的一致性?

延时双删策略:就是先删除缓存key,再更新数据库数据;等待一会后,再尝试删除一下缓存key,防止整个过程中,出现还没更新完成,其他线程查询了改key并缓存了旧值的情况。

使用过 Redis 做异步队列么,你是怎么用的?有什么缺点?

般使用 list 结构作为队列,rpush 生产消息,lpop 消费消息。当 lpop 没有消息的时候,要适当 sleep一会再重试。

缺点:

  • 在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如 rabbitmq 等。
  • 能不能生产一次消费多次呢?
  • 使用 pub/sub 主题订阅者模式,可以实现 1:N 的消息队列。

其他TIP

创建临时表的语法与创建表语法类似,不同之处是增加关键字TEMPORARY

count(1)指根据第一个字段来得到总行数,如果允许该字段为空会得到错误结果

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值