#和$的区别
#是参数占位符,会做sql预编译,变量替换后会加上单引号,可以防止sql注入。
$是替换符号,不会预编译,变量替换后不会加上单引号,只是简单的文本替换,不能防止sql注入。
Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
第一种是使用标签(resultMap),逐一定义数据库列名和对象属性名之间的映射关系。
第二种是使用sql列的别名功能(resultType),将列的别名书写为对象属性名。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。
延迟加载的基本原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。
Mybatis是如何进行分页的?分页插件的原理是什么?
Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页。可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
通常一个mapper.xml文件,都会对应一个Dao接口,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
Mapper 接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象proxy,代理对象会拦截接口方法,根据类的全限定名+方法名,唯一定位到一个MapperStatement并调用执行器执行所代表的sql,然后将sql执行结果返回。
Mapper接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。
如何配置数据库连接池的
如何保障mysql和redis之间的数据一致性
在更新缓存方面,是先更新再更新缓存还是直接删除缓存呢?又或者是先删除缓存再删除数据库?
延时双删策略(适用于写不频繁):1.先删除缓存 2.再写入数据库 3.休眠业务耗时的毫秒数 4.再次删除缓存 (备注:要设置缓存的超时时间)
引入MQ(重试时间内缓存不会更新):修改数据的请求发送给MQ,MQ有两个消费者,一个操作Redis,一个操作MySQL,如果删除缓存失败了,可以通过MQ的重试机制再次删除缓存。
定时器:将热点缓存设置为永不过期,但是在value中写入一个逻辑上的过期时间,另外启一个后台线程,扫描这些key,对于已经逻辑上过期的缓存,进行删除。
Redis分布式锁的原理
分布式锁使用setnx、getset、expire、del这四个redis指令实现
过程:
1.当A通过setnx(lockkey,currenttime+timeout)命令能成功设置lockkey时,即返回值为1,过程与原理1一致;
2.当A通过setnx(lockkey,currenttime+timeout)命令不能成功设置lockkey时,这是不能直接断定获取锁失败;因为我们在设置锁时,设置了锁的超时时间timeout,当当前时间大于redis中存储键值为lockkey的value值时,可以认为上一任的拥有者对锁的使用权已经失效了,A就可以强行拥有该锁;具体判定过程如下;
3.A通过get(lockkey),获取redis中的存储键值为lockkey的value值,即获取锁的相对时间lockvalueA
lockvalueA!=null && currenttime>lockvalue,A通过当前的时间与锁设置的时间做比较,如果当前时间已经大于锁设置的时间临界,即可以进一步判断是否可以获取锁,否则说明该锁还在被占用,A就还不能获取该锁,结束,获取锁失败;
4.步骤4返回结果为true后,通过getSet设置新的超时时间,并返回旧值lockvalueB,以作判断,因为在分布式环境,在进入这里时可能另外的进程获取到锁并对值进行了修改,只有旧值与返回的值一致才能说明中间未被其他进程获取到这个锁
lockvalueB == null || lockvalueA==lockvalueB,判断:若果lockvalueB为null,说明该锁已经被释放了,此时该进程可以获取锁;旧值与返回的lockvalueB一致说明中间未被其他进程获取该锁,可以获取锁;否则不能获取锁,结束,获取锁失败。
实现原理:
1.通过setnx(lock_timeout)实现,如果设置了锁返回1, 已经有值没有设置成功返回0
2.死锁问题:通过时间来判断是否过期,如果已经过期,获取到过期时间get(lockKey),然后getset(lock_timeout)判断是否和get相同,相同则证明已经加锁成功,因为可能导致多线程同时执行getset(lock_timeout)方法,这可能导致多线程都只需getset后,对于判断加锁成功的线程, 再加expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS)过期时间,防止多个线程同时叠加时间,导致锁时效时间翻倍。
3.过期时间评估不好,锁提前过期:加时锁,先设置一个过期时间,然后开启一个守护线程,定时去检测这个锁的失效时间,如果锁快要过期了,操作共享资源还未完成,那么就对锁自动进行续期,重新设置过期时间。(Redisson是一个Redis SDK客户端,在使用分布式锁的时候就采用了自动续期的方案来避免锁过期,这个守护线程一般也叫做看门狗线程。
4.锁被别人释放:锁写入唯一标识,释放锁先检查标识,再释放。
Redis的持久化方式
redis有两种持久化方式。可以单独使用也可以结合使用。RDB就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上;AOF,是将redis执行过的所有写指令记录下来,在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。
这两种方式也可以同时使用,但这样Redis重启后会优先采用AOF方式恢复数据,因为完整度更高。
RDB会先把数据写入一个临时文件中,持久化过程结束后才会用这个临时文件替代之前的文件。
对于RDB方式,redis会创建一个子进程来持久化,如果已经有子进程在持久化就返回错误。
Redis的主从机制
Redis的淘汰策略
Redis的事务