锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

JAVA八股文面试题(卷二)

时间:2023-02-08 20:00:00 数码fpc连接器fpc补强连接器

JAVA八股文面试题(卷2)

注:材料来自和https://www.bilibili.com/video/BV1LS4y1V7MX?p=41

MySQL

MySQL有哪些存储引擎?

可从系统结构图中找到,MySQL与其他数据库不同,数据库最重要的特点之一是其插件式表存储引擎。插件式存储引擎的优点是,每个存储引擎都有自己的特点,可以根据具体应用程序建立不同的存储引擎表。

InnoDB存储引擎
InnoDB是MySQL默认事务引擎也是最重要、最广泛使用的存储引擎。它被设计用于处理大量的短期内(short-lived)优先考虑事务InnoDB引擎。

MylSAM存储引擎
在MySQL 5.1及以前的版本,MylSAM是默认存储引擎。MylSAM不支持事务和行级
,崩溃后无法安全恢复MylSAM由于表锁的问题,很容易导致典型的性能问题。

Memory引擎
Memory表至少比MylSAM数量级快,数据文件存储在内存中。Memory重启后将保留表的结构,但数据将丢失。

MylSAM和InnoDB有什么区别?

1.InnoDB支持事务,MylSAM不支持事务。
这是MySQL默认存储引擎MylSAM变成InnoDB其中一个重要原因;

2.InnoDB支持外键,而MylSAM不支持。
包含外键的一个lnnoDB表转为MYISAM会失败;

3.InnoDB是聚集索引,MylSAM是非聚集索引。
聚簇索引的文件存储在主键索引的叶节点上,因此 InnoDB必须有主键,通过主键索引效率很高。但辅助索引需要两次查询,先查询主键,再通过键查询数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而MylSAM数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。

4.InnoDB 不保存表的具体行数,执行select count(*) from table需要全表扫描MylSAM用变量保存整个表的行数,执行上述句子时只需读出变量,速度非常快;

5.InnoDB最小锁粒度为行锁,MylSAM表锁是最小的锁粒。
一个更新的句子会锁定整个表,阻止其他查询和更新,因此并发访问有限。MySQL默认存储引擎 MylSAM变成InnoDB其中一个重要原因;

在设计数据库表时,您将如何选择字段?

1.字段类型优先级

整型> date,time > enum char > varchar > blob,text避免使用字段长度最小、固定长度和数值字段
“ZEROFILL”。
time :考虑时区,快速计算长度,节省时间sql不方便
enum:能约束值的目的是用整形手术储存内部,但与char联查时,内部应经历串与值的转换char:定长,考虑字符集和校对集
varchar:不定长,在转换和排序字符集时要考虑校对集,速度慢text, blob:内存临时表不能使用(排序操作只能在磁盘上进行)
注意: date,time可直接选择使用时间戳,enum(男;女)//内部转换成数字存储,多了一个转换过程,可以使用tinyint更换最佳用途tinyint。

2.可以选择整形而不选择字符串

整形是固定的,没有国家/地区的区别,也没有字符集的区别。tinyint 和char(1)从空间上看是字节,但是order by排序 tinyint 快。原因是后者需要考虑字符集和校对集(即排序优先集)。

3.足够就够了,不要慷慨

大字段影响内存速度。以年龄为例:tinyint unsigned not null;可储存255岁,足够使用int浪费三个字节varchar(10),,varchar(300)存储内容相同,但在表中查询时,varhcar(300)花更多的内存。

4.尽量避免使用NULL

Null不利于索引和查询。=null或者!= null都找不到值,只能用is null或者is notnull可以。因此,它可以在创建字段时使用。not null default “”
的形式。

5.char与varchar选择

char长度固定,处理速度比varchar要快得多,但存储空间相对昂贵;因此,对存储空间的要求不大,但对速度有要求的可以使用char类型,反之亦然varchar类型。

Mysql中VARCHAR(M)最多能存储多少数据?

对于VARCHAR(M)类型列最多可定义65535个字节。M代表这种类型中存储的字符数量最多,但在实际存储中不能存储这么多。
MySQL除了限制记录占用的最大存储空间外,记录所占用的最大存储空间BLOB或者TEXT除类型列外,所有其他列(不包括隐藏列和记录头信息)占用的字节长度不得超过65535字节。

请谈谈事务的基本特征

事务应该有四个属性**:原子性、一致性、隔离性、持久性**。这四个属性通常被称为ACID特性。

原子性:这意味着一个事务中的所有操作要么成功,要么失败
一致性:这意味着数据库总是从一致性状态转变为另一致性状态。例如,A转移到B假设中间100元sql系统崩溃A在执行过程中不会损失100元,因为事务没有提交,修改也不会保存在数据库中。
隔离性:这意味着在最终提交前修改一个事务,对其他事务是看不见的。
持久性:这意味着一旦事务提交,修改将永久保存在数据库中。

