面试题第三期
1.为什么选用redis代替session
-
在分布式环境下,用户的请求可能会被负载均衡到其他的服务器,从而导致无法获取之前的session,要在多台服务器上共享session实现数据同步,会增加系统的复杂性,此时考虑使用redis会更好
-
在项目中我采用了hash的类型来存储token,此时token作为外部键的一部分,用户信息作为值,设置了过期时间为30分中。同时利用拦截器对过期时间进行续期。
-
redis的读取速率要高于session适合高频访问的业务场景,而我的项目中有优惠劵的秒杀业务就很适合。
2.Transactional失效的场景
-
Transactional只适用于public方法
-
自调用,没有通过spring代理绕过了事务拦截器,解决方法有通过依赖注入自身来解决,或者获取代理对象
-
抛出除了 RuntimeException 和 Error的其他异常,此时可以通过给transactional注解配置异常类型来解决
-
未启用事务注解或者数据库存在多数据源
-
在内部捕获了异常却没有抛出
3.你知道redisson的那几种锁
-
可重入锁,同一个线程可以多次获取同一把锁,不会造成自己把自己锁到外面的情形
-
红锁,同时向多个redis节点申请锁,只有超过半数的锁被释放才会真正的释放,可以利用redis集群来实现
-
联锁,红锁的低配版,只有所有的锁被释放才算真正的释放
-
公平锁,先到的线程会先拿到锁
4.redisson的看门狗
你可以把它看成一个锁的保镖,假如现在你有一个业务需要执行40秒,锁默认30秒的过期时间,此时看门狗每隔过期时间的1/3时间会检查一次,如果锁还被持有就会续期,直到释放锁。要想看门狗生效不能设置固定的过期时间,当锁释放或者线程结束时看门狗也会自动生效。
5.cookie,session,token的区别
Cookie 是存储在用户浏览器端的一个小型数据文件,用于跟踪和保存用户的状态信息。
主要用于保持用户登录状态、跟踪用户行为、存储用户偏好等。
存储在浏览器端。
Session:
Session 是服务器端保存用户状态的机制,每个用户会话都有一个唯一的 Session ID。
主要用于跟踪用户在服务器上的状态信息,例如登录状态和购物车内容。
存储在服务器端,然后对应的 Session ID 通过 Cookie 保存在客户端浏览器中。
Token:
Token 本质是一种加密的字符串,用于身份验证和授权,可以包含用户信息和权限,用于验证用户身份或授权访问资源。
认证后,后端服务会返回 Token,存储在客户端(浏览器或移动应用中),后续客户端访问服务端需要带上这个 Token。
它们之间使用场景区别:
- Cookie:主要用于客户端状态的简单存储和追踪。
- Session:用于服务器端的复杂状态管理,特别是在需要存储大量会话数据时。
- Token:用于无状态的认证和授权,特别是在分布式和跨域环境下。
简单来说,Cookie 和Session 更适合用于单次会话的认证和状态管理,而 Token 更适合用于跨会话的认证和状态管理。
6.跳表
现在有一个十分长的链表,你想要查询某个数据,你需要遍历来查找它,此时时间花费会很大,聪明的你想到了如果链表已经有序了,我们在众多节点里边挑选几个作为类似索引的东西,每当你查询时我们比对一下就可以缩小区间来加快速度。此时聪明的你又又又想到了,如果我查找的数据特别靠后,抽取的节点还是太多怎么办,这时候我们将原来抽取的节点再进行一次抽取,形成一个更广泛的索引便可迅速定位到对应区间。
7.为什么 Redis Zset 用跳表实现而不是红黑树?B+树?
1)相比红黑树而言实现简单
跳表基于多层链表实现,通过概率算法动态生成索引层级,没有左旋右旋等操作,逻辑理解上更为简单。而红黑树需要复杂的平衡操作(旋转)来维护结构,代码实现复杂度较高,理解门槛更高。
2)范围查询更高效
范围查询跳表可以通过 O(logn) 的时间复杂度定位起点,然后在原始的链表中往后遍历即可。
红黑树从结构上不支持范围查询。
3)结构更灵活
跳表的层数和节点结构是动态的,可以基于概率分布调整层数,灵活的适应不同的数据量(数据量大层级可以多一些,小的话层级少一些)。
红黑树则无法调整。
8.mvcc
MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种并发控制机制,允许多个事务同时读取和写入数据库,而无需互相等待,从而提高数据库的并发性能。
在 MVCC 中,数据库为每个事务创建一个数据快照。每当数据被修改时,MySQL 不会立即覆盖原有数据,而是生成新版本的记录。每个记录都保留了对应的版本号或时间戳。
多版本之间串联起来就形成了一条版本链,这样不同时刻启动的事务可以无锁地获得不同版本的数据(普通读)。此时读(普通读)写操作不会阻塞。
写操作可以继续写,无非就是会创建新的数据版本(但只有在事务提交后,新版本才会对其他事务可见。未提交的事务修改不会影响其他事务的读取),历史版本记录可供已经启动的事务读取。