简栈

拥抱AI,持续成长

环境准备

我们通过Docker快速的拉取一个环境,这样子对于学习来说成本比价低。直接参照https://hub.docker.com/r/mysql/mysql-server/ 来做就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 快速拉取一个mysql的image
➜ ~ docker pull mysql/mysql-server
Using default tag: latest
latest: Pulling from mysql/mysql-server
501550cfd5c0: Pull complete
e0509d775110: Pull complete
d5a01765d011: Pull complete
970507e942eb: Pull complete
Digest: sha256:0bb21c0f1aa9296e7deafacec5703b80e4d923dfdfcaa2efbe0c8377a8592128
Status: Downloaded newer image for mysql/mysql-server:latest
docker.io/mysql/mysql-server:latest

# 启动mysql容器
➜ ~ docker run --name=mysql1 -d mysql/mysql-server:latest
780c312f6fa1d3b001cb7c97fddb7df39fea61f27732b5fb9b59d5a29b12cfde

# 查看CONTAINER
➜ ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
780c312f6fa1 mysql/mysql-server:latest "/entrypoint.sh mysq…" 59 seconds ago Up 58 seconds (healthy) 3306/tcp, 33060-33061/tcp mysql1

# 查看默认的密码
➜ ~ docker logs mysql1 2>&1 | grep GENERATED
[Entrypoint] GENERATED ROOT PASSWORD: enohmYS*ecvISAqYh@GafRoDP3v

# 直接登录进去
➜ ~ docker exec -it mysql1 mysql -uroot -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 17
Server version: 8.0.23

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

# 修改一下容易记忆的密码,方便下次来使用
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'root';
Query OK, 0 rows affected (0.01 sec)

这是我认为非常快速的一个环境搭建的方式。

数据准备

一般像Orace、MySQL这些好的软件都会提供一些官网的数据来让方便学习着来学习。https://dev.mysql.com/doc/index-about.html

http://static.cyblogs.com/Jietu20210127-205659@2x.jpg

那我们就下载sakila database来学习一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mysql> show tables;
+----------------------------+
| Tables_in_sakila |
+----------------------------+
| actor |//演员表
| address |//地址表
| category |//影片的分类
| city |//城市信息
| country |//国家信息
| customer |//观看影片的用户信息
| film |//影片信息
| film_actor |//影片演员关联表
| film_category |//影片分类关联表
| film_text |//影片的文本信息,更新film的时候通过触发器保持film_text的同步
| inventory |//库存信息
| language |//影片的语言
| payment |//租赁付款信息
| rental |//某个影片库存的租赁信息
| staff |//商店员工信息
| store |//影片的商店信息
+----------------------------+
22 rows in set (0.00 sec)

整体的来看一下数据的一个关系

阅读全文 »

2020年,感觉真的是一个不平凡的年。是时间流逝非常快的一年,也是成长最快的一年。感谢身边所有的人~

谈口罩

还记得年初的时候,怕被疫情隔在家里了,正月初三就开车到了深圳。那时候听新闻说可能要带半年或者一年的口罩,当时感觉不可思议,根本不可能。现在一年已经已经过去了,但口罩还依然要继续……

说到口罩,记得刚刚开始在老家带口罩的时候,家里好多人不理解,为什么要戴口罩?(估计跟很多老美一样,觉得不尊重,觉得自己没病等)到了“恐慌”的时候,家里人比我们更着急,更加上心。在这次疫情中,真的也是感受到了人间百态,看到了白衣天使的伟大,看到了一些黑心的商家,看到了那些平凡的人做着不平凡的事儿,感受到了近些年来祖国的伟大(非常感谢国内短视频)。

今年给我最大的感受是繁忙、成长、充实,总的来说是收获的一年吧。

写作与公众号

年初的时候开启了我的个人公共账号「简栈文化」,一共发表了文章112篇,几乎全部都是跟技术相关的,在编写与梳理这些知识的时候,内心是充满热情与喜悦的。特别是大家去催更的时候,或者有问题请教你的时候,跟你讨论的时候,所有的熬夜与辛苦都是值得的,因为你获得的知识与朋友。后面也是因为工作原因,更新就慢慢的停了下来,但内心一直牵挂着写作这件事儿。