事务并发可能导致什么问题?

脏读

1.在事务A执行过程中,事务A修改了数据资源,事务B读取了事务A修改后的数据。
2.由于某些原因,事务A没有完成提交,发生了RollBack操作时,事务B读取的数据为脏数据。
读取另一项事务未提交的数据的现象是脏读(Dirty Read)
请添加图片描述

不可重复读

读取事务B两个数据资源,在这两次这两次读取过程中修改了数据,导致事务B在这两次读取数据不一致。在同一事务中,前后两次读取的数据不一致的现象是不能重复读取(NonrepeatableRead)。

幻读

事务B前后读取相同范围的数据两次。在事务B两次读取的过程中,事务A增加了数据,导致事务B后一次读取到前一次查询中未见的行。幻读类似于不可重复读取,但幻读强调集合的增减,而不是单个数据的更新

简单描述下MySQL各种索引?

MySQL根据字段特征,索引可分为:主键索引、普通索引、前缀索引

1.主键索引

基于主键的索引被称为主键索引,数据表只能有一个主键索引,索引列值不允许有空值,通常在创建表时一起创建。

2.唯一索引

建立在UNIQUE字段上的索引被称为唯一索引,一张表可以有多个唯一的索引,允许索引列值为空,列值中的多个空值不会重复冲突。

3.普通索引

基于普通字段的索引被称为普通索引

4.前缀索引

前缀索引是指对字符类型字段的前几个字符或对二进制前几个类型字段bytes建立的索引,而不是整个字段。前缀索引可以建立在类型上char.
varchar、binary、varbinary的列
它可以大大降低索引占用的存储空间,提高索引的查询效率。
前缀索引是使索引更小更快的有效方法,但也包含缺点: mysql前缀索引不能用order by 和group by

三星索引是什么?

三星索引可能是查询中最好的索引。
如果使用三星索引进行查询,一次查询通常只需要随机读取磁盘和窄引片的扫描,因此其相应时间通常比使用一个普通索引的响应时间少几个数量级。
一个查询相关的索引行是相邻的或者至少相距足够靠近的则获得一星;

如果索引中的数据顺序和查找中的排列顺序一致则获得二星

如果索引中的列包含了查询中需要的全部列则获得三星。
三星索引在实际的业务中如果无法同时达到,一般我们认为第三颗星最重要,第一和第二颗星重要性差不多,根据业务情况调整这两颗星的优先度。

lnnoDB——棵B+树可以存放多少行数据?

这个问题的其实非常简单:约2千万

计算机在存储数据的时候,有最小存储单元,在计算机中磁盘存储数据最小单元是扇区(这就好比我们今天进行现金的流通最小单位是一毛),一个扇区的大小是512字节,而文件系统(例如XFS/EXT4)的最小单元是块,一个块的大小是4k,而对于我们的InnoDB存储引擎也有自己的最小储存单元——页(Page) ,一个页的大小是16K。

Innodb的所有数据文件(后缀为ibd的文件),他的大小始终都是16384 (16k)的整数倍。

数据表中的数据都是存储在页中的,所以一个页中能存储多少行数据呢?假设一行数据的大小是1k,那么一个页可以存放16行这样的数据。对于B+树而言,只有叶子节点存放数据,非叶子节点存放的是只保存索引信息和下一层节点的指针信息。一个非叶子节点能存放多少指针?其实这也很好算,我们假设主键ID为常用的bigint类型,长度为8字节,而指针大小在InnoDB源码中设置为6字节,这样一共14字节,我们一个页中能存放多少这样的单元,其实就代表有多少指针,即16384/14=1170个。那么可以算出一棵高度为2的B+树,存在一个根节点和若干个叶子节点能存放117016=18720条这样的数据记录。
根据同样的原理我们可以算出一个高度为3的B+树可以存放1170117016=21902400条这样的记录。

如何提高insert的性能?

1、合并多条insert为一条

即: insert into t values(a,b,c),(d,e,f) …
原因分析:主要原因是多条insert合并后日志量(MySQL的binlog和innodb的事务日志)减少了,降低日志刷盘的数据量和频率,从而提高效率。通过合并SQL语句,同时也能减少SQL语句解析的次数,减少网络传输的IO。

2、修改参数bulk_insert_buffer_size,调大批量插入的缓存;

3、设置innodb_flush_log_at_trx_commit = 0

