Algorithm
142. 环形链表 II
使用快慢指针可以推导出相遇点到入环点的距离等于起始点到入环点的距离
func detectCycle(head *ListNode) *ListNode {
fast := head
slow := head
for fast != nil {
slow = slow.Next
if fast.Next == nil {
return nil
}
fast = fast.Next.Next
if slow == fast {
result := head
for {
if slow == result {
return result
}else{
slow = slow.Next
result = result.Next
}
}
}
}
return nil
}
Review
Organizing projects and defining names in Go
本文介绍了作者在他们项目实践中遵循的一些编码规范,包括:
- 如何组织一个library项目:
- 遵循单一职责规范
- 必要的时候声明好interface并在sub_package中进行相关implement
- 包名命名尽量简洁准确
- 如何组织一个application项目:
├── README.md
├── go.mod
├── go.sum
├── cmd
│ ├── api
│ │ └── main.go
│ └── appctl
│ └── main.go
├── config
│ ├── config.go
├── book
│ ├── book.go
│ ├── service.go
│ ├── service_test.go
│ ├── mock
│ │ ├── service.go
│ │ ├── repository.go
│ ├── postgres
│ │ ├── book_storage.go
│ │ ├── book_storage_test.go
├── internal
│ ├── http
│ ├── gin
│ ├── handler.go
| ├── book.go
│ ├── book_test.go
│ ├── event
│ │ ├── event.go
│ │ └── event_test.go
│ │ │── kafka
│ │ ├── event.go
│ │ ├── event_test.go
- 包、函数、变量命名:
- 包名:尽量短、清晰表达包职责(遵循单一职责)
- 变量:驼峰命名、简单清晰
- 函数:避免和包名重复表达
TIP
"本文使用的完整可运行源码
去公众号「网管叨bi叨」发送【设计模式】即可领取"
// 命令接收者,负责逻辑的执行
type CPU struct{}
func (CPU) ADoSomething(param int) {
fmt.Printf("a do something with param %v\n", param)
}
func (CPU) BDoSomething(param1 string, param2 int) {
fmt.Printf("b do something with params %v and %v \n", param1, param2)
}
func (CPU) CDoSomething() {
fmt.Println("c do something with no params")
}
// 接口中仅声明一个执行命令的方法 Execute()
type Command interface {
Execute()
}
// 命令对象持有一个指向接收者的引用,以及请求中的所有参数,
type ACommand struct {
cpu *CPU
param int
}
// 命令不会进行逻辑处理,调用Execute方法会将发送者的请求委派给接收者对象。
func (a ACommand) Execute() {
a.cpu.ADoSomething(a.param)
a.cpu.CDoSomething()// 可以执行多个接收者的操作完成命令宏
}
func NewACommand(cpu *CPU, param int) Command {
return ACommand{cpu, param}
}
"本文使用的完整可运行源码
去公众号「网管叨bi叨」发送【设计模式】即可领取"
type BCommand struct {
state bool // Command 里可以添加些状态用作逻辑判断
cpu *CPU
param1 string
param2 int
}
func (b BCommand) Execute() {
if b.state {
return
}
b.cpu.BDoSomething(b.param1, b.param2)
b.state = true
b.cpu.CDoSomething()
}
func NewBCommand(cpu *CPU, param1 string, param2 int) Command {
return BCommand{false,cpu, param1, param2}
}
type PS5 struct {
commands map[string]Command
}
// SetCommand方法来将 Command 指令设定给PS5。
func (p *PS5) SetCommand(name string, command Command) {
p.commands[name] = command
}
// DoCommand方法选择要执行的命令
func (p *PS5) DoCommand(name string) {
p.commands[name].Execute()
}
func main() {
cpu := CPU{}
// main方法充当客户端,创建并配置具体命令对象, 完成命令与执行操作的接收者的关联。
ps5 := PS5{make(map[string]Command)}
ps5.SetCommand("a", NewACommand(&cpu, 1))
ps5.SetCommand("b", NewBCommand(&cpu, "hello", 2))
ps5.DoCommand("a")
ps5.DoCommand("b")
}
Share
学习mysql 45讲
31 | 误删数据后除了跑路, 还能怎么办?
误删行
用Flashback工具通过闪回把数据恢复回来。Flashback恢复数据的原理, 是修改binlog的内容, 拿回原库重放。 而能够使用这个方案的前提是, 需要确保binlog_format=row和 binlog_row_image=FULL。
具体恢复数据时, 对单个事务做如下处理:
- 对于insert语句, 对应的binlog event类型是Write_rows event, 把它改成Delete_rows event即可;
- 同理, 对于delete语句, 也是将Delete_rows event改为Write_rows event;
- 而如果是Update_rows的话, binlog里面记录了数据行修改前和修改后的值, 对调这两行的位置即可。
误删库/表
这种情况下, 要想恢复数据, 就需要使用全量备份, 加增量日志的方式了。 这个方案要求线上有定期的全量备份, 并且实时备份binlog。
在这两个条件都具备的情况下, 假如有人中午12点误删了一个库, 恢复数据的流程如下:
- 取最近一次全量备份, 假设这个库是一天一备, 上次备份是当天0点;
- 用备份恢复出一个临时库;
- 从日志备份里面, 取出凌晨0点之后的日志;
- 把这些日志, 除了误删除数据的语句外, 全部应用到临时库。
延迟复制备库
延迟复制的备库是一种特殊的备库, 通过 CHANGE MASTER TO MASTER_DELAY = N命令,可以指定这个备库持续保持跟主库有N秒的延迟。
比如你把N设置为3600, 这就代表了如果主库上有数据被误删了, 并且在1小时内发现了这个误操作命令, 这个命令就还没有在这个延迟复制的备库执行。 这时候到这个备库上执行stop slave, 再通过之前介绍的方法, 跳过误操作命令, 就可以恢复出需要的数据。
rm删除数据
只要不是恶意
地把整个集群删除, 而只是删掉了其中某一个节点的数据的话, HA系统就会开始工作, 选出一个新的主库, 从而保证整个集群的正常工作。这时, 你要做的就是在这个节点上把数据恢复回来, 再接入整个集群。
32 | 为什么还有kill不掉的语句?
收到kill以后, 线程做什么?
实现上, 当用户执行kill query thread_id_B时, MySQL里处理kill命令的线程做了两件事:
- 把session B的运行状态改成THD::KILL_QUERY(将变量killed赋值为THD::KILL_QUERY);
- 给session B的执行线程发一个信号。
kill无法生效的情况
- kill无效的第一类情况, 即: 线程没有执行到判断线程状态的逻辑。
- 另一类情况是, 终止逻辑耗时较长。