谈工作

今年可以说自己是换了工作,也可以说自己没有换工作,因为做的事情不同了,从楼上换到了楼下,但最大的老板还是同样的老板。不过,还是真的很感谢保险与国际的同事,都给了我很大的帮助、鼓励、肯定。在保险从事的是信贷相关的业务,从小白变成了有一丝丝信贷经验的从业者,也有着自己的小团队,而且氛围非常好,非常的优秀。到国际做的是证券业务,团队又是从0开始搭建,业务又不是很熟悉,感觉一下突然回到了4年前。经过了4个月左右的时间,终于又把团队搭建起来了,同时也在努力的去学习业务与技术。更重要的,我自己对自己的要求更高了,想做的更好。这次的团队跟之前一样,无可挑剔,更加的棒~

读书

阅读全文 »

发现问题

今天本来是为了解决一个Apollo配置与Code同时变更不一致问题,我需要去通过SPI的方式去重写Apollo刷新Remote配置。所以,我就写一个很小的DEMO来验证每次变更Apollo配置对我服务端的取数。

1
2
3
4
5
6
7
@Data
@Component
public class MarketConfig {
@Value("${market.test.chenyuan}")
private String testName;

}

对应在Apollo的配置

1
market.test.name = Test1111

Controller层的代码

1
2
3
4
5
6
7
@Resource
private MarketConfig marketConfig;

@RequestMapping(value = "/sayHello", method = RequestMethod.POST)
private String sayHello(){ // 注意,这里由于我的不小心写成了private
return marketConfig.getMarketTestName();
}

然后我就发起请求来验证我的取数,结果给我报了NullpointException的异常。因为这种代码几乎每天都在写,一下次说我的Bean没有注入进来,还有点方。

然后,我细心的debug了看了一下。如图所示:

http://static.cyblogs.com/Jietu20201204-085800.jpg

阅读全文 »

前奏

MySQL 的默认事务隔离级别为 Repeatable Read。而 ORACLESQLServer 等的默认隔离级别使用的是 Read Committed 模式,为什么呢?

开始我们的内容,相信大家一定遇到过下面的一个面试场景

面试官:“讲讲 mysql 有几个事务隔离级别?”

你:“读未提交,读已提交,可重复读,串行化四个!默认是可重复读”
面试官:“为什么 mysql 选可重复读作为默认的隔离级别?”
(你面露苦色,不知如何回答!)
面试官:“你们项目中选了哪个隔离级别?为什么?”
你:“当然是默认的可重复读,至于原因…呃…”
(然后你就可以回去等通知了!)

为了避免上述尴尬的场景,请继续往下阅读!
Mysql 默认的事务隔离级别是可重复读 (Repeatable Read),那互联网项目中 Mysql 也是用默认隔离级别,不做修改么?
OK,不是的,我们在项目中一般用读已提交 (Read Commited) 这个隔离级别!
what!居然是读已提交,网上不是说这个隔离级别存在不可重复读和幻读问题么?不用管么?好,带着我们的疑问开始本文!

我们先来思考一个问题,在 OracleSqlServer 中都是选择读已提交 (Read Commited) 作为默认的隔离级别,为什么 Mysql 不选择读已提交 (Read Commited) 作为默认隔离级别,而选择可重复读 (Repeatable Read) 作为默认的隔离级别呢?

Why?Why?Why?

这个是有历史原因的,当然要从我们的主从复制开始讲起了!
主从复制,是基于什么复制的?
是基于 binlog 复制的!这里不想去搬 binlog 的概念了,就简单理解为 binlog 是一个记录数据库更改的文件吧~
binlog 有几种格式?
OK,三种,分别是:

  • statement: 记录的是修改 SQL 语句
  • row:记录的是每行实际数据的变更
  • mixed:statement 和 row 模式的混合