相对于 innodb_flush_log_at_trx_commit = 1可以十分明显的提升导入速度;innodb_flush_log_at_trx_commit参数解释如下:
0: log buffer中的数据将以每秒一次的频率写入到og file中,且同时会进行文件系统到磁盘的同步操作,但是每个事务的commit并不会触发任何log buffer到log file 的刷新或者文件系统到磁盘的刷新操作;
1:在每次事务提交的时候将log buffer中的数据都会写入到log file,同时也会触发文件系统到磁盘的同步;
2:事务提交会触发log buffer到log file的刷新,但并不会触发磁盘文件系统到磁盘的同步。此外,每秒会有一次文件系统到磁盘同步操作。

4、手动使用事务

因为mysql默认是autocommit的,这样每插入一条数据,都会进行一次commit;所以,为了减少创建事务的消耗,我们可用手工使用事务
即START TRANSACTION;insert。。,insert。。commit;即执行多个insert后再一起提交;一般1000条insert 提交一次。

什么是全局锁、共享锁、排它锁?

全局锁就是对整个数据库实例加锁,它的典型使用场景就是做全库逻辑备份。这个命令可以使整个库处于只读状态。使用该命令之后,数据更新语句、数据定义语句、更新类事务的提交语句等操作都会被阻塞。
共享锁又称读锁(read lock),是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。当如果事务对读锁进行修改操作,很可能会造成死锁。
排他锁exclusive lock(也叫 writer lock)又称写锁。
若某个事物对某一行加上了排他锁,只能这个事务对其进行读写,在此事务结束之前,其他事务不能对其进行加任何锁,其他进程可以读取,不能进行写操作,需等待其释放。排它锁是悲观锁的一种实现。

谈一下MySQL中的死锁

死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。

如何查看死锁?

使用命令show engine innodb status查看最近的一次死锁。
IlnnoDB Lock Monitor打开锁监控,每15s输出一次日志。使用完毕后建议关闭,否则会影响数据库性能。

对待死锁常见的两种策略:

通过innodblockwait_timeout来设置超时时间,一直等待直到超时;
发起死锁检测,发现死锁之后,主动回滚死锁中的某一个事务,让其它事务继续执行。

RabbitMQ

为什么使用消息队列(MQ)?

面试官问你这个问题,期望的一个回答是说,你们公司有个什么业务场景,这个业务场景有个什么技术挑战,如果不用MQ可能会很麻烦,但是你现在用了MQ之后带给了你很多的好处。消息队列的常见使用场景,其实场景有很多,但是比较核心的有3个:解耦、异步、削峰
异步

解耦

削峰
每天0点到16点,A系统风平浪静,每秒并发请求数量就100个。结果每次一到16点~23点,每秒并发请求数量突然会暴增到1万条。但是系统最大的处理能力就只能是每秒钟处理1000个请求啊。怎么办?需要我们进行流量的削峰,让系统可以平缓的处理突增的请求。

如何选择合适的消息队列(MQ)?

RabbitMQ


RabbitMQ于2007年发布,是使用Erlang编程语言编写的,最早是为电信行业系统之间的可靠通信设计的,也是少数几个支持AMQP协议的消息队列之
RabbitMQ:轻量级、迅捷,它的宣传口号,也很明确地表明了RabbitMQ的特点:Messagingthat just works,开箱即用的消息队列。也就是说,RabbitMQ是一个相当轻量级的消息队列,非常容易部署和使用。
RabbitMQ的客户端支持的编程语言大概是所有消息队列中最多的。

RabbitMQ的问题
·RabbitMQ对消息堆积的支持并不好,当大量消息积压的时候,会导致RabbitMQ的性能急剧下降。
·RabbitMQ的性能是这几个消息队列中最差的,大概每秒钟可以处理几万到十几万条消息。如果应用对消息队列的性能要求非常高,那不要选择RabbitMQ。
.RabbitMQ使用的编程语言Erlang,扩展和二次开发成本高。

Kafka


Apache Kafka是一个分布式消息发布订阅系统。它最初由LinkedIln公司基于独特的设计实现为一个分布式的日志提交系统,之后成为Apache 项目的一部分。
在早期的版本中,为了获得极致的性能,在设计方面做了很多的牺牲,比如不保证消息的可靠性,可能会丢失消息,也不支持集群,功能上也比较简陋,这些牺牲对于处理海量日志这个特定的场景都是可以接受的。
但是,随后几年Kafka逐步补齐了这些短板,当下的Kafka已经发展为一个非常成熟的消息队列产品,无论在数据可靠性、稳定性和功能特性等方面都可以满足绝大多数场景的需求。

Kafka与周边生态系统的兼容性是最好的没有之一,尤其在大数据和流计算领域,几乎所有的相关开源软件系统都会优先支持Kafka。
Kafka 性能高效、可扩展良好并且可持久化。它的分区特性,可复制和可容错都是不错的特性。Kafka使用Scala和Java语言开发,设计上大量使用了批量和异步的思想,使得Kafka 能做到超高的性能。Kafka的性能,尤其是异步收发的性能,是三者中最好的,但与RocketMQ并没有量级上的差异,大约每秒钟可以处理几十万条消息。在有足够的客户端并发进行异步批量发送,并且开启压缩的情况下,Kafka的极限处理能力可以超过每秒2000万条消息。

