获得徽章 14
- #青训营笔记创作活动#
1月15日 打卡day36
今日学习
今天的内容关于Go语言基础语法。
允许对值为 nil 的 slice 添加元素,但对值为 nil 的 map 添加元素,则会造成运行时 panic。
当访问 map 中不存在的 key 时,Go 则会返回元素对应数据类型的零值,比如 nil、’’ 、false 和 0,取值操作总有值返回,故不能通过取出来的值,来判断 key 是不是在 map 中。检查 key 是否存在可以用 map 直接访问,检查返回的第二个参数即可。
string类型的值不能修改,尝试使用索引遍历字符串,来更新字符串中的个别字符,是不允许的。string 类型的值是只读的二进制 byte slice,如果真要修改字符串中的字符,将 string 转为 []byte 修改后,再转为 string 即可。
switch 语句中的 case 代码块会默认带上 break,但可以使用 fallthrough 来强制执行下一个 case 代码块。
直接在处理 HTTP 响应错误的代码块中,直接关闭非 nil 的响应体;手动调用 defer 来关闭响应体。
要主动关闭过http连接,不关闭会程序可能会消耗完 socket 描述符。
在解析 JSON 数据时,Go 默认会将数值当做 float64 处理。
在一个 defer 延迟执行的函数中调用 recover ,它便能捕捉/中断 panic。
简短声明的变量只能在函数内部使用。
struct 的变量字段不能使用 := 来赋值。
不能用简短声明方式来单独为一个变量重复声明, := 左侧至少有一个新变量,才允许多变量的重复声明。
range迭代map是无序的。Go 的运行时是有意打乱迭代顺序的,所以你得到的迭代结果可能不一致。但也并不总会打乱,得到连续相同的 5 个迭代结果也是可能的。
recover 必须在 defer 函数中运行。recover 捕获的是祖父级调用时的异常,直接调用时无效。
defer 在函数退出时才能执行,在 for 执行 defer 会导致资源延迟释放。
可以通过 context 包来避免内存泄漏。
在go语言中,for select配合时,break 并不能跳出循环。
展开评论1 - #青训营笔记创作活动#
1月14日 打卡day35
今日学习
今天的内容关于Go的HTTP框架Hertz。
登陆前要进行注册,注册流程:获取用户名密码和邮箱、判断用户是否存在、创建用户。
服务器需要在用户第一次登陆的时候,验证用户账号和密码,并签发 jwt token。
Authenticator:用于设置登录时认证用户信息的函数,demo 当中定义了一个 loginStruct 结构接收用户登陆信息,并进行认证有效性。这个函数的返回值 users[0] 将为后续生成 jwt token 提供 payload 数据源。
PayloadFunc:它的入参就是 Authenticator 的返回值,此时负责解析 users[0],并将用户名注入 token 的 payload 部分。
Key:指定了用于加密 jwt token 的密钥为 "secret key"。
Timeout:指定了 token 有效期为一个小时。
MaxRefresh:用于设置最大 token 刷新时间,允许客户端在 TokenTime + MaxRefresh 内刷新 token 的有效时间,追加一个 Timeout 的时长。
访问配置了 jwt 中间件的路由时,会经过 jwt token 的校验流程。
代码大部分是通过 hz 命令行工具生成的脚手架代码,开发者无需花费大量时间在构建一个良好的代码结构上,专注于业务的编写即可。
hertz 使用开源库 go-tagexpr进行参数的绑定及验证,demo 中也频繁使用了这个特性。
展开评论1 - #青训营笔记创作活动#
1月13日 打卡day34
今日学习
今天的内容关于MySQL索引机制。
使用B+树结构的原因:简化全表扫描过程。
当手动创建索引后,MySQL会先看一下当前表的存储引擎是谁,接着会判断一下表中是否存在数据,如果表中没有数据,则直接构建一些索引的信息,例如索引字段是谁、索引键占多少个字节、创建的是啥类型索引、索引的名字、索引归属哪张表、索引的数据结构。
当表中有数据时,首先MySQL-Server会先看一下目前要创建什么类型的索引,然后基于索引的类型对索引字段的值,进行相应的处理,比如:
唯一索引:判断索引字段的每个值是否存在重复值,如果有则抛出错误码和信息。
主键索引:判断主键字段的每个值是否重复、是否有空值,有则抛出错误信息。
全文索引:判断索引字段的数据类型是否为文本,对索引字段的值进行分词处理。
前缀索引:对于索引字段的值进行截取工作,选用指定范围的值作为索引键。
联合索引:对于组成联合索引的多个列进行值拼接,组成多列索引键。
聚簇索引和非聚簇索引的根本区别:
聚簇索引中,表数据和索引数据是按照相同顺序存储的,非聚簇索引则不是。
聚簇索引在一张表中是唯一的,只能有一个,非聚簇索引则可以存在多个。
聚簇索引在逻辑+物理上都是连续的,非聚簇索引则仅是逻辑上的连续。
聚簇索引中找到了索引键就找到了行数据,但非聚簇索引还需要做一次回表查询。
InnoDB-非聚簇索引与MyISAM-非聚簇索引的区别:
InnoDB中的非聚簇索引是以聚簇索引的索引键,与具体的行数据建立关联关系的。
MyISAM中的非聚簇索引是以行数据的地址指针,与具体的行数据建立关联关系的。
一般来说,由于MyISAM引擎中的索引可以根据指针直接获取数据,不需要做二次回表查询,因此从整体查询效率来看,会比InnoDB要快上不少。
展开评论1 - #青训营笔记创作活动#
1月12日 打卡day33
今日学习
今天的内容关于MySQL表单行数。
如果主键声明 int 大小,也就是 32 位,那么支持 2^32-1 ~~21 亿;如果是 bigint,那就是 2^62-1 ?(36893488147419103232),难以想象这个的多大了,一般还没有到这个限制之前,可能数据库已经爆满了。如果建表的时候,自增字段选择无符号的 bigint , 那么自增长最大值是 18446744073709551615。
数据实际是放在一个叫 person.ibd (innodb data)的文件中,也叫做表空间;虽然数据表中,他们看起来是一条连着一条,但是实际上在文件中它被分成很多小份的数据页,而且每一份都是 16K。
因为每个页只有 16K 的大小,但是如果数据很多,那一页肯定就放不下这些数据,那数据肯定就会被分到其他的页中,所以为了把这些页关联起来,肯定就会有记录前后页地址,方便找到对应页;同时每页都是唯一的,那就会需要有一个唯一标志来标记页,就是页号;
页中会记录数据所以会存在读写操作,读写操作会存在中断或者其他异常导致数据不全等,那就会需要有校验机制,所以里面还有会校验码,而读操作最重要的就是效率问题,如果按照记录一个个进行遍历,那肯定是很费劲的,所以这里面还会为数据生成对应的页目录(Page Directory)。
在 mysql 中索引的数据结构和刚刚描述的页几乎是一模一样的,而且大小也是 16K, 但是在索引页中记录的是页 (数据页,索引页) 的最小主键 id 和页号,以及在索引页中增加了层级的信息,从 0 开始往上算,所以页与页之间就有了上下层级的概念。
Mysql 的表数据是以页的形式存放的,页在磁盘中不一定是连续的。
页的空间是 16K, 并不是所有的空间都是用来存放数据的,会有一些固定的信息,如,页头,页尾,页码,校验码等等。
在 B+ 树中,叶子节点和非叶子节点的数据结构是一样的,区别在于,叶子节点存放的是实际的行数据,而非叶子节点存放的是主键和页号。
索引结构不会影响单表最大行数,2kw 也只是推荐值,超过了这个值可能会导致 B + 树层级更高,影响查询性能。
展开评论1 - #青训营笔记创作活动#
1月11日 打卡day32
今日学习
今天的内容关于MySQL语句。
max_connections:MySQL的最大连接数,超出后新到来的连接会阻塞或被拒绝。
version:当前数据库的版本。
ft_min_word_len:使用MyISAM引擎的表中,全文索引最小搜索长度。
ft_max_word_len:使用MyISAM引擎的表中,全文索引最大搜索长度。
ft_query_expansion_limit:MyISAM中使用with query expansion搜索的最大匹配数。
innodb_ft_min_token_size:InnoDB引擎的表中,全文索引最小搜索长度。
innodb_ft_max_token_size:InnoDB引擎的表中,全文索引最大搜索长度。
optimizer_switch:MySQL隐藏参数的开关。
skip_scan:是否开启索引跳跃扫描机制。
innodb_page_size:InnoDB引擎数据页的大小。
tx_isolation:事务的隔离级别。
autocommit:事务自动提交机制。
innodb_autoinc_lock_mode:插入意向锁的工作模式。
innodb_lock_wait_timeout:InnoDB锁冲突时,阻塞的超时时间。
innodb_deadlock_detect:是否开启InnoDB死锁检测机制。
innodb_max_undo_log_size:本地磁盘文件中,Undo-log的最大值,默认1GB。
innodb_rollback_segments:指定回滚段的数量,默认为1个。
innodb_undo_directory:指定Undo-log的存放目录,默认放在.ibdata文件中。
innodb_undo_logs:指定回滚段的数量,默认为128个,也就是之前的innodb_rollback_segments。
innodb_undo_tablespaces:指定Undo-log分成几个文件来存储,必须开启innodb_undo_directory参数。
back_log:回滚日志的最大回撤长度(一条数据的最长版本链长度)。
展开评论1 - #青训营笔记创作活动#
1月10日 打卡day31
今日学习
今天的内容关于Redis本地锁和分布式锁,包括本地锁,分布式锁,分布式锁实现,Redlock红锁。
本地锁主要是针对单体服务而言的,锁的都是单体应用内的进程。
多服务并发时,如果还是只给当前线程加锁,多个用户一起尝试获取锁时,可能会有多个用户同时获取到锁,导致出现问题。每个服务都是单独的,加锁操作也只是给自己的,大家不能共享,那么实际上在高并发的时候,是根本没效果的。我1号服务抢到了锁,还没等到释放,2号服务又获取到了锁,接着3号、4号等等,大家都可以操作数据库,这把锁也就失去了它该有的作用。
本地锁失效是因为无法锁住各个应用的读写请求,失效的根本原因就是其他的服务无法感知到是否已经有请求上锁了,即无法共享锁信息。分布式锁,其实也就是将加锁的这一个操作,单独的抽取出来了,让每个服务都能感知到。
分布式锁的实现有多种方式,大的范围中有 Redis、Zookeeper、MySQL等实现方式,我具体讲的是以 Redis 的实现。
使用SET KEY NX EX TIME 命令解决死锁问题。增加身份标识(UUID) 解决锁被其他人释放问题。使用 Lua 脚本,将解锁操作变成原子性操作。Redisson实现分布式锁,解决了锁自动续期问题。
为了获取锁,客户端以毫秒为单位获取当前时间,它尝试顺序获取所有 N 个实例中的锁,在所有实例中使用相同的键名和随机值。在步骤 2 中,当在每个实例中设置锁时,客户端使用一个与锁自动释放总时间相比较小的超时来获取它。例如,如果自动释放时间为 10 秒,则超时可能在 ~ 5-50 毫秒范围内。这可以防止客户端在尝试与已关闭的 Redis 节点通信时长时间保持阻塞:如果一个实例不可用,我们应该尽快尝试与下一个实例通信。客户端通过从当前时间中减去步骤 1 中获得的时间戳来计算获取锁所用的时间。当且仅当客户端能够在大多数实例(至少 3 个)中获取锁时,且获取锁的总时间小于锁的有效时间,则认为锁已被获取。如果获得了锁,则其有效时间被认为是初始有效时间减去经过的时间,如步骤 3 中计算的那样。如果客户端由于某种原因未能获得锁(它无法锁定 N/2+1 个实例或有效时间为负数),它将尝试解锁所有实例(即使是它认为没有的实例)能够锁定)。
展开评论1 - #青训营笔记创作活动#
1月9日 打卡day30
今日学习
今天的内容关于跨域问题,包括跨域情况、跨域问题演示、解决跨域问题、跨域问题原理分析。
跨域情况:协议不同、域名不同、端口不同。
解决跨域问题:
使用 @CrossOrigin 注解实现跨域;
通过配置文件实现跨域;
通过 CorsFilter 对象实现跨域;
通过 Response 对象实现跨域;
通过实现 ResponseBodyAdvice 实现跨域。
跨域问题原理:跨域问题本质是浏览器的行为,它的初衷是为了保证用户的访问安全,防止恶意网站窃取数据,那想要解决跨域问题就变得很简单了,只需要告诉浏览器这是一个安全的请求,“我是自己人”就行了,那怎么告诉浏览器这是一个正常的请求呢? 只需要在返回头中设置“Access-Control-Allow-Origin”参数即可解决跨域问题,此参数就是用来表示允许跨域访问的原始域名的,当设置为“*”时,表示允许所有站点跨域访问。
跨域问题的本质是浏览器为了保证用户的一种安全拦截机制,想要解决跨域问题,只需要告诉浏览器“我是自己人,不要拦我”就行。它的常见实现方式有 5 种:通过注解实现局部跨域、通过配置文件实现全局跨域、通过 CorsFilter 对象实现全局跨域、通过 Response 对象实现局部跨域,通过 ResponseBodyAdvice 实现全局跨域。
展开评论1 - #青训营笔记创作活动#
1月8日 打卡day29
今日学习
今天的内容关于流程引擎,包括工作流、流程图、流程绘制工具。
流程相对来说还是比较复杂的,此时你再用一个 status 字段去描述,就很难说的请到底是怎么回事了。每一步审批,都有可能批准也有可能拒绝,拒绝并不意味着流程结束,员工修改报销资料之后,还可以继续提交。此时如果还用 status 去描述,那么 status 将有 N 多个值去表示不同的情况,这个维护起来非常不便。
Activiti 目前是侧重云,他目前的设计会向 Spring Cloud、Docker 这些去靠拢。
Flowable 核心思想还是在做一个功能丰富的流程引擎工具,除了最最基础的工作流,他还提供了很多其他的扩展点,我们可以基于 Flowable 实现出许多我们想要的功能(当然这也是小伙伴们觉得 Flowable 使用复杂的原因之一)。
Camunda 相对于前两个而言比较轻量级,Camunda 有一个比较有特色的功能就是他提供了一个小巧的编辑器,基于 bpmn.io 来实现的(松哥之前已经发文讲过了)。如果你的项目需求是做一个轻巧的、灵活的、定制性强的编辑器,工作流是嵌入式的,那么可以选择 Camunda。
工作流程图这块其实有一个统一的标准,那就是 BPMN。BPMN 全称是 Business Process Model and Notation,中文译作业务流程模型和标记法,这个中文太绕口了,还是简称 BPMN 吧。
这是一套图形化表示法,用图形来表示业务流程模型。BPMN 最初由业务流程管理倡议组织(BPMI, Business Process Management Initiative)开发,BPMI 于 2005 年与对象管理组织(OMG, Object Management Group)合并,并于 2011 年 1 月 OMG 发布 2.0 版本,同时改为现在的名称。
一句话,就是流程图这块有一个特别古老的规范,那就是 BPMN,而我们前面所说的无论是 Activiti、Flowable 还是 Camunda,都是支持这个规范的,所以呢,无论你使用哪一个流程引擎,都可以使用同一套流程图。
展开评论1 - #青训营笔记创作活动#
1月7日 打卡day28
今日学习
今天的内容关于接口。
一般向外暴露的接口,都需要加上一个访问限制,以防止有人恶意刷流量或者爆破,访问限制的做法有很多种,从控制粒度上来看可以分为:全局访问限制和接口访问限制,本文讲的是接口访问的限制。
基于 AOP + 自定义注解 + Redis,并且封装在一个单独的模块 common-web 下,需要使用的模块只需引入该包,并且给需要限制的方法添加注解即可,很方便,且松耦合。
唯一的缺点是该方法只支持在方法上添加注解,不支持给类添加,如果想给一个类的所有方法添加上限制,则必须给该类的所有方法都加上该注解才行。
实现引入依赖主要需要 Redis 和 AOP的依赖,redis我们用spring的,然后aop使用org.aspectj下的aspectjweaver。
写注释新建一个包,命名为anno,然后在包下新建注解,命名为RequestLimit,再新建一个类,命名为RequestLimitAspect,
写逻辑基于RequestLimit注解写环绕运行的逻辑,也就是开始写 RequestLimitAspect 的内容了。
测试时,先把刚刚写的那个模块用maven进行本地打包,然后在其他服务中引入该模块为依赖,对需要进行访问限制的方法使用,运行项目,访问该接口进行测试。
展开评论1 - #青训营笔记创作活动#
1月6日 打卡day27
今日学习
今天的内容关于分库分表,包括分库分表定义、分库分表原因、何时分库分表、如何分库分表、分库分表的问题、分库分表架构模式。
分库分表:分库分表是在海量数据下,由于单库、表数据量过大,导致数据库性能持续下降的问题,演变出的技术方案。分库分表是由分库和分表这两个独立概念组成的,只不过通常分库与分表的操作会同时进行,以至于我们习惯性的将它们合在一起叫做分库分表。通过一定的规则,将原本数据量大的数据库拆分成多个单独的数据库,将原本数据量大的表拆分成若干个数据表,使得单一的库、表性能达到最优的效果(响应速度快),以此提升整体数据库性能。
分库分表原因:我们给数据库实例分配的磁盘容量是固定的,数据量持续的大幅增长,用不了多久单机的容量就会承载不了这么多数据,解决办法简单粗暴,加容量!单机的容量可以随意扩展,但数据库的连接数却是有限的,在高并发场景下多个业务同时对一个数据库操作,很容易将连接数耗尽导致too many connections报错,导致后续数据库无法正常访问。将原本单数据库按不同业务拆分成订单库、物流库、积分库等不仅可以有效分摊数据库读写压力,也提高了系统容错性。
分库分表的问题:分页、排序、跨节点联合查询;事务一致性;全局唯一的主键;多数据库高效治理;历史数据迁移。
分库分表架构模式:client客户端模式和proxy代理模式(入侵时常用)。
展开评论1