Mysql 在 5.0 这个版本以前,binlog 只支持 STATEMENT 这种格式!而这种格式在读已提交 (Read Commited) 这个隔离级别下主从复制是有 bug 的,因此 Mysql 将可重复读 (Repeatable Read) 作为默认的隔离级别!
接下来,就要说说当 binlogSTATEMENT 格式,且隔离级别为读已提交 (Read Commited) 时,有什么 bug 呢?如下图所示,在主 (master) 上执行如下事务

阅读全文 »

首先,我们来看一下Eureka的架构图,有一个整体的认识。传送门:https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance

http://static.cyblogs.com/eureka_architecture.png

Eureka核心功能

服务注册

在微服务启动时,首先,服务提供者需要将自己的服务注册到服务注册中心,服务提供者在启动的时候会发送REST请求将自己注册到服务注册中心上,并带上一些元信息。服务注册中心接收到REST请求,会将元信息存储在一个双层Map中,第一层key是服务名,第二层key是具体服务的实例名。

注意:在服务注册时,需要确认一下eureka.client.register-with-eureka=true是否正确,如果为false是禁止向服务注册中心注册的。

服务同步

当服务成功的注册到了注册中心之后,由于注册中心可能是高可用的集群,那么我们的服务可能只注册到了一个集群中的一个注册中心上,被一个注册中心所维护,而不被另外一个注册中心所维护,那么这个时候,我们就需要将这个注册中心的信息同步给集群中其他的注册中心,这就叫服务同步。那么他是如何实现的呢?

由于在集群中,一个注册中心互为其他注册中心的服务,当服务提供者请求到一个服务注册中心后,它会将请求转发到其他服务注册中心,实现注册中心之间的服务同步。

通过服务同步,服务提供者的服务信息可以通过集群中的任何一个服务注册中心获取。

阅读全文 »

分支管理

总览(一张流程图给大家先镇镇惊)

http://static.cyblogs.com/git分支总图概览.jpg

它主要体现了Git对我们源代码版本的管理。

(转载者加)一般情况:

  • masterdevelop并行。
  • master上始终是最稳定的代码,develop是正在开发的代码。
  • feature则是某个开发为了自己的功能拉的分支。
    不一般情况:
  • develop正在开发,如果你上线突然被拒绝了,这时候就要从master上开一个热分支,或者release分支也行,改好之后在分别合并到其他分支。但,本人感觉release通常意味着终止。别在从release上拉分支了。

为何是Git?

​ 对于Git与其他集中式代码管理工具相比的优缺点的全面讨论,请参见这里。这样的争论总是喋喋不休。作为一个开发者,与现今的其他开发工具相比较,我更喜欢GitGit真得改变了开发者对于合并和分支的思考。我曾经使用经典的CVS/Subversion,然而每次的合并/分支和其他行为总让人担惊受怕(“小心合并里的冲突,简直要命!”)。
但是对于Git来说,这些行为非常简单和搞笑,它们被认为是日常工作中的核心部分。例如,在很多CVS/Subversion书里,分支与合并总是在后面的章节中被讨论(对于高级用户使用),然而在每个Git书中,在第3章就已经完全涵盖了(作为基础)。
简单和重复的特性带来的结果是:分支与合并不再是什么可以害怕的东西。分支/合并被认为对于版本管理工具比其他功能更重要。
关于工具,不再多说,让我们直接看开发模型吧。这个模型并不是如下模型:在管理软件开发进度方面,面对每个开发过程,每个队员必须按一定次序开发。

分布式而非集中式

​ 对于这种分支模型,我们设置了一个版本库,它运转良好,这是一个”事实上” 版本库。不过请注意,这个版本库只是被认为是中心版本库(因为Git是一个分布式版本管理系统,从技术上来讲,并没有一个中心版本库)。我们将把这个版本库称为原始库,这个名字对所有的Git用户来说都很容易理解。

阅读全文 »

一.简介

简单的说,RedisTemplateStringRedisTemplate的关系如下:

1.StringRedisTemplateRedisTemplate的子类。

2.StringRedisTemplate的各个序列化策略都是StringRedisSerializer,而RedisTemplate用的是JdkSerializationRedisSerializer