Kafka的问题
Kafka异步批量的设计带来的问题是,它的同步收发消息的响应时延比较高,因为当客户端发送一条消息的时候,Kafka 并不会立即发送出去,而是要等一会儿攒一批再发送,在它的 Broker中,很多地方都会使用这种先攒一波再一起处理的设计。当你的业务场景中,每秒钟消息数量没有那么多的时候,Kafka 的时延反而会比较高。所以,Kafka 不太适合在线业务场景。topic达到上百个时,吞吐量会大幅下降。

RocketMQ


RocketMQ是阿里巴巴在2012年开源的消息队列产品,用Java语言实现,在设计时参考了Kafka,并做出了自己的一些改进,后来捐赠给Apache 软件基金会,2017正式毕业,成为Apache 的顶级项目。RocketMQ在阿里内部被广泛应用在订单,交易,充值,流计算,消息推
送,日志流式处理,Binglog分发等场景。经历过多次双十一考验,它的性能、稳定性和可靠性都是值得信赖的。
RocketMQ有着不错的性能,稳定性和可靠性,具备一个现代的消息队列应该有的几乎全部功能和特性,并且它还在持续的成长中。
RocketMQ有非常活跃的中文社区,大多数问题可以找到中文的答案。RocketMQ使用Java语言开发,源代码相对比较容易读懂,容易对 RocketMQ进行扩展或者二次开发。RocketMQ对在线业务的响应时延做了很多的优化,大多数情况下可以做到毫秒级的响应,如果你的应用场景很在意响应时延,那应该选择使用RocketMQ。RocketMQ的性能比 RabbitMQ要高一个数量级,每秒钟大概能处理几十万条消息。

RocketMQ的问题
RocketMQ的劣势是与周边生态系统的集成和兼容程度不够

RabbitMQ如何保证消息不丢失?

1)确保消息到MQ
2)确保消息路由到正确的队列
3)确保消息在队列正确的存储
4)确保消息从队列中正确的投递至消费者

解决方案:
确保消息到MQ:发送方的确认模式
确保消息路由到正确的队列:路由失败通知
确保消息在队列正确的存储:交换器、队列、消息都需要持久化
确保消息从队列中正确的投递至消费者:手动确认-→>交给消费者来确认

什么是MQ中的消息重复?

第一类原因
消息发送端应用的消息重复发送,有以下几种情况。

·消息发送端发送消息给消息中间件,消息中间件收到消息并成功存储,而这时消息中间件出现了问题,导致应用端没有收到消息发送成功的返回因而进行重试产生了重复。
·消息中间件因为负载高响应变慢,成功把消息存储到消息存储中后,返回“成功”这个结果时超时。
·消息中间件将消息成功写入消息存储,在返回结果时网络出现问题,导致应用发送端重试,而重试时网络恢复,由此导致重复。
·可以看到,通过消息发送端产生消息重复的主要原因是消息成功进入消息存储后,因为各种原因使得消息发送端没有收到“成功”的返回结果,并且又有重试机制,因而导致重复。
第二类原因
消息到达了消息存储,由消息中间件进行向外的投递时产生重复,有以下几种情况。
·消息被投递到消息接收者应用进行处理,处理完毕后应用出问题了,消息中间件不知道消息处理结果,会再次投递。
·消息被投递到消息接收者应用进行处理,处理完毕后网络出现问题了,消息中间件没有收到消息处理结果,会再次投递。
·消息被投递到消息接收者应用进行处理,处理时间比较长,消息中间件因为消息超时会再次投递。

如何解决MQ中的重复消息?

那么有什么办法可以解决呢?主要是要求消息接收者来处理这种重复的情况,也就是要求消息接收者的消息处理是幂等操作。
什么是幂等性?
对于消息接收端的情况,幂等的含义是采用同样的输入多次调用处理函数,得到同样的结果。例如,一个SQL操作
update stat_table set count= 10 where id =1
这个操作多次执行,id等于1的记录中的count字段的值都为10,这个操作就是幂等的,我们不用担心这个操作被重复。
再来看另外一个SQL操作
update stat_table set count= count +1 where id= 1;这样的SQL操作就不是幂等的,一旦重复,结果就会产生变化。

因此应对消息重复的办法是,使消息接收端的处理是一个幂等操作。这样的做法降低了消息中间件的整体复杂性,不过也给使用消息中间件的消息接收
端应用带来了一定的限制和门槛。

1.MVCC:

