Innodb 聚集索引会存放两份数据吗?
- 我们知道,Innodb引擎下的分为聚集索引和非聚集索引,非聚集索引的叶子节点数据存放的是主键值;主键索引是用主键建立的索引,叶子节点存放的是整行数据。
那么,是不是每行数据都被存了两遍呢(数据本身,一级索引再存一遍)?
当然不是,一级索引是建立再数据本身的
在 Innodb 引擎下,如果建表时没有指定一个主键,mysql 会使用表中整型唯一索引做主键,如果还是没有,则mysql默认创建一个隐藏主键列(自增)。
- Redis 单线程保证了节点在高并发创建时的互斥 set key value 1 px nx
- ETCD Revision 机制 revision值依次从小到大获取锁,公平锁
- Zookeeper 容器 顺序节点 处理指令为单线程 保证互斥
- Redis key 过期机制
- ETCD 租约机制
- Zookeeper 临时节点
- Redis 主从哨兵、cluster 性能(内存操作)
- ETCD 集群 内存操作 性能(内存操作)
- Zookeeper 主从(Leader - follower -) 性能(内存操作)
- Redis key ()
- etcd key
- zookeeper key
Redis redlock 锁多个实例,同时向大多数 Redis 实例申请成功之后才算 锁成功
Zookeeper ZAP 保证 写一致性(写入大部分节点成功,才算写入成功),leader 节点出现故障,重新选举会选择最新数据的follower节点作主 (写节点永远都是一个节点)
ETCD 采用 raft 算法 写一致性
Redis watch dog (底层大致原理异步线程,续约过期时间)
Zookeeper 临时节点,会话断开临时节点便会删除,不用自定义过期时间,但是也存在问题,Zookeeper 服务器维护一个Session 依赖客户端发送定时心跳来维持连接,客服端由于网络延迟,GC 等情况定时心跳没发送过来,Zookeeper 也会认为 Session 过期,删除该Session,另外一个客户端就能获取到锁
ETCD 的租约机制 保证了安全性,线程异常中断,没有解锁,租约到期自动删除解锁 (watch dog 续约)
客服端由于网络延迟,GC 等情况 Watch dog没续约,Zookeeper 客服端没有发送心跳
解决方案:要存在一个兜底的策略,分布式锁不是绝对安全。
Redis,notify ()
Zookeeper 创建顺序节点,从小到大依次排列,利用 Zookeeper waitch 机制,后一个节点只监听前一个节点的状态变化,避免了羊群效应
ETCD watch 机制,客户端获取锁的列表 /lock/mylock 读取 Key 的值列表(列表中带有 Key 对应的 Revision),判断自己 Key 的 Revision 是否是当前节点最小的,如果是则获取到锁,否则监听列表中前一个比自己小的 Key 的删除事件,一旦监听到删除事件(主动删除或者租约到期删除)则自己获取到锁。
Bean 对象调用自己的方法,会导致自己的方法事务失效
多线程导致事务失效,这样会导致两个方法不在同一个线程中,获取到的数据库连接不一样,从而是两个不同的事务。如果想doOtherThing方法中抛了异常,add方法也回滚是不可能的
Spring 声明式事务依赖的是 动态代理对象,在动态代理对象中this.method 调用的不是代理方法而是调用对象本身的方法,导致没有添加事务方法 ,形成了事务失效
为什么在我们日常开发中事务失效了,但是没有暴露出线上问题呢?
propagation_required
,客户端与调用端存在同一个事务中,如果被调用端发生异常,那么调用端和被调用端事务都将回滚
1 |
|
Spring嵌套事务依赖Spring实现事务挂起功能(suspend()
)底层实现:(事务的执行过程,无论是嵌套还是其他的事务传播行为,都是同一个线程因此需要事务的挂起)
其实就是将线程变量里面的事务信息拿出来,再置空。 待事务提交或回滚后再放回线程变量中
TransactionSynchronizationManager 中的事务信息拿出来,存放在 SuspendedResourceHolder,等嵌套事务执行完,在将其放回 TransactionSynchronizationManager
利用 JDBC Savepoint,JDBC Savepoint帮我们在事务中创建检查点(checkpoint),这样就可以回滚到指定点
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
从IO模型上分为同步模型和异步模型
CPU资源大量浪费在阻塞等待上,导致CPU资源利用率低。在Java 8之前,一般会通过回调的方式来减少阻塞,但是大量使用回调,又引发臭名昭著的回调地狱问题,导致代码可读性和可维护性大大降低。
为了增加并发度,会引入更多额外的线程池,随着CPU调度线程数的增加,会导致更严重的资源争用,宝贵的CPU资源被损耗在上下文切换上,而且线程本身也会占用系统资源,且不能无限增加。
使用 CompletableFuture 相较于 Future、RxJava、Reactor 具有 学习成本低、满足我们异步、可组合的需求
假设有三个操作step1、step2、step3存在依赖关系,其中step3的执行依赖step1和step2的结果。
1 |
|
类似“观察者模式”的设计思想
每个 Completable 可以看做一个被观察者,其内部有一个 Completion 类型的链表成员变量 stack。
AQS 使用 FIFO 队列实现了一个锁相关的并发器模板,可以基于这个模板来实现各种锁,包括独占锁、共享锁、信号量等。
AQS 中,有一个核心状态是 waitStatus,这个代表节点的状态,决定了当前节点的后续操作,比如是否等待唤醒,是否要唤醒后继节点。
1 |
|
https://img2020.cnblogs.com/blog/1128804/202010/1128804-20201022161838828-1750298688.png
默认情况下,Namespace=public、Group=DEFAULT_GROUP,默认Cluster是DEFAULT。
Nacos的默认的命名空间是public,Namespace主要用来实现隔离。比如说,现在有三个环境:开发、测试和生产环境,我们就可以创建三个Namespace,不同的Namespace之间是隔离的。
Group默认是DEFAULT_GROUP,Group可以把不同的微服务划分到同一个分组里面。
Service就是微服务。一个Service可以包含多个Cluster(集群),Nacos默认Cluster就是DEFAULT,Cluster是对指定微服务的一个虚拟划分。比如说,为了容灾,将Service微服务分别部署在了杭州机房和广州机房,这时候就可以给杭州机房的Service微服务起一个集群名称(HZ),给广州机房的Service微服务起一个集群名称(GZ),还可以尽量让同一个机房的微服务互相调用,以提升性能。
Instance,就是微服务的实例。
在表数据量大的情况下,直接对表进行新增修改字段操作会导致锁表,这个过程可能需要很长时间甚至导致服务崩溃。因此需要另外一种方法
一般情况下,十几万的数据量,可以直接进行加字段操作。
将当前数据库表导出成文件
基于要操作的表创建一个临时表,执行要修改的操作,比如add column或者drop column
将文件导入新的临时表
注意不要用intsert into table_copy select * from table,
导成文件
1
2
3
4
5
select * from cms_gift_code into outfile '/usr/local/mysql/data/cms_gift_code.txt' fields terminated by ',' line terminated by '
';
将表中数据导入到新的临时表
对新表进行修改新增字段操作 DDL
将在这期间旧表产生的数据 复制到 新表。(ID、时间)
将新表重命名, 旧表重命名为新表
1 |
|
注:导出 导入需要时间、因此步骤3的操作导入的数据可能不完整,这时需要通过一些机制来比对新老数据 (ID、时间)。找到差异数据、再导入新的临时表
不过还是会可能损失极少量的数据。
所以,如果表的数据特别大,同时又要保证数据完整,最好停机操作。
在从库进行加字段操作,然后主从切换
使用第三方在线改字段的工具