二.RedisTemplate和StringRedisTemplate的代码结构

RedisTemplate类说起。

RedisTemplate类中,定义了这样四个变量:

1
2
3
4
5
6
7
8
@Nullable
private RedisSerializer keySerializer = null;
@Nullable
private RedisSerializer valueSerializer = null;
@Nullable
private RedisSerializer hashKeySerializer = null;
@Nullable
private RedisSerializer hashValueSerializer = null;

分别代表了普通keyvalue,和Hash类型的key,value的序列化策略,可以分别设置。

另外定义变量,用来指定默认的序列化策略:

1
2
@Nullable
private RedisSerializer<?> defaultSerializer;
阅读全文 »

使用新版idea创建了一个springboot项目,运行build时发生如下报错。

1
2
3
4
5
6
Error:(3, 32) java: 程序包org.springframework.boot不存在
Error:(4, 46) java: 程序包org.springframework.boot.autoconfigure不存在
Error:(5, 40) java: 程序包org.springframework.boot.builder不存在
Error:(6, 52) java: 程序包org.springframework.boot.web.servlet.support不存在
Error:(9, 34) java: 找不到符号
符号: 类 SpringBootServletInitializer

http://static.cyblogs.com/QQ20201107-080822@2x.jpg

诡异的是,使用mvn打包却是正常的,能生成正确的jar并且能通过命令行启动 后来通过查阅资料得知新版IDEA需要在Setting里将 delegate IDE build/run actions to Maven勾选上即可。

http://static.cyblogs.com/QQ20201107-081022@2x.jpg

其它

在解决这个问题的过程中,还了解到使用spring-boot-maven-plugin这个插件打的jar的结构会和普通插件maven-jar-plugin的有些不一样。spring-boot-maven-plugin将所有应用启动运行所需的jar全部包含进来了,具备独立运行的条件。

1
2
3
4
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

spring-boot-maven-plugin打包的jar目录:

http://static.cyblogs.com/QQ20201107-081147@2x.jpg

阅读全文 »

前言

使用spring开发时,进行配置主要有两种方式,一是xml的方式,二是java config的方式。spring技术自身也在不断的发展和改变,从当前springboot的火热程度来看,java config的应用是越来越广泛了,在使用java config的过程当中,我们不可避免的会有各种各样的注解打交道,其中,我们使用最多的注解应该就是@Autowired注解了。这个注解的功能就是为我们注入一个定义好的bean。那么,这个注解除了我们常用的属性注入方式之外还有哪些使用方式呢?它在代码层面又是怎么实现的呢?这是本篇文章着重想讨论的问题。

@Autowired注解用法

在分析这个注解的实现原理之前,我们不妨先来回顾一下@Autowired注解的用法。

@Autowired注解应用于构造函数,如以下示例所示

1
2
3
4
5
6
7
8
9
10
11
public class MovieRecommender {

private final CustomerPreferenceDao customerPreferenceDao;

@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}

// ...
}

@Autowired注释应用于setter方法

1
2
3
4
5
6
7
8
9
10
11
public class SimpleMovieLister {

private MovieFinder movieFinder;

@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

// ...
}

@Autowired注释应用于具有任意名称和多个参数的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MovieRecommender {

private MovieCatalog movieCatalog;

private CustomerPreferenceDao customerPreferenceDao;

@Autowired
public void prepare(MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}

// ...
}
阅读全文 »

方式一

如果用普通的key/value结构来存储,主要有以下2种存储方式:

第一种方式将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,

如:

1
2
3
4
set u001 "李三,18,20010101" 

127.0.0.1:6379> get u001
"\xe6\x9d\x8e\xe4\xb8\x89,18,20010101"

这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。

方式二

第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,

如:

1
2
3
4
5
6
7
8
mset user:001:name "李三" user:001:age 18 user:001:birthday "20010101"

127.0.0.1:6379> get user:001:name
"\xe6\x9d\x8e\xe4\xb8\x89"
127.0.0.1:6379> get user:001:age
"18"
127.0.0.1:6379> get user:001:birthday
"20010101"
阅读全文 »
0%