多版本并发控制,乐观锁的一种实现,仕生广有区达旧心的L133n0号,消费者去更新时需要去比较持有数据的版本号,
版本号不一致的操作无法成功。例如博客点赞次数自动+1的接口:public boolean addCount(Long id, Long version);
update blogTable set count= count+1,version=version+1 where id=321 and version=123每一个version只有一次执行成功的机会,一旦失败了生产者必须重新获取数据的最新版本号再次发起更新。

2.去重表:

利用数据库表单的特性来实现幂等,常用的一个思路是在表上构建唯一性索引,保证某一类数据一旦执行完毕,后续同样的请求不再重复处理了(利
用一张日志表来记录已经处理成功的消息的ID,如果新到的消息ID已经在日志表中,那么就不再处理这条消息。)

以电商平台为例子,电商平台上的订单id就是最适合的token。当用户下单时,会经历多个环节,比如生成订单,减库存,减优惠券等等。每一个环节执行时都先检测一下该订单id是否已经执行过这一步骤,对未执行的请求,执行操作并缓存结果,而对已经执行过的id,则直接返回之前的执行结果,不做任何操作。这样可以在最大程度上避免操作的重复执行问题,缓存起来的执行结果也能用于事务的控制等。

如何进行RocketMQ的性能调优?

JVM层面
监控暂停
rocketmq-console这个是官方提供了一个WEB项目,可以查看rocketmq数据和执行一些操作。但是这个监控界面又没有权限控制,并且还有一些消耗性能的查询操作,如果要提高性能,建议这个可以暂停。一般的公司在运维方面会有专门的监控组件,如zabbix会做统一处理。或者是简单的shell命令监控的方式有很多,比如简单点的,我们可以写一个shell脚本,监控执行rocketmqJava进程的存活状态,如果rocketmq crash了,发送告警。

消除偏向锁
在JDK1.8,sync有偏向锁,但是在RocketMQ都是多线程的执行,所以竞争比较激烈,建议把偏向锁取消,以免没有必要的开销。
-XX:-UseBiasedLocking:禁用偏向锁

垃圾回收参数
RocketMQ推荐使用G1垃圾回收器。
-Xms8g -Xmx8g -Xmn4g -XX:+UseG1GC
这个就是很关键的一块参数了,也是重点需要调整的,就是默认的堆大小是8g内存,新生代是4g内存。如果是内存比较大,比如有48g的内存,所以这里完全可以给他们翻几倍,比如给堆内存20g,其中新生代给10g,甚至可以更多些,当然要留一些内存给操作系统来用
-XX:G1HeapRegionSize=16m
这里把G1的region大小设置为了16m,这个因为机器内存比较多,所以region大小可以调大一些给到16m,不然用2m的region,会导致region数量过多。
-XX:G1ReservePercent=25
这个参数是说,在G1管理的老年代里预留25%的空闲内存,保证新生代对象晋升到老年代的时候有足够空间,避免老年代内存都满了,新生代有对象要进入老年代没有充足内存了。默认值是10%,略微偏少,这里RocketMQ给调大了一些

-XX:initiatingHeapOccupancyPercent= 30

这个参数是说,当堆内存的使用率达到30%之后就会自动启动G1的并发垃圾回收,开始尝试回收一些垃圾对象。默认值是45%,这里调低了一些,也就是提高了GC的频率,但是避免了垃圾对象过多,一次垃圾回收耗时过长的问题

操作系统层面

基本参数
vm.overcommit_memory1

是否允许内存的过量分配
当为0的时候,当用户申请内存的时候,内核会去检查是否有这么大的内存空间

当为1的时候,内核始终认为,有足够大的内存空间,直到它用完了为止

当为2的时候,内核禁止任何形式的过量分配内存

vm.swappiness= 10
swappiness=0仅在内存不足的情况下,当剩余空闲内存低于vm.min_free_kbytes limit时,使用交换空间
swappiness=1内核版本3.5及以上、Red Hat内核版本2.6.32-303及以上,进行最少量的交换,而不禁用交换

swappiness=10当系统存在足够内存时,推荐设置为该值以提高性能swappiness=60默认值
swappiness=100内核将积极的使用交换空间vm.max_max_count=655360+
定义了一个进程能拥有的最多的内存区域,默认为65536ulimit=1000000
limits.conf设置用户能打开的最大文件数.

网卡
网络接口控制器(英语: network interface controller,NIC)
因Ring Buffer 写满导致丢包的
情况很多。当业务流量过大且出现网卡丢包的时候,建议调整Ring Buffer的大小,这个大小的设置一定程度上是可以缓解丢包的状况。
关闭IRQBalance
IRQBalance主要功能是可以合理的调配使用各个CPU核心,特别是对于目前主流多核心的CPU,简单的说就是能够把压力均匀的分配到各个CPU核心上,对提升性能有很大的帮助。
但实际中往往影响cpu的使用均衡,建议服务器环境中关闭。

