事务
事务特性
原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节,而且事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样,就好比买一件商品,购买成功时,则给商家付了钱,商品到手;购买失败时,则商品在商家手中,消费者的钱也没花出去。
一致性(Consistency):是指事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。比如,用户 A 和用户 B 在银行分别有 800 元和 600 元,总共 1400 元,用户 A 给用户 B 转账 200 元,分为两个步骤,从 A 的账户扣除 200 元和对 B 的账户增加 200 元。一致性就是要求上述步骤操作后,最后的结果是用户 A 还有 600 元,用户 B 有 800 元,总共 1400 元,而不会出现用户 A 扣除了 200 元,但用户 B 未增加的情况(该情况,用户 A 和 B 均为 600 元,总共 1200 元)。
隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执 ...
缓存更新策略
前提由于CAP理论,我们无法保证强一致性,在实际业务中,我们选择最终一致性。
Cache Aside 旁路策略写策略
更新数据库
删除缓存
读策略
缓存未命中
读取数据库的数据
回写缓存
解决方案
在更新缓存时增加分布式锁
给缓存增加过期时间·
高可用
主从复制所有的数据修改只在主服务器上进行,然后将最新的数据同步给从服务器,这样就使得主从服务器的数据是一致的。
哨兵模式使用主从服务时,当redis的主从服务器出现故障宕机时,需要手动进行恢复。为了解决这个问题,Redis 增加了哨兵模式(Redis Sentinel),因为哨兵模式做到了可以监控主从服务器,并且提供主从节点故障转移的功能。
脑裂由于网络问题,集群节点之间失去联系。主从数据不同步;重新平衡选举,产生两个主服务。等网络恢复,旧主节点会降级为从节点,再与新主节点进行同步复制的时候,由于会从节点会清空自己的缓冲区,所以导致之前客户端写入的数据丢失了。解决方案:当主节点发现从节点下线或者通信超时的总数量小于阈值时,那么禁止主节点进行写数据,直接把错误返回给客户端。
持久化
AOFRedis 每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里,然后重启 Redis 的时候,先去读取这个文件里的命令,并且执行它。特点:
先执行写操作命令,再将命令记录到AOF日志中。
写回策略
写回策略
写回时机
优点
缺点
Always
同步写回
可靠性高、最大程度保证数据不丢失
每个写命令都要写回硬盘,性能开销大
Everysec
每秒写回
性能适中
宕机时丢失1秒内的数据
No
由操作系统控制写回
性能好
宕机时丢失的数据可能会很多
AOF过大时的策略当 AOF 文件的大小超过所设定的阈值后,Redis 就会启用 AOF 重写机制,来压缩 AOF 文件。
只记录最新的键值对
RDB快照机制,把内存中所有数据记录到磁盘中,
执行bgsave时通过fork()创建子进程进行,同时子进程复制父进程的页表,所以读是不会阻塞子进程的。针对写操作,被修改的数据会复制一份副本,然后bgsave子进程会把该副本数据写入RDB文件,在这个过程中,主线程仍然可以直接修改原来的数据。
两者对比
AOF记录指令,RDB记录数据
AOF优点是丢失数据少,但 ...
缓存失效
缓存雪崩当大量缓存数据在同一时间过期(失效)或者 Redis 故障宕机时,如果此时有大量的用户请求,都无法在 Redis 中处理,于是全部请求都直接访问数据库,从而导致数据库的压力骤增。
解决方案:
均匀设置过期时间:将过期时间加一个随机数
互斥锁:如果发现访问的数据不在Redis里,就加个互斥锁,保证统一时间内只有一个请求来构建缓存
缓存预热若Redis故障宕机,我们可以启动服务熔断机制,暂停业务应用对缓存服务的访问,直接返回错误,不再继续访问数据库,从而降低对数据库的访问压力。
缓存击穿如果缓存中的某个热点数据过期了,此时大量的请求访问了该热点数据,就无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮。
解决方案:
互斥锁
不给热点数据设置过期时间
缓存穿透当用户访问的数据,既不在缓存中,也不在数据库中,导致请求在访问缓存时,发现缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据,没办法构建缓存数据,来服务后续的请求。那么当有大量这样的请求到来时,数据库的压力骤增。
解决方案:
非法请求的限制
将空结果(NULL)或默认查询结果存入到缓存中,并设置 ...
gin框架—中间件
应用场景中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计。
中间件的使用使用中间件必须是一个 func(c *gin.Context)类型的函数,底层实现了handeFunc方法
中间件分类全局中间件1、定义一个函数接收一个*gin.Context类型的参数,和handler的写法是一样的:
123456func MyMiddleware(c *gin.Context){ }router = gin.Default()router.Use(MyMiddleware)
2、定义一个返回值为HandlerFunc类型的函数
1234567func MyMiddleware(){ return func(c *gin.Context){ }}router = gin.Default()router.Use(MyMiddleware())
路由组中间件12345... server := gin.Default() ... server.Group("/app/v1").Use(Middleware1())...
路由中间件1ro ...
关键字-type
功能一:类型别名12345type FT func(int, string) (string, error)func Foo(a FT) string { return ""}
实现接口123type Collection interface { Push(ele int)}
拥有自己的方法12345type Stack []intfunc (s *Stack) Push(ele int) { //实现了Collection接口的方法 *s = append(*s, ele)}
责任链模式
什么是责任链责任链模式是一种行为设计模式,允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。
责任链设计模式的基本组成
抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
概念示例让我们来看看一个医院应用的责任链模式例子。 医院中会有多个部门, 如:
前台
医生
药房
收银
病人来访时, 他们首先都会去前台, 然后是看医生、 取药, 最后结账。 也就是说, 病人需要通过一条部门链,每个部门都在完成其职能后将病人进一步沿着链条输送。
此模式适用于有多个候选选项处理相同请求的情形,适用于不希望客户端选择接收者(因为多个对象都可处理请求) 的情形,还适用于想将客户端同接收者解耦时。客户端只需要链中的首个元素即可。
参考文章https ...
go-goroutine
使用协程goroutine在Go语言中属于轻量级的线程,我们可以通过命名函数和匿名函数来启动一个新的goroutine。
命名函数123456789101112131415161718192021package testimport ( "fmt" "testing")func setVto1(v *int) { *v = 1}func setVto2(v *int) { *v = 2}func Test_Goroutine(t *testing.T) { v := new(int) go setVto1(v) go setVto2(v) fmt.Println(*v)}
在以上代码中的结果可能有多种情况,最后的”*v”可能是0、1或者2。
匿名函数123456789101112131415package testimport ( "fmt" "testing" "time")func Test_Goroutine(t *testing.T) { go func(name string) { fmt.Println(name) }("a") time.Sleep(time.Seco ...
go-GMP模型
前言Go语言中的GMP线程模型对两级线程模型进行了一定程度的改进,它由主要三个模块构成:
模块
说明
Goroutine
轻量级的用户线程,并行的最大数量等于P的数量
Machine
一个Machine对应一个内核线程,相当于内核线程在Go语言进程中的映射,且运行过程中M和内核线程之间对应关系不会改变
Processor
承上启下的一个调度器
如下图所示:
参考小徐先生的编程世界-Golang GMP原理