分库分表的实现方式

分库分表 数据库的数据量增大会直接影响读写的性能,比如一次查询操作,扫描 5 万条数据和 500 万条数据,查询速度肯定是不同的。分库分表就是把一个表拆分成多个库的多个表,这些表可以在同一个服务器上,也可以在不同的服务器上,总而言之,就是要通过拆分,减小单库单表的读写压力。 那么什么时候才需要分库分表呢? 参考阿里巴巴的《Java 开发手册》中数据库部分的建表规约: **单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。**由于不同的服务器配置、数据的存储结构都不同,所以这个数据并不确切,但可以作为一个参考。

继续阅读

使用Kubebuilder编写Operator

CRD和自定义控制器 在k8s的Informer机制这篇文章中我们了解了K8s的Informer机制,以及对于k8s原生资源的Informer对象的使用方式。Informer机制归根到底就是利用了kube-apiserver的ListAndWatch机制,去监听集群中特定的API对象的变更,进而去实现一些控制。而且k8s的API对象是支持拓展的,允许用户在 Kubernetes 中添加一个跟 Pod、Node 类似的、新的 API 资源类型,然后编写一个针对自定义API对象的控制器,通过Informer机制去监听这个新的API对象在集群中的变更,然后实现一些控制。这中自定义的API对象就是CRD(Custom Resource Definition)。

继续阅读

Golang的channel底层实现

Channel的底层数据结构 channel是golang在语言层面提供的goroutine通信机制,也是我们经常使用的数据结构,它的数据结构如下: // 在src/runtime/chan.go中定义 type hchan struct{ // 环形队列相关 qcount uint // 当前队列中剩余元素的个数 dataqsiz uint // 环形队列的长度,即可以存放的元素个数 buf unsafe.

继续阅读

分布式锁的几种实现方式

分布式锁 单机锁 就是本地应用的互斥锁,或者读写锁,用来锁住某个资源,先获取锁才能操作资源,为了并发安全,多个线程操作同一个资源时不会出现意外的情况,保证同一个时刻只有一个线程可以操作这个资源。 分布式锁 就是同样是某个资源,但是会有分布式的应用同时访问,同样需要保证任意时刻只有一个应用能访问这个资源,就需要使用分布式锁。 简单理解,单机锁用于进程内的多个线程争抢同一个资源,分布式锁就是系统内多个进程争抢同一个资源。 比如电商网站的秒杀,特价之类的活动,高并发下会出现成千上万人抢购一个商品的场景。系统设计时会通过限流、异步、排队等方式优化,但整体的并发还是平时的数倍以上,参加活动的商品一般都是限量库存,分布式锁就是一个防止库存超卖,避免并发问题的解决方案。 分布式锁的一般特性 互斥性:一个应用获取了锁,那么另一个应用就不能获取。这是最基本的。 可重入性:一个线程获取了这个锁之后,在持有锁的时间,依然可以再次获取到这个锁,只需要计数加1,同时释放锁时如果是已经重入的锁,只需要给计数减一即可,不用删除,该程序依然保有这个锁。 锁超时:为了防止一个线程给资源加锁之后挂掉,导致不能释放锁,其他应用迟迟不能获取这个锁,就需要给锁设置超时时间,让他自动解锁。 高效:加锁、解锁的操作要高效,这里不能成为性能瓶颈。 高可用:分布式锁要多个副本,防止单点故障后,整个分布式锁就不能用。 支持阻塞和非阻塞式的使用:当一个锁被其他线程持有时,这个线程获取锁的操作会阻塞住,直到可以获取锁,这就是阻塞。非阻塞就是获取不到锁就立刻返回,需要定时获取,或者轮询的方式直到拿到锁,直到一定时间之后,可以抛出拿不到锁的异常。 公平锁和非公平锁:公平锁就是,每个想要获取锁的线程排队,等到锁释放了,就按照先后顺序依次获得锁。非公平锁就是每次锁释放都是所以线程抢夺锁,谁先抢到是谁的,这种就可能出现某个线程长时间都拿不到锁的情况。 MySQL实现分布式锁 如果项目中本来就用了MySQL,其实就可以利用MySQL做一个分布式锁。虽然很少会使用用这种方案,但是理论上也是可行的。

继续阅读

分布式事务的实现方式

分布式基础理论 CAP CAP 理论可以表述为,一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition Tolerance)这三项中的两项。 C(Consistency):一致性,所有节点同时看到相同的数据,即每次写入,都是要保证所有节点都写入成功。 A(Availability):可用性,任何时候,读写都是成功的,即服务总是可用的,比如稳定性可以做到三个9,四个9等,这就是对可用性的描述。 P(Partition Tolerance):分区容错性,当部分节点故障或者网络错误时,系统仍然可用。 CAP理论用反证法很容易证明,如果CAP都满足,有分区容错P,就一定不能满足一致性C。

继续阅读

Redis数据类型、操作指令和底层实现原理

字符串 SDS 字符串在 Redis 中的实现叫做SDS(Symple Dynamic Strings),简单动态字符串。redis中所有场景中出现的字符串,基本都是由SDS来实现的,包括: 所有非数字的key。例如set msg "hello world" 中的"msg"这个key; 字符串数据类型的值。例如set msg "hello world"中的msg的值"hello wolrd"; 非字符串数据类型中的“字符串值”。例如RPUSH fruits "apple" "banana" "cherry"中的”apple” “banana” “cherry”。 SDS的数据结构如下(3.

继续阅读

Golang的指针和内存对齐

指针 golang中的指针种类 golang的指针类型分为三种,*类型,unsafe.Pointer,uintptr。 *类型:普通指针类型,用于传递对象地址,不能进行指针运算。 unsafe.Pointer:通用指针类型,用于转换不同类型的指针,不能进行指针运算,不能读取内存存储的值,必须要转换到某一类型的普通指针。 uintptr:用于指针运算,GC不把uintptr当指针,uintptr无法持有对象,uintptr类型的目标会被回收。 通过以上就可以看出: unsafe.

继续阅读

Golang中的defer、select和range的底层原理

Defer defer语句用于延迟函数的调用,每次defer都会把一个函数压入栈中,当前函数返回前,再把延迟的函数取出来并执行,所以多个defer语句是后写的先执行的。 几个题目 问题1:以下输出什么? func deferFuncParameter() { var aInt = 1 defer fmt.

继续阅读

Golang的map底层实现

Map的底层数据结构 Golang的map数据类型底层使用的是哈希表实现,一个哈希表可以有多个哈希表节点,即bucket,一个bucket保存着map中的一个或者一组键值对。 hmap的数据结构: map的实现实际上就是runtime包里的hmap: // 定义在src/runtime/map.go中 type hmap struct{ count int // 当前保存的元素个数 .

继续阅读

Golang实现单例模式

单例模式 单例模式是保证一个类,确保在一个系统中永远只有一个实例。在go中,就是保证一个结构体,永远只有一个实例化的对象。 当一个应用程序中某个对象需要是全局唯一的,就要使用单例模式。单例模式常用在以下场景中: 配置类 日志类 必须要以共享模式访问的资源类 需要频繁的创建和删除对象,而对象又可以复用的场景 单例模式不适用的场景:

继续阅读