TCP NODEALY
Nagle算法用于对缓冲区内的一定数量的消息进行自动连接。该处理过程(称为Nagling),通过减少必须发送的封包的数量,提高了网络应用程序系统的效率。
(Nagle虽然解决了小封包问题,
但也导致了较高的不可预测的延迟,同时降低了吞吐量。)

Redis

什么是Redis?

Redis是现在最受欢迎的NoSQL数据库之一,Redis是一个使用ANSI C编写的开源、包含多种数据结构、*支持网络、基于内存、可选持久性的键值对存储数据库,其具备如下特性:

基 于内存运行,性能高效(每秒可以处理超过10万次读写操作)

支持分布式,理论上可以无限扩展

key-value存储系统(key是字符串,键有字符串、列表、集合、散列表、有序集合等)

开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API

Redis有哪些数据类型?各自的使用场景?

Redis主要有5种数据类型,包括String,List,Set,Zset,Hash,满足大部分的使用要求。但Redis还为我们提供了几种高级数据结构,bitmaps,HyperLogLong、Geo,其中bitmaps,HyperLogLong底层是基于String,Geo则是基于zset。

string

介绍:
字符串类型是Redis最基础的数据结构。首先键都是字符串类型,而且其他几种数据结构都是在字符串类型基础上构建的,所以字符串类型能为其他四种数据结构的学习奠定基础。字符串类型的值实际可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB。
使用场景:
字符串类型的使用场景很广泛:

缓存功能
Redis 作为缓存层,MySQL作为存储层,绝大部分请求的数据都是从Redis中获取。由于Redis具有支撑高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用。
计数
使用Redis作为计数的基础工具,它可以实现快速计数、查询缓存的功能,同时数据可以异步落地到其他数据源。

共享Session
一个分布式Web服务将用户的Session信息(例如用户登录信息)保存在各自服务器中,这样会造成一个问题,出于负载均衡的考虑,分布式服务会将用户的访问均衡到不同服务器上,用户刷新一次访问可能会发现需要重新登录,这个问题是用户无法容忍的。
为了解决这个问题,可以使用Redis将用户的Session进行集中管理,在这种模式下只要保让Redis是高可用和扩展性的,每次用户更新或者查询登录信息都直接从Redis中集中获取。
限速
比如,很多应用出于安全的考虑,会在每次进行登录时,让用户输入手机验证码,从而确定是否是用户本人。但是为了短信接口不被频繁访问,会限制用户每分钟获取验证码的频率,例如一分钟不能超过5次。一些网站限制一个IP地址不能在一秒钟之内方问超过n次也可以采用类似的思路。

list

介绍:
list即是链表
列表( list)类型是用来存储多个有序的字符串,a、b、c、 d、e五个元素从左到右组成了一个有序的列表,列表中的每个字符串称为元素(element)
列表类型有两个特点:第一、列表中的元素是有序的,这就意味着可以通过索引下标获取某个元素或者某个范围内的元素列表。第二、列表中的元素可以是重复的。

使用场景:
消息队列,Redis的 lpush+ brpop命令组合即可实现阻塞队列,生产者客户端使用lrpush从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞式的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。
文章列表
每个用户有属于自己的文章列表,现需要分页展示文章列表。
此时可以考虑使用列表,因为列表不
但是有序的,同时支持按照索引范围获取元素。
实现其他数据结构
lpush+lpop = Stack (栈)lpush +rpop = Queue(队列)
lpsh+ ltrim = Capped Collection(有限集合)lpush+brpop =Message Queue(消息队列)

hash

介绍: hash类似于JDK1.8前的HashMap,内部实现也差不多(数组+链表)。

使用场景:
哈希类型比较适宜存放对象类型的数据,我们可以比较下,如果数据库中表记录为:
使用String类型:

set user:1:name king;set user:1:age 18;set user: 1:sex boy;
优点:简单直观,每个键对应一个值
缺点:键数过多,占用内存多,用户信息过于分散,不用于生产环境

将对象序列化存入redis
set user:1 serialize(userlnfo);
优点:编程简单,若使用序列化合理内存使用率高
缺点:序列化与反序列化有一定开销,更新属性时需要把userlnfo全取出来进行反序列化,更新后再序列化到redis
使用hash类型:
hmseuser:1 hame King age 18 sex boy
优点:简单直观,使用合理可减少内存空间消耗

set

介绍: set类似于lava中的 HashSet。Redis中的set类型是一种无序集合,集合中的元素没有先后顺序。当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择.

