前言
之前去过几次相关 go 的线下 meetup,这次相对来说比较大型一些,两天的听下来还是比较烧脑的,光是记录的笔记都有近千行了,整体来说收获很大。
有的人问,值票价吗?我回答:对喜欢的投资没有不值得的。对我来说值了~
有的人问,值得去吗?我回答:不一定,因为可能在很多大佬看来能听到的点不多(采访了几位现场的大厂观众,普遍表示只有其中 1 到 2 场满足他们的要求)但是如果你的小白或者一年到两年左右,还是能见识很多东西的。可能是我听多了,和之前自己学到的有点重复...
下面是个人精炼总结,全是个人总结,自己如果对这个知识或者相关点比较清晰的我就没有记录了。其中会伴随一些个人思考和疑问,可以小声 bb 。大会分 1和 2 会场,所以只能选其中一个听,我也是记录我自己听到的,另外一个会场不清楚(我交叉穿来穿去的)注意并不是有的老师讲的真的不好,而是对我个人来说意义不明显,可能由于知识点已经掌握,也可能是由于工作上确实用不到哦
最后会附上会上笔记,仅做个人使用,乱了也不管~ 最后无论如何,感谢每一位老师的付出~~~
要点总结
- 分布式数据库设计难题:数据分片,节点加入和减少
- 如果让你设计一个系统,扫描两个人经过的地理位置,从而进行匹配出擦肩而过的人,你会怎么设计?
- Go Programming Patterns (这一部分建议直接看 PPT,每一张仔细看,学到很多,这一部分很值)https://www.slideshare.net/haoel/go-programming-patterns
- 使用嵌入或者聚合来做设计
- 使用 IOC 进行控制翻转来设计*
- golang 中 如何 做 MapReduce
- generic 我还是不太喜欢生成很多无用代码,虽然确实方便,但总觉得代码量太大看着不喜欢
- 如何实现一个类似 linux 管道 命令的 go 代码 pattern
- Go-kit 如何设计一个脚手架生成器,需要关注那些点
- 奥卡姆剃刀法则,大道至简
- 是否应该使用 sidecar,我觉得更多时候不需要,虽然这是一个不错的设计
- 配置加载的最佳实践:配置文件yaml -> struct -> 提供 options 方法 -> 传入到初始化方法中(这个思路可以)
- code review 很重要,一次提交 170 多个评论,NB
- 文档优先,30% coding 时间(我也是一个不太愿意去写文档的开发,后面可能会有所改观)文档不是补出来的
- 《软件开发的 201 个原则》
- 《代码艺术》
- 拆分时的一个原则:单一职责
-
https://github.com/didi/nightingale
- 夜莺本身我觉得是一个挺不错的产品,为运维开发提供了一个解决方案
- 但是讲座本身更多的在将功能和设计,对我来说意义不是特别大
- 产品定位很明确,希望后面越来越好
- go+ 很 py 让我了解了原来可以在一个语言之上构建一个语言,解释器很棒,很喜欢,后面会考虑拿来用;老许没来,可惜了
- tidb 遇到的问题
- 调度延迟
- 内存管理 transparent huge page 透明大页还会有这样的问题,学到了
- NUMA 下的问题,多核绑定
- Go 语言编译器 这又是一位大牛分享怎么优化编译器的,让我学到了编译过程,但能力有限,后面再说吧....(没有兴趣继续研究)
- EDAS 云原生
- 讲师对于控制器模式的讲解很到位,这是我第一次听到有人将这个模式比喻的很清晰(建议好好看 ppt)k8 的核心
- OAM 模型很不错,这个抽象很棒,后续可以详细了解一下
- gorm 嗯,挺棒的,但是我现在不用~ 等我有机会回来再用你
个人总结
我建议下面几场没听的可以去听下,至少对我来说收获比较大:
- 耗子哥的 Go Programming Patterns 让我学到了一些好的设计,其实和毛老师的有重合,毛老师+了一点点【1.3 go-programming-patterns】【1.6 Functional options and config for APIs】
- 百度的 BFE 主要学到了代码之外的项目管理方法和思路,还有文档,以及一些非编程相关的思想,经验传递!【2.1.1 百度万亿流量转发平台背后的故事】
- tidb 让我认识到了一些极高压力下的性能瓶颈,有些场景怕是我这辈子不一定能遇到,up!【2.1.4 PingCAP-Go runtime related problems in TiDB production environment】
- EDAS 其中的 OAM 模型抽象不错,学到了【2.2.6 Golang大规模云原生应用管理实践】
- go+ 和 编译器 让我理解了 go 的编译过程,之前学的比较粗,现在有了新的认识,更加明确了,这块确实不适合我~【2.1.5 Go语言编译器简介】
当然还有几场我没听到的据说也有很多金子,后面再想办法看看吧。
PS:英文的那场普罗米修斯真的对我来说太难了,语速真的超过我的理解能力了,抱歉是我不行。
-/以下为 大会上 笔记 边听边盲打 易错体质/-
探探分布式存储
结合业务需求需要一个轮子,需要一个分布式存储
业务:其实更多的业务场景不需要分布式事务,更多是通过补偿机制来完成的。是否需要强一致性的业务?
- ACID BASE
- RECELE
- SQL
特性
sql
解析 sql
- AST
- 语法校验
- 优化
- 列执行计划
- 生成执行器
逻辑优化器:列裁剪、谓词下推、聚合下推、topN 下推
执行器优化:从“火山执行器”走到“向量执行器” 以列为单位去处理
数据分片
hash:使用 hash 之后之后取余之后存储
rehash:需要将机器上的数据扫一遍,有点慢
故障检测
中心化:超时,网络分区,使用的是这个
无中心化:gossip 收敛会慢
其实就是通过心跳检测去监控故障检测
故障恢复
使用分布式锁,直接进行选举,没必要讲究什么公平性,只要抢占的越快负载越低,直接当选即可
当故障进行恢复的时候,所有的请求直接打到一个新的从节点从而导致峰值,解决方式是偶尔将请求发送给从节点,从而保持从节点的热数据
实践
时延从 19ms - 3ms
有针对磁盘的存储进行优化
词法分析的优化暂不考虑
Golang
goroutine 限制和泄露,使用 goleak 来进行检测 http://github.com/uber-go/goleak
忘记 defer cancel 导致 context 泄露
主要还是依赖 pprof
尽量减少对象分配,进行逃逸分析,尽量分配到 stack 上去,在外部申请对象,将对象的指针传入到 function
TODO
将在 2021 年开源,物理优化,同步复制,Range 分区(因为分区的原因,其实是去每一台机器上去查一遍然后进行聚合,暂时没有好的解决),生态
对于 join 的查询支持是不好的,还是有可能出现所有机器进行扫描全量数据的可能性
Go Programming Patterns
左耳朵
function vs receiver
推荐使用 receiver 进行
interface patterns
使用 对象 进行实现 接口,进行解耦 EX: io.Reaed / ioutil.ReadAll
time
使用 RFC3339 的时间格式进行交互
delegation
使用嵌入或者聚合来做设计
使用 IOC 进行控制翻转来设计
error handle
if err ≠ nil
使用 closure for error
bufio.NewScanner(
想法还是包装一次,使用一个 error 的一个局部变量,当有错误直接 return,否则进行处理
使用包装 error %w
function options
使用 config 进行传入,使用 withOption 进行
MapReduce
使用 Mapreduce 进行函数式编程
SumIf、CountIF,FileterIn
generic map
使用反射进行 transform 进行处理更加优雅
Verify function signature
可以用它来去类型
type assert 和 reflection
使用类型检查和反射来使泛型操作
go generate,使用代码生成来生成你的不同类型的操作
可以使用 gen
pros / Cons
别 cv 了,类型检查比较慢,使用 gen 有成本(代码量膨胀),reflection 代码比较难
decorator
使用包装函数来包装一个函数的前后方法
还可以使用 defer 进行包装调用
EX:http server
kubernetes visitor
visitor,使用嵌套进行包装,并且进行内部进行调用装饰处理,这样的就可以实现一个 receiver,进行多个 function 的调用
确实比较绕一点,优雅
pipeline
使用一个函数的输入输出来进行 pipe
或者使用一个 channel 进行解耦,可以写出 类似 linux pipeline 的编程模式
Grab food - discovery
preview
tree - model
api pool
grab-kit
grab-kit
可伸缩、容错、观察
支持 http/rpc
目标就是去简化 go-kit 的开发过程
脚手架的使用
- 通过 proto 去定义
- service 定义 req 和 resp,req 的简单校验
- 调用命令去生成其他所有的脚手架
中间件的使用
- 服务中间件将中间件的定义成了不同类型的中间件
- defensive 去检测 typed nil,使用中间件去保证业务上不要出现一些问题
- chaos 混沌测试,在生产环境中,独立出一部分流量,故意失败一些服务,测试服务伸缩告警等性能是否正常
- resilient 弹性控件:限流、cpu 利用率的限制、使用 cache、timeout
监控
datadog,商用的,通过启动时进行注册,打点会直接相关数据直接扔到上面去
会有一个默认的 dashboard 的进行展示
food discovery(ML PIPELINE)
候选集合 - 过滤 - 重排序 - 后排序
从移动端的日志流,进入数据湖,train 会生成模型,然后不断进行训练修改,deploy 到线上,然后提供 api 进行调用
模型很复杂、训练样本,特征,都会有影响
模型和模型之间有很多关联关系
训练样本很多,很多时候特征不是很重要,但是计算复杂
特征还好发生变化,模型和系统之间的交互很复杂,对于你的环境造成很多影响
反馈环导致准确率不高,很难评估和验证
需要提供完备的监控来保证这个
将所有的 CI\CT\CD 持续训练,通过 devops 来的想法,将整个流程自动化起来,降低交付成本;
准备数据,训练,实验,部署,监控
Hierarchiacal Temporal-Contextrual
LightGBM
why go
- 编译速度,使用一个 repo 来维护一个整个大的微服务,一个微服务的改动会使得上游进行编译
- 依赖管理
- GC pause time
*使用 kubebuilder 进行 build 构建
华为云 通信协议
Service Center
注册发现:
- 微服务的静态信息和动态信息分离:冗余信息不进行传输,减少网络压力
- 契约:api 优先,将 api 的定义直接到了注册中心,使用 mock 的方式来保证效率;还有 restful 定义不一致,还有相似的 api 能力,帮助团队 API 审视;
- 服务依赖管理:让整个微服务架构能知道服务调用链路,这里是通过卖点实现的;使用 webhook 或者 mq 进行解耦;减少链路,层级关系降低,让一个服务去聚合;
- 缓存更新机制:使用 etcd 来进行维护,将所有的数据缓存到本地,watch key 变化,即使 ETCD 挂了也没事,缓存还有
交付的不只有业务,包括监控,告警,中间件,限流,跨域,认证,调用链追踪等等,插件的部署
交付角度:
- 一个分支进行交付(不对的!一定要做好插件化,一个分支进行迭代)
- 依赖倒置,对接不同的服务,在编译器保证插件
- 对象存储不同的对接
- 不同的算法对接
chassis
http://github.com/go-chassis/go-chassis
手段 1 将后端服务作为插件使用
把你的后端服务作为一个资源使用;注意这个并不是一个微服务的体系,这个还是有区别的;
面向用户来说,定义接口,定义插件安装api,然后提供 api;
手段 2 标准化模型
将不同的请求转换成一个一致的协议,然后到一个统一的中间件进行治理;所有的请求都将转换到一个 invocation 进行调用;
通过申明试的调用,对自己的流量进行描述,然后限流策略,那些接口等;
熔断,复杂均衡等都是通过申明式实现的
收益:其他的业务团队可以减少开发,很多提供的能力都可以复用来看
手段 3 配置治理
单体对于配置管理很简单,但是分布式的时候配置管理就需要进行配置治理;
source 概念:当发生一些配置变更的时候,将触发不同的 event 然后分发到不同 listeners
手段 4 易处理
统一的接口层,运行你注册业务逻辑,有基本的 start 和 stop
支持不同的编程模型
利用一个装饰器的模式,进行一个修饰,将框架的东西加进去,利用插件的方式安装进去,利用配置文件,指定不同的协议,都可以
优雅停机:基本的系统信号都会捕捉,保证所有业务请求都处理完成才会真正的 shutdown
提供 pre、post 等自定义之后
go 1.8 之后已经支持优雅停机了
手段 5 奥卡姆剃刀法则
最小化容器的大小
最小化安全漏洞
最小化代码量
最小化复杂度
其实是将很多依赖都搬运到了 extention 中
bootstrap功能:任意替换默认实现,加载并允许新的模块供运行时提供和使用
该不该用 sidecar
跟流量相关的治理功能都可以沉降到服务网格中
后面可以将功能进行云原生
go-chassis
- 华为网关产品
- 使用了这个产品
- 实时音频的 RTC
- 边缘计算 kubeedge
- 基于框架的二次封装
functional options and config for apis
毛剑
function 的定义
初始化 api 定义;将所有的初始化的方法都来一遍,很麻烦
解决方式 1:通过一个结构进行参数定义
需要将所有的配置对外暴露,其实就是搞个 config 一个对象来进行初始化
- 不知道参数是否可选
- 文档写起来比较多
- 外部有可能会直接进行修改
*对于一个 public 方法不要传递一个 nil 来进行
解决方式 2:通过 options 来进行扩展
每一个 withoption 都是可以进行使用的
当 option 为 nil 使用 default 进行替换
独立去维护每个 option 文档
还可以保留状态,使用 defer 进行状态还原,-vvv
还定义 Option interface 在 before 进行前后进行操作
问题:配置文件是 json 或者 yaml
你的 api 和你的配置文件不应该强绑定
*google sre 运维
将你的配置文件转换成 Config 对象,然后让 Config 对象提供一个 Options 方法,然后将配置返回成 Options slice
这样就能保证使用 json 或者 yaml 格式的配置文件,也能支持 option 方法的 function 设计
配置最佳实践
配置中心方便了配置变动,那么更加容易出错;
- 避免复杂配置,更多使用默认配置
- 通过配置文件模板来帮助你减少错误,配置中心或支持配置引用
- 配置的防御编程
- 配置的权限变更和追踪
- 配置版本和应用版本对齐,回滚的时候也要注意配置回滚
配置是否需要支持热加载
配置分为动态配置和静态配置
静态配置热加载是很有风险的,所以不要进行配置热加载,任何一次配置变更都走一个迭代版本;
动态配置还是通过后端数据进行拉取更加靠谱;
百度 BFE 平台
讲如何做一个 BFE 的,是怎么构成的?
什么是 BFE
一个统一的七层流量转发平台,在内网跨机房的调度
一个专用的转发引擎
对于流量的转发
支持春晚的红包
团队理念
对技术足够痴迷,一个东西做 5 年做 6 年去做,寻找的真正的 SE
30 开始只是写代码无数必备素质之一
看中寻找和培养人才,只有把人培养好
看中过程:如果没有正确的方法,成功只是偶然
只需要 975,需要聪明的工作,平衡了工作和生活
项目研发的指导思想
意识 =》看见 =》行动
研发方向的确立
以一个研究问题的角度,去解决,直接对标了国际上的论文去专门研究
通过趋势的判断,来指定产品的角度
研发过程的管控
- 质量,来源于对产品研发过程的严格管控
- 只要的把控
- 代码
- 文档
- 项目管理
一流代码特性
- 鲁棒
- 搞笑
- *简洁
- *简短
- 可测试性
- 共享(可复用)
- 可移植
- 可监控
- 可运维
- 可扩展
正确、性能、可读、可维护、共享、重用、运维、运营
code review 的重要性
一个 commit 添加 187 条 commet
复用的基础库
不断地抽离可以复用的基础库
建立基础库的前提 → 知道怎么切分模块 还是需要抽象的能力
文档
文档本身也是产出:codeing 时间少于30%
写文档是整理思路的过程:打字的速度应该快于思考的速度
没有文档后期会花费更多的维护成本
简单的项目也需要文档
文档不是补出来的
《软件开发的 201 个原则》
文档和代码都是一种沟通方式
- 文档也需要 review
- 以项目为单位建立文档索引
项目管理
好的管理比好的技术更重要
项目的规划和启动
周报
迭代管理
项目总结和回顾
lite weight process
研发和运维的一体化
- 研发和运维分离的问题
- 研发不考虑运维的痛点
- 可运维能力,是系统的重要考虑
- 日志,内部转给他的展示,监控 monitor 的一个库
- 链路追踪
努力提升工程能力
正规科学的研发方法,提升工程能力,才是解决之道
复杂系统并不是的技术无法实现,而是因为工程能力没有正确的
模块划分
单一目的 single purpose
数据封装
《代码艺术》
滴滴
https://github.com/didi/nightingale
海量运维问题
- 大量不同的服务
- 大规模的机器
- 网络抖动
- 机器环境各异
- 机器上发行版不一样
- ....
在流量转发层做文章
统一机器初始化
统一权限,统一日志,统一审计
自动化工单,知识库构建,答疑
为什么选择 go
资源占用少,适合开发 agent
静态编译依赖少
运维相关的东西都是用的 go
运维平台体系构筑方向
安全稳定高效低成本
监控标准
故障的响应原则
漏报率是一个比较重要的指标
稳定性
预防、发现、定位、止损、富婆
应急备案
变更体系
带外装机管理、堡垒机跳板、机器初始化
效率体系
日常工作平台化
重复工作自动化
ChatOps 各类聊天机器人
知识库
运维体系如何构建
夜莺
任务执行更新
支持 ansible saltstack 批量执行脚本,权限可以控制的更针对一些,审计方面,轻量级一些
主要还是一个运维工单的能力来进行的一个构建,能够帮助一个新的公司快速构建一个运维体系
想要支持 M3
所有的模块都是高可用的,体系化程度更高
http 接口的成功率,延时,产品化程度更高一些
对于物理机虚拟机的监控更加支持一些
GO+
七牛
为什么要做 go+
作用于有限的领域
作用于科学领域
数据规模非常的大
python 让数据软件平民化
python 做不了基础设施?
在 go+ 之上做数学软件
现在有什么特征
和 go 完全兼容
语法、代码简洁
所有 go 的包都可以直接引用
- 支持有理数
- map 特有语法
- 让计算处理更简单
- 特有的 err 处理 ! ? 。。。
运行逻辑
go+ 源码 - ast - 双引擎 - 字节码或者 go 源码
token 和 parser 是生成拓扑序列的
- 词法分析:将源文件转换成 token 序列,go+ 有自己的 tokne
- 语法分析:将 token 序列转换为 AST,go+ 需要支持自己的语法,需要将这个抽查语法树的重新实现和构建
- 编译:解析 AST 转换成 二进制或者 go 源码,生成 go 源码将 AST+ → AST 即可,转成 go 的源码
Go+的下一步
冷板凳?其实做语言本身是很难得,很少有人愿意去学习解析语法等等一些功能
PingCAP TiDB
runtime 相关问题
调度延迟
案例:
多个 goroutine 向 channel 写消息,一个 for 循环去处理这个消息,发现延迟很高
调查:
从网络可读到到 goroutine 被调度唤醒可以处理请求,出现 4.x ms
在混合负载中 由公平调度导致的
内存管理
找操作系统分配内存,都是 runtime 决定的;真正用到的就是 inused memery
runtime 会估算自己要用多少,不会马上归还操作系统,会保留一些
操作系统有一个快表,TLB,默认的一个页大小是 4k,经过虚拟地址和物理地址的映射
透明大页:它把每页的大小改大
案例:
开启透明大页之后发现内存很大
从操作系统层面看到内存量很高,但是实际使用不高
anonHugePage
和 transparent huge page 开启相关
分析:
当开启之后 THP 在 go 的视角里面其实释放了,但是因为页大,所有操作系统看来,有的页还有内存碎片,所以无法回收
操作系统会有对内存进行调整
GC Reload
go gc 有一个优化,写屏障;
当垃圾回收的速度跟不上分配的速度怎么办?
go 会拿 25% CPU 时间,超过 25 之后就会让分配慢下来,进行清扫工作
案例:
请求响应不正常,cpu、io、网络都没问题
抓 go 的 trace,发现请求有被 GC 占用了很多时间
即使没有 STW,但是 runtime 还是会让它慢下来
发现扫描了 1G 的内存,但是只是发现只有 8k 被回收
依然会有抖动
*抢占主要解决 for 死循环导致 STW 时间过长的问题
QPS 瓶颈
在多核下的单一进程,没有多进程绑核来的快
资源没有被耗尽但是还是有瓶颈,CPU 使用率变高了,NUMA
分析:
pprof/mutex 去查了一下,优化了一下
是不是和 NUMA 绑定核心有关?
单个改动的性能提升最大
发现 67% 时间没有在干活
runtime 在页分配器是有锁的,会有锁的争抢,在多核的情况下会遇到
现在的 go 的版本已经优化了
NUMA
现在 CPU 核数很多,每个 NUMA 核心上尽量会分配到和自己近的
gc is not numa aware
所以在多核上的调度不是特别
Go 语言编译器简介
编译器的重要性
汇编语言无法构建大型系统
计算机复杂度理论
编译器的难题
将不同的语言增加,需要有不同的机器,将不同的语言编译成不同的机器需要很多任务
简化:
将别的语言编译成 C 然后再编译成别的语言
将别的机器先编译成 x86,然后再编译成目标机器的语言
通用(非专用)编译器方案
高级语言先生成 AST 然后再 SSA IR 单一静态赋值
基于 SSA 的优化
LLVM
中断接受中间表示进行优化,对每种机器机械能优化
GO 编译工作流程
语法分析
将源代码翻译成 AST
语义分析
编译器会进行常量折叠,会将 2*3 → 6
内联:会将 B 函数调用 A 函数的函数方法直接内联到函数内部,为了提高效率
SSR IR
更像汇编语言
赋值,goto,if。。。
中端优化
公共子表达式消除,将重复运算减少;
运算强度消除,将乘法进行优化成位操作,或者是取余操作替换为&操作
常量折叠:将两次乘法合成一次乘法
-循环不变量外提、将乘法变成加法
后端指令选择
将 IR 映射到汇编
阿里云-云原生
go 在云原生中的应用
程序员视角的云原生
标准化技术,标准化运维
从资源统筹规划,大规模的自动化运维
让云更经济的被用户使用
go 在 CNCF 中占据了很大的部分
插入:策略与机制
策略是做事的概念和计划,坐火车和坐飞机是一种策略
机制是面向内部实现的,机制是追求稳定和复用
应用管理额策略和机制
策略:部署策略,负载均衡策略,路由规则,工作负载
直接面向机制很难;
认知成本高,k8s 没有统一的使用方式,我们需要学习复杂的申明字段,各种申明
对开发者很好的使用这个平台,融入云原生
插入:k8s 的核心机制-申明试资源与控制器
- 控制器:空调控制器
- 被控制系统:房间温度
- 期望状态:26 度
- 管控动作:送冷气降温
- 当前状态:28 度
label 是一等公民:通过它去寻找
事件触发闭环反馈
由很多的控制器组合来完成的
EDAS
阿里云云原生 PAAS 平台
将所有平台的运维能力,将所有的控制器都部署到了 k8s 的平台;
OAM 应用模型
open application model 微软和阿里
一个应用是由很多个组件构成,每个组件都有自己的工作负载去描述运行时(有状态/无状态...),有了不同的运维特征
路由策略,基于 QPS 的自动弹性,这些都是运维特征
多个组件可以通过一个作用域来描述,这几个组件的资源限制
- 应用
- 组件
- 工作负载
- 运维特征
- 作用域
组件间依赖
KubeVela
内置 workloads Traits 和 Scopes
其实还是通过一个控制器模式,将上面模型中的所有资源进行初始化,配置,调度,向上包装一层
新的复杂度-开发模式
声明试资源的设计,一定要一个终态;
可重构的状态机,期望状态,你状态是可重构的,事件驱动+主动轮询
重试和幂等,自愈
坑-最终一致性
有一个资源是成功,很多人同时修改,利用 version 和 resoureveriosn 做乐观 observeVersion
可观测的
如果应用没有到终态,是因为什么原因;
抛出 k8s 的事件和消息
控制器的管控平台,来管控制器的状态
发展趋势
通过应用模型描述好了,我们只需要提交代码即可
运行时的依赖也标准化
轻量可观测,开发和运维的区别就是观测上的区别
*datr,全部都已 sidecar 的进行封装了
*将噪音在前面就消除,宁愿什么都不动,也不要动错
gorm 剖析和最佳实践
语句是如何生成的
基本就是将代码解析成语句
初始化的时候就将不同的数据库版本来生成不同的配置,对于 sql 进行不同的版本生成
hits 对前后进行封装
对于不同的数据库实现都是不一样的
插件支持
读写分离的支持,加入一些判断
最佳实践
0 值问题
指定字段,通过传入一个 map 也可以
直接指定字段
表达式
使用 map 或者 struct 去创建
自定义类型
支持自定义类型就可以支持 json 类型的数据
分页逻辑
自定义分页逻辑
数据库事务
支持全局关闭事务
使用 transaction 块
支持事务嵌套,使用安全点实现
字段的权限
可读,可写,禁止操作等
有疑问加站长微信联系(非本文作者)