22年11月底,应聘杭州西湖区某自研公司,按照客户要求做activiti相关开发。
题目的答案,是根据我的理解和个人整理编写的。可能有不对的地方,欢迎大家指正。
string(字符串),hash(哈希),list(列表),set(集合),zsetsorted set:有序集合。
如果是 Redis 高级用户,还可以加上 HyperLogLog、Geo、Pub/Sub。
如果说还玩过 Redis Module,像 BloomFilter,RedisSearch,Redis-ML,就更牛逼了。
String
最常规的是set/get操作,value可以是String也可以是数字。
一般做一些复杂的计数功能的缓存。
Hash
value存放的是结构化的对象,比较方便的就是操作其中的某个字段。
博主在做单点登录的时候,就是用这种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session的效果。
list
可以做简单的消息队列的功能。
还有,可以利用lrange命令,做基于redis的分页功能,性能极佳,用户体验好。
还有,很合适生产者和消费者的场景。LIST可以很好的完成排队,先进先出的原则。
set
因为set堆放的是一堆不重复值的集合。所以可以做全局去重的功能。
为什么不用JVM自带的Set进行去重?
因为系统一般都是集群部署,使用JVM自带的Set,比较麻烦,难道为了一个做一个全局去重,再起一个公共服务,太麻烦了。
另外,利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。
sorted set
setsorted比set多了一个权重参数score,集合中的元素能够按score进行排列。
可以做排行榜应用,取TOP N 操作
基于内存
1、通过redis日志记录排查问题
2、通过redis内存使用情况排查问题
3、通过命令检测redis客户端用户的连接数
网友经验:可能是Redis内存占用太高(写二进制文件之类的大操作),Linux为了保护自己把它杀了。
网友经验2:弄套普罗米修斯(prometheus)和 granfa,自动监控系统,不难,可以快速定位问题,这样以后的工作就好做了
(我觉得就是问持久化)
Redis 提供两种持久化机制 RDB 和 AOF 机制:
Redis DataBase: 用数据集快照的方式半持久化模式 记录 redis 数据库的所有键值对,在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。
优点:1、只有一个文件 dump.rdb,方便持久化。
2、容灾性好,一个文件可以保存到安全的磁盘。
3、性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis的高性能
4.相对于数据集大时,比 AOF 的启动效率更高。
缺点:1、数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候
Append-only file: 是指所有的命令行记录以 redis 命令请求协议的格式完全持久化存储 保存为 aof 文件。
优点:1、数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次命令操作就记录到 aof 文件中一次。
2、通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof工具解决数据一致性问题。
3、AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的 flushall))
缺点:1、AOF 文件比 RDB 文件大,且恢复速度慢。2、数据集大的时候,比 rdb 启动效率低。
———AOF的 appendfsync 属性 补充———
aof的修改内容持久化写入aof file的步骤
aof buffer → os buffer → aof file
appendfsync=always,每次修改事件都将aof buffer的内容写到aof文件并同步aof文件,这种策略安全性最高,效率最低,发生系统奔溃时最多丢失一个事件循环的数据。
appendfsync=everysec,每次修改都aof buffer内容写到aof文件,每秒同步一次aof文件,这种策略与每次修改都同步的策略相比,安全性差一些,但性能高一些,是性能与安全性的折中,发生系统奔溃时最多会丢失1s的数据。
appendsync=no,每次修改都将aof buffer内容写到aof文件,同步动作的进行时机交给操作系统来决定,redis不管,这种安全性最低,但性能最好。操作系统系统一般考虑繁忙程度以及其它一些因素综合判断同步时机。
appendsync=no同步策略与binlog的binlog_sync=0的刷盘策略思路是一样的,都是将刷盘的时机判断脚背操作系统。
大前提:先读缓存,如果缓存没有,才从数据库读取。
该方案会导致双写不一致。
原因是,同时有一个请求A进行更新操作,另一个请求B进行查询操作。会出现如下情形:
(1)请求A进行写操作,删除缓存
(2)请求B查询发现缓存不存在
(3)请求B去数据库查询得到旧值
(4)请求B将旧值写入缓存
(5)请求A将新值写入数据库
上述情况就会导致不一致。而且,如果不给缓存设置过期时间,该数据永远都是脏数据。
那如何解决呢?采用延时双删策略
(1)先淘汰缓存
(2)再写数据库(这两步和原来一样)
(3)休眠1秒,再次淘汰缓存这么做,可以将1秒内所造成的缓存脏数据,再次删除。
那1秒怎么确定,具体该休眠多久呢?
读者应该评估自己的项目的读数据业务逻辑的耗时。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上,加几百ms即可。
这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。
如果你用了mysql的读写分离架构怎么办?
在这种情况下,造成数据不一致的原因如下,还是两个请求,一个请求A进行更新操作,另一个请求B进行查询操作。
(1)请求A进行写操作,删除缓存
(2)请求A将数据写入数据库了
(3)请求B查询缓存发现,缓存没有值
(4)请求B去从库查询,这时,还没有完成主从同步,因此查询到的是旧值
(5)请求B将旧值写入缓存
(6)数据库完成主从同步,从库变为新值上述情形,出现数据不一致。
解决:还是使用双删延时策略。
只是,睡眠时间修改为在主从同步的延时时间基础上,加几百ms。
采用这种同步淘汰策略,吞吐量降低怎么办?
ok,那就将第二次删除作为异步的。自己起一个线程,异步删除。这样,写的请求就不用沉睡一段时间后了,再返回。这么做,加大吞吐量。
第二次删除,如果删除失败怎么办?第二次删除失败,就会出现如下情形。有两个请求,一个请求A进行更新操作,另一个请求B进行查询操作,为了方便,假设是单库:
(1)请求A进行写操作,删除缓存
(2)请求B查询发现缓存不存在
(3)请求B去数据库查询得到旧值
(4)请求B将旧值写入缓存
(5)请求A将新值写入数据库
(6)请求A试图去删除,请求B写入对的缓存值,结果失败了。如果第二次删除缓存失败,会再次出现缓存和数据库不一致的问题。
解决:加上重试机制
这个存在并发问题?比如,这会儿有两个请求,一个请求A做查询操作,一个请求B做更新操作,那么会有如下情形产生
(1)缓存刚好失效
(2)请求A查询数据库,得一个旧值
(3)请求B将新值写入数据库
(4)请求B删除缓存
(5)请求A将查到的旧值写入缓存。
如果发生上述情况,确实是会发生脏数据。然而,发生这种情况的概率又有多少呢?
如何解决上述并发问题?
首先,给缓存设有效时间是一种方案。
其次,采用策略(2)里给出的异步延时删除策略,保证读请求完成以后,再进行删除操作。
还有其他造成不一致的情形吗?有的,如果删缓存失败了怎么办,失败了会有不一致的情况出现。
比如一个写数据请求,然后写入数据库了,删缓存失败了,这会就出现不一致的情况了。
如何解决?提供一个保障的重试机制即可,这里给出两套方案。
方案一:如下图所示
流程如下所示
(1)更新数据库数据;
(2)缓存因为种种问题删除失败
(3)将需要删除的key发送至消息队列
(4)自己消费消息,获得需要删除的key
(5)继续重试删除操作,直到成功。
该方案缺点,对业务线代码造成大量的侵入。
于是有了方案二,在方案二中,启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作。
流程如下图所示:
(1)更新数据库数据
(2)数据库会将操作信息写入binlog日志当中
(3)订阅程序提取出所需要的数据以及key
(4)另起一段非业务代码,获得该信息
(5)尝试删除缓存操作,发现删除失败
(6)将这些信息发送至消息队列
(7)重新从消息队列中获得该数据,重试操作。
备注:上述的订阅binlog程序在mysql中有现成的中间件叫canal,可以完成订阅binlog日志的功能。
至于oracle中,博主目前不知道有没有现成中间件可以使用。
另外,重试机制,博主是采用的是消息队列的方式。
如果对一致性要求不是很高,直接在程序中另起一个线程,每隔一段时间去重试即可,这些大家可以灵活自由发挥,只是提供一个思路。
(也叫 HEAP)堆内存:使用存在内存中的内容来创建表。每个 MEMORY 表只实际对应一个磁盘文件。 MEMORY 类型的表访问非常得快,因为它的数据是放在内存中的,并且默认使用HASH 索引。但是一旦服务关闭,表中的数据就会丢失掉。 Memory 同时支持散列索引和 B 树索引, B树索引可以使用部分查询和通配查询,也可以使用<,>和>=等操作符方便数据挖掘,散列索引相等的比较快但是对于范围的比较慢很多 。
MyIsam和InnoDB区别, mysql默认用哪个
主要面向OLTP(Online Transaction Processing,在线事务处理)方面的应用,是第一个完整支持ACID事务的存储引擎(BDB第一个支持事务的存储引擎,已经停止开发)。
特点:
1 支持行锁 2 支持外键
3 支持自动增加列AUTO_INCREMENT属性
4 支持事务
5 支持MVCC模式的读写(MVCC保证了隔离性)
6 读的效率低于MYISAM
7.写的效率高优于MYISAM
8.适合频繁修改以及设计到安全性较高的应用
9.清空整个表的时候,Innodb是一行一行的删除,
不存储总行数:
一个 InnoDb 引擎存储在一个文件空间(共享表空间,表大小不受操作系统控制,一个表可能分布在多个文件里),也有可能为多个(设置为独立表空,表大小受操作系统文件大小限制,一般为 2G),受操作系统文件大小的限制;
主键索引采用聚集索引(索引的数据域存储数据文件本身),辅索引的数据域存储主键的值;因此从辅索引查找数据,需要先通过辅索引找到主键值,再访问辅索引;最好使用自增主键,防止插入数据时,为维持 B+树结构,文件的大调整。
是MySQL官方提供的存储引擎,主要面向OLAP(Online Analytical Processing,在线分析处理)方面的应用。
特点:
1 独立于操作系统,当建立一个MyISAM存储引擎的表时,就会在本地磁盘建立三个文件,例如我建立tb_demo表,那么会生成以下三个文件tb_demo.frm,tb_demo.MYD,tb_demo.MYI
2 不支持事务,
3 支持表锁和全文索引
4 MyISAM存储引擎表由MYD和MYI组成,MYD用来存放数据文件,MYI用来存放索引文件。MySQL数据库只缓存其索引文件,数据文件的缓存交给操作系统本身来完成;
5 MySQL5.0版本开始,MyISAM默认支持256T的单表数据;
6.选择密集型的表:MYISAM存储引擎在筛选大量数据时非常迅速,这是他最突出的优点
7.读的效率优于InnoDB
8.写的效率低于InnoDB
9.适合查询以及插入为主的应用
10.清空整个表的时候,MYISAM则会新建表
存储表的总行数;
用非聚集索引,索引文件的数据域存储指向数据文件的指针。辅索引与主索引基本一致,但是辅索引不用保证唯一性。
SQL 标准定义了四个隔离级别:
READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。【Oracle默认】
REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生【MySQL默认】
SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读
使用左或者左右模糊匹配,也就是 like %xx 或者 like %xx%,会造成索引失效;
因为索引B+树是按照“索引值”有序排列存储的,只能根据前缀进行比较。当采用左或者左右模糊匹配的时候,比如“%明”的话,可能会查出“张明、李明、周明”这样的数据,所以不知道从哪个索引值开始比较,于是只能采用全表查询的方式。
因为索引保存的是索引字段的原始值,而不是经过函数计算后的值,自然没法走索引查询了。
MySQL 在遇到字符串和数字比较的时候,会自动把字符串转为数字,然后再进行比较。如果字符串是索引列,而条件语句中的输入参数是数字的话,那么索引列会发生隐式类型转换,由于隐式类型转换是通过 CAST 函数实现的,等同于对索引列使用了函数,所以就会导致索引失效。
也就是按照最左优先的方式进行索引的匹配,否则就会导致索引失效。
在 WHERE 子句中,如果在 OR 前的条件列是索引列,而在 OR 后的条件列不是索引列,那么索引会失效。
基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。
与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现 Serializable 序列化接口(可用来保存对象的状态),可在它的映射文件中配置 ;
当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。
————详解版————
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制,缓存可以极大的提升查询效率。MyBatis中默认定义了两级缓存,分别是一级缓存和二级缓存。
(1) 默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
(2)二级缓存需要手动开启和配置,二级缓存是基于namespace级别的缓存。
(3)为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存。
一、一级缓存
① 一级缓存(local cache), 即本地缓存, 作用域默认为sqlSession。当 Session flush 或 close 后, 该Session 中的所有Cache 将被清空。
② 本地缓存不能被关闭, 但可以调用clearCache()来清空本地缓存, 或者改变缓存的作用域。
③ 在mybatis3.1之后,可以配置本地缓存的作用域,在 mybatis.xml 中配置。
一级缓存失效的四种情况
① 不同的SqlSession对应不同的一级缓存
② 同一个SqlSession但是查询条件不同
③ 同一个SqlSession的两次查询期间执行了增删改操作
④ 同一个SqlSession的两次查询期间手动清空了缓存
二、二级缓存
二级缓存介绍
① 二级缓存(second level cache),是全局作用域缓存。
② 二级缓存默认不开启,需要手动配置。
③ MyBatis提供二级缓存的接口以及实现,实现二级缓存要求被查询的JavaBean实现Serializable接口。
④ 二级缓存在SqlSession 关闭或提交之后才会生效。
二级缓存使用步骤
① 在mybatis全局配置文件中开启二级缓存
② 在需要使用二级缓存的mapper映射文件中使用标签配置缓存。
③ 被查询的JavaBean需要实现Serializable接口。
三、缓存相关属性
eviction
缓存回收策略:
(1) LRU – 最近最少使用的:移除最长时间不被使用的对象。eviction的默认值 是 LRU。
(2) FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
(3) SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
(4) WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
flushInterval
刷新间隔,取值单位毫秒:
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size
引用数目,取值正整数:
代表缓存最多可以存储多少个对象,太大容易导致内存溢出。
readOnly
只读,取值true/false
(1)true:只读缓存; 会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
(2)false:读写缓存; 会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。
四、缓存相关设置
① 全局setting的 cacheEnable :
– 配置二级缓存的开关。一级缓存一直是打开的。
② select标签的 useCache 属性:
– 配置这个select是否使用二级缓存,一级缓存一直是使用的。
③ sql标签的 flushCache 属性:
– 增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。
默认值为flushCache=false。
④ sqlSession. clearCache ():
– 只是用来清除一级缓存。
⑤ 当在某一个作用域 (一级缓存Session/二级缓存Namespaces) 进行了增删改操作后,默认该作用域下所有 select 中的缓存将被clear 。