使用场景:
set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。可以基于set轻易实现交集、并集、差集的操作。
集合类型比较典型的使用场景是
标签( tag)。例如一个用户可能对娱乐、体育比较感兴趣,另一个用户可能对历史、新闻比较感兴趣,这些兴趣点就是标签。有了这些数据就可以得到喜欢同一个标签的人,以及用户的共同喜好的标签,这些数据对于用户体验以及增强用户黏度比较重要。例如一个电子商务的网站会对不同标签的用户做不同类型的推荐,比如对数码产品比较感兴趣的人,在各个页面或者通过邮件的形式给他们推荐最新的数码产品,通常会为网站带来更多的利益。

除此之外,集合还可以通过生成随机数进行比如抽奖活动,以及社交图谱等等。

zset

介绍:
有序集合相对于哈希、列表、集合来说会有一点点陌生,但既然叫有序集合,那么它和集合必然有着联系,它保留了集合不能有重复成员的特性,但不同的是,有序集合中的元素可以排序。但是它和列表使用索引下标作为排序依据不同的是,它给每个元素设置一个分数( score)作为排序的依据。有序集合中的元素不能重复,但是score可以重复,就和一个班里的同学学号不能重复,但是考试成绩可以相同。
有序集合提供了获取指定分数和元素范围查询、计算成员排名等功能,合理的利用有序集合,能帮助我们在实际开发中解决很多问题。
使用场景:
有序集合比较典型的使用场景就是排行榜系统。例如视频网站需要对用户上传的视频做排行榜,榜单的维度可能是多个方面的:按照时间、按照播放数量、按照获得的赞数。

bitmap

介绍:
现代计算机用二进制(位)作为信息的基础单位,1个字节等于8位,例如“big”字符串是由3个字
节组成,但实际在计算机存储时将其用二进制表示,“big”分别对应的ASCII码分别是98、105、103,对应的二进制分别是01100010、01101001和01100111。
Bitmaps本身不是一种数据结构,实际上它就是字符串,但是它可以对字符串的位进行操作。Bitmaps单独提供了一套命令,所以在Redis中使用Bitmaps和使用字符串的方法不太相同。可以把Bitmaps想象成一个以位为单位的数组,数组的每个单元只能存储0和1,数组的下标在Bitmaps 中叫做偏移量。

使用场景:适合需要保存状态信息(比如是否签到、是否登录…)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。

为什么要用Redis(缓存)?

主要从“高性能”和“高并发”这两点来看待这个问题。

高性能:
假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在数缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!

高并发:
直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。

Redis与Memcached的区别

