1 mysql如何实现事务的 acid,mysql事务的默认隔离级别,如何解决幻读
事务是一组 sql 语句,是一个执行单位。
事务的ACID特性
原子性(Atomicity):单个事务的sql要么全部执行成功,要么全部执行失败。通过 undo log 日志来实现。在事务开始之前,把要修改的记录存到 undo日志里,当事务回滚或数据库崩溃时,可以利用 undo 日志,撤销事务对数据库产生的影响。
持久性(Dutability):一个事务一旦提交,它对数据库的改变是永久的。通过 redo log 日志来实现。事务中修改的任何数据,都会将最新的值备份存储到 Redo Log,在系统 carsh 重启后可通过 redo log 来恢复数据。
隔离性(Isolation):一个事务的执行不能被其他事务干扰,即一个事务内部的操作即使用的数据对其他并发的事务是隔离的。主要通过锁和多版本控制技术(MVCC)来保证。
一致性(Consisitency):一致性是一种约束,是一种目的,在事务开始之前和事务结束之后,数据库的完整性不会被破坏,包括约束一致性和数据一致性。它是原子性,持久性,隔离性共同保证的结果。
事务隔离级别
隔离级别从低到高分别为:读未提交,读已提交,可重复读,串行化。
MySQL 默认的隔离级别是可重复读,公司的MySQL也是用的默认设置。
可重复读怎么实现的
可重复读主要是通过事务的一致性视图 和 数据的多版本控制技术(MVCC)来实现的。
在可重复读隔离级别下,事务在启动时就会创建一个视图,类似于拍了个照,记录该事务的事务id,数值,整个事务存在期间都用这个视图。
数据的多版本控制技术(MVCC)指每行数据都可以有多个版本,每次事务修改数据后都会生成一个新的数据版本,数据版本里也会记录事务的id,数值,同时会保留旧的数据版本,生成一条回滚日志。
事务查询数据时,都要根据最新的数据版本的当前值,依次执行图中所有的回滚操作得到。
什么是幻读
一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。在可重复读隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的。因此,幻读在“当前读”下才会出现。
幻读有什么问题
给所有的记录都加上锁,也阻止不了新插入的记录,无法保证数据一致性,同时数据和日志在逻辑上也会出现不一致。
如何解决幻读
产生幻读的原因是,行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的“间隙”。因此,为了解决幻读问题,InnoDB 只好引入新的锁,也就是间隙锁 (Gap Lock)。
间隙锁的引入,可能会导致同样的语句锁住更大的范围,引发死锁,影响并发度。
有没有更简单一点的处理方法呢?
间隙锁是在可重复读隔离级别下才会生效的。所以,如果把隔离级别设置为读提交的话,就没有间隙锁了。但同时要解决可能出现的数据和日志不一致问题,需要把 binlog 格式设置为row。
binlog有两种常用的格式,一种是statement(默认),一种是row。
statement格式记录的是我们执行的sql,而row格式记录的则是实际受影响的数据的变化前后值。
3 如何优化子查询
4 mybatis的dao接口的工作原理是什么?
Dao 接口即 Mapper 接口,接口的全限定名对应于映射文件中 namespace 的值,接口的方法名对应于映射文件中每条 sql 的 id 值;接口方法内的参数,就是传递给 sql 的参数值。
采用 Mapper 代理开发方式时,Mapper 接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个 MapperStatement。在 Mybatis 中,每一个 <select>、<insert>、<update>、<delete>标签,都会被解析为一个 MapperStatement 对象。
每个Mybatis 运行时会使用 JDK 动态代理为 Mapper接口生成代理对象,代理对象去调用执行方法,ResultHandler 将结果集进行转换,返回最终的结果。
5 kafka 如何实现顺序消费
每条发布到 kafka 集群的消息都有一个主题,物理上不同主题的消息是分开存储的。
主题下还会分成多个分区,消息以追加的方式写入分区,然后以先入先出的顺序读取,且每条消息只会存在某一个分区中。
如果需要严格保证消息的消费顺序,需要将分区数目设为 1,但是这样做就丢失了 kafka 多分区带来的高吞吐量和负载均衡的优势。
还有一种方式
发消息的时候,在消息体里根据不同的业务封装不同的标记位,并针对标记位设定专门的分区策略,保证同一标记位的所有消息都发送到同一分区。这样既可以保证分区内的消息顺序,也可以享受到多分区带来的性能优势。