两者都是非关系型内存键值数据库,现在公司一般都是用Redis来实现缓存,而且Redis自身也越来越强大了! Redis 与Memcached主要有以下不同:
(1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型

(2)redis的速度比memcached快很多

(3) redis可以持久化其数据

Redis的应用场景

计数器

可以对String进行自增自减运算,从而实现计数器功能。Redis这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。

缓存

将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率。

会话缓存

可以使用Redis 来统一存储多台应用服务器的会话信息。当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用性以及可伸缩性。

全页缓存(FPC)

除基本的会话token之外,Redis还提供很简便的FPC平台。以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。

查找表

例如DNS记录就很适合使用Redis进行存储。查找表和缓存类似,也是利用了Redis快速的查找特性。但是查找表的内容不能失效,而缓存的内容可以失效
因为缓存不作为可靠的数据来
源。

消息队列(发布/订阅功能)

List是一个双向链表,可以通过lpush和rpop写入和读取消息。不过最好使用Kafka、RabbitMQ等消息中间件。

分布式锁实现

在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。可以使用Redis自带的SETNX命令实现分布式锁,除此之外,还可以使用官方提供的RedLock分布式锁实现。

其它

Set可以实现交集、并集等操作,从而实现共同好友等功能。ZSet可以实现有序性操作,从而实现排行榜等功能。

Redis为什么这么快?

1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap 的优势就是查找和操作的时间复杂度都是O(1);
2、数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的;
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4、使用多路I/О复用模型,非阻塞IO;
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,
Redis直接自己构建了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

为什么要用Redis 而不用map/guava做缓存?

缓存分为本地缓存和分布式缓存。
以Java为例,使用自带的map或者guava 实现的是本地缓存,最主要的特点是轻量以及快速,生命周期随着jvm 的销毁而结束,并且在多实例的情况下,每个实例都需要各自保存一份缓存,缓存不具有一致性。
使用redis或 memcached之类的称为分布式缓存,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。缺点是需要保持redis或 memcached服务的高可用,整个程序架构上较为复杂。

Redis的持久化机制是什么?

Redis 的数据全部在内存里,如果突然宕机,数据就会全部丢失,因此必须有一种机制来保证

Redis的数据不会因为故障而丢失,这种机制就是

Redis的持久化机制。Redis 的持久化机制有两种,第一种是RDB快照,第二种是AOF日志。

RDB持久化

RDB持久化是将某个时间点上Redis中的数据保存到一个RDB文件中,如下所示

该文件是一个经过压缩的二进制文件,通过该文件可以还原生成RDB文件时Redis中的数据,如下所示:


Redis提供了2个命令来创建RDB文件,一个是SAVE,另一个是BGSAVE。
因为BGSAVE命令可以在不阻塞服务器进程的情况下执行,所以推荐使用BGSAVE命令。

载入RDB文件

载入RDB文件的目的是为了在Redis服务器进程重新启动之后还原之前存储在Redis中的数据。然后,Redis载入RDB文件并没有专门的命令,而是在Redis服务器启动时自动执行的。而且,Redis服务器启动时是否会载入RDB文件还取决于服务器是否启用了AOF持久化功能,具体判断逻辑为:
1.只有在AOF持久化功能处于关闭状态时,服务器才会使用RDB文件来还原数据。

2.如果服务器开启了AOF持久化功能,那么服务器会优先使用AOF文件来还原数据。

AOF持久化

AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库数据的

默认情况下,AOF持久化功能是关闭的,如果想要打开,可以修改配置。

载入AOF文件

因为AOF文件包含了重建数据库所需的所有写命令,所以Redis服务器只要读入并重新执行一遍AOF文件里面保存的写命令,就可以还原Redis服务器关闭之前的数据。
Redis读取AOF文件并还原数据库的详细步骤如下:
1.创建一个不带网络连接的伪客户端
(因为Redis的命令只能在客户端上下文中执行,而载入AOF文件时所使用的命令直接来源于AOF文件而不是网络连接,所以服务器使用了一个没有网络连接的伪客户端来执行AOF文件保存的写命令。伪客户端执行命令的效果和带网络连接的客户端执行命令的效果完全一样)

2.从AOF文件中分析并读取出一条写命令。

3.使用伪客户端执行被读取出的写命令。

4.一直执行步骤2和步骤3,直到AOF文件中的所有写命令都被执行完毕。

RDB持久化、AOF持久化的区别

实现方式

RDB持久化是通过将某个时间点Redis服务器存储的数据保存到RDB文件中来实现持久化的。AOF持久化是通过将Redis服务器执行的所有写命令保存到AOF文件中来实现持久化的。

文件体积

由上述实现方式可知,RDB持久化记录的是结果,AOF持久化记录的是过程,所以AOF持久化生成的AOF文件会有体积越来越大的问题,Redis提供了AOF重写功能来减小AOF文件体积。

安全性

AOF持久化的安全性要比RDB持久化的安全性高,即如果发生机器故障,AOF持久化要比RDB持久化丢失的数据要少。因为RDB持久化会丢失上次RDB持久化后写入的数据,而AOF持久化最多丢失1s之内写入的数据(使用默认evervsec配詈的话)

优先级

由于上述的安全性问题,如果Redis服务器开启了AOF持久化功能,Redis服务器在启动时会使用AOF文件来还原数据,如果Redis服务器没有开启AOF持久化功能,Redis服务器在启动时会使用RDB文件来还原数据,所以AOF文件的优先级比RDB文件的优先级高。

如何保证缓存与数据库双写时的数据一致性?

什么是数据一致性问题?

只要使用到缓存,无论是本地内存做缓存还是使用redis做缓存,那么就会存在数据同步的问题。
更新缓存方案
1、先更新缓存,再更新DB
这个方案我们一般不考虑。原因是更新缓存成功,更新数据库出现异常了,导致缓存数据与数据库数据完全不一致,而且很难察觉,因为缓存中的数据一直都存在。

2.先更新DB,再更新缓存
这个方案也我们一般不考虑,原因跟第一个一样,数据库更新成功了,缓存更新失败,同样会出现数据不一致问题。

首先,给缓存设有效时间是一种方案。其次,采用异步延时删除策略。
但是,更新数据库成功了,但是在删除缓存的阶段出错了没有删除成功怎么办?这个问题,在删除缓存类的方案都是存在的,那么此时再读取缓存的时候每次都是错误的数据了。
此时解决方案有两个,一是就是利用消息队列进行删除的补偿。

什么是缓存穿透?怎么解决?

缓存穿透是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。

解决方案
接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓仔伺效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。

布隆过滤器

采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap中,一个一定不存在的数据会被这个bitmap 拦截掉,从而避免了对底层存储系统的查询压力

1970年布隆提出了十种布隆过滤器的算法,用来判断一个元素是否在一个集合中。这种算法由一个二进制数组和一个Hash 算法组成。

选择多个Hash 函数计算多个Hash值,这样可以减少误判的几率

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章