0%

设置root账户密码

1
2
3
4
5
# 修改root密码
sudo passwd root

# 切换到root账户
su root

查看ip

1
2
3
4
5
# 安装网络工具
apt install net-tools

# 查看ip
ifconfig

允许远程

1
2
3
4
5
6
# 将 PermitRootLogin的值 设置为 yes
vim /etc/ssh/sshd_config
PermitRootLogin yes

# 重启ssh
systemctl restart ssh

运行Sql Server(Docker)

Docker Sql Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 拉取镜像
docker pull mcr.microsoft.com/mssql/server:2017-latest

# 运行容器
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=Password@001" \
-p 1433:1433 --name sql1 --hostname sql1 \
-d \
mcr.microsoft.com/mssql/server:2017-latest

# 进入容器
docker exec -it sql1 bash

# 验证可以进入数据库
/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P Password@001
1
2
3
4
5
6
7
# 创建数据库 TestDB
CREATE DATABASE TestDB
GO

# 查询所有数据库名称
SELECT Name from sys.Databases
GO

下载Sql Server数据库管理工具

https://learn.microsoft.com/zh-cn/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-ver16

连接调测

1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution:
def minimumWhiteTiles(self, floor: str, numCarpets: int, carpetLen: int) -> int:
# 【定义状态】dp[i][j]:使用i条地毯覆盖长度为j的砖块,没被覆盖的白色砖块的最少数目
dp = [[0]*(len(floor)+1) for _ in range(numCarpets+1)]

for i in range(numCarpets+1):
for j in range(1, len(floor)+1):
if i == 0:
# 未使用地毯时,没被覆盖的白色砖块 即 地板上白色砖块的数目
dp[i][j] = dp[i][j-1] + int(floor[j-1])
continue

# 使用地毯时,分2种情况讨论:
# (1)第j块砖块没被覆盖时,没被覆盖的白色砖块 = 使用i条地毯覆盖长度为j-1的砖块时的最少白块数目+第j块是否为白块
# (2)第j块砖块被覆盖时,没被覆盖的白色砖块 = 使用i-1条地毯覆盖长度为j-carpetLen的砖块
# (若j<=carpetLen,则j块砖全部被覆盖,没被覆盖的白色砖块为0)
dp[i][j] = min(
dp[i][j-1] + int(floor[j-1]),
j > carpetLen and dp[i-1][j-carpetLen] or 0
)

return dp[numCarpets][len(floor)]

版本

1
2
3
4
5
6
celery                4.4.2
django 1.11.29
django-redis 4.5.0
django-redis-sentinel 1.0
redis 3.2.0
python 2.7

工程关键代码

Django setting文件

Cache配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "mymaster/10.211.55.4:26379/2",
"OPTIONS": {
"CLIENT_CLASS": "django_redis_sentinel.SentinelClient",
"CONNECTION_POOL_KWARGS": {
"max_connections": 100
}
}
},
"trace": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "mymaster/10.211.55.4:26379/3",
"OPTIONS": {
"CLIENT_CLASS": "django_redis_sentinel.SentinelClient",
"CONNECTION_POOL_KWARGS": {
"max_connections": 100
}
}
}
}

Celery配置

1
2
3
4
5
6
7
8
9
10
BROKER_BACKEND = 'redis'
MASTER_NAME = 'mymaster'
CELERY_BROKER_URL = 'sentinel://10.211.55.4:26379/0'
CELERY_BROKER_TRANSPORT_OPTIONS = {'master_name': MASTER_NAME}
CELERY_RESULT_BACKEND = 'sentinel://10.211.55.4:26379/1'
CELERY_RESULT_BACKEND_TRANSPORT_OPTIONS = {'master_name': MASTER_NAME}
CELERY_RESULT_EXPIRES = 10
CELERY_MAX_CACHED_RESULTS = 2
CELERY_TIMEZONE = 'Asia/Shanghai'
CELERY_ENABLE_UTC = True

Django Redis使用

使用Cache

1
2
3
4
from django.core.cache import caches
cache1 = caches['trace']
cache1.set('cache_default', dict(name='cache_default'), 60)
cache1.get("cache_default")

使用Celery

1
2
celery -A LearningPlatformPy2 worker --loglevel=info
celery -A LearningPlatformPy2 beat --loglevel=info

关键命令

1
2
3
4
5
6
7
8
# 客户端访问redis服务
redis-cli -h 10.211.55.4 -p 6379

# 启动指定配置文件的redis服务
redis-server redis.conf

# 停止指定的redis服务
redis-cli -h 10.211.55.4 -p 6379 shutdown

贪心:尽可能选择个数多的字符

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
class Solution:
def __init__(self) -> None:
self.char_with_cnt = None

def longestDiverseString(self, a: int, b: int, c: int) -> str:
ret = ''

self.char_with_cnt = [['a', a], ['b', b], ['c', c]]
ignore_char = None
while True:
next_char = self.get_next_char(ignore_char)
if not next_char:
break
self.char_with_cnt[ord(next_char)-ord('a')][1] -= 1

ret += next_char
ignore_char = (len(ret) >= 2 and ret[-2] == ret[-1]) and ret[-1] or None

return ret

def get_next_char(self, ignore_char):
available_char_list = sorted(
[node for node in self.char_with_cnt if node[0]!=ignore_char and node[1]>0],
key=lambda item: -item[1]
)
if not available_char_list:
return None
return available_char_list[0][0]

引言:NoSQL

什么是NoSQL

Not Only SQL,不仅仅是SQL,泛指非关系型数据库

  • 关系型数据库:操作数据时,使用sql(sql server、mysql等)

  • 非关系型数据库:操作数据时,不是使用sql,有自己的语法

为什么会出现NoSQL

随着业务的发展,关系型数据库面对大规模和高并发的动态网站,出现了实现复杂、性能低的问题。

举例:

  • 商城网站中对商品的频繁查询
  • 热搜排行榜
  • 订单超时问题
  • 音频、视频存储

NoSQL四大分类

键值(Key-Value)存储数据库

  • 说明:主要使用一个哈希表,表中由键值对组成(类比python中对字典)

  • 特点:Key/Value模型优势在于简单、易部署

  • 产品:Redis

列存储数据库

  • 说明:用于分布式存储海量数据(十亿、百亿级数据)

  • 特点:键仍然存在,但特点是指向了多个列

  • 产品:HBase

文档型数据库

  • 说明:以特定格式存储数据,比如:json。比键值数据库查询效率更高

    1
    2
    3
    # 数据举例
    {id: 1, name: Zhangsan, age:10, score:100}
    {id: 2, name: Lisi}
  • 特点:以文档形式存储

  • 产品:MongoDB

图形(Graph)数据库

  • 说明:数据以灵活的图形模型进行存储
  • 特点:没有标准的查询语言,通过接口进行查询
  • 产品:Neo4J

NoSQL应用场景

  • 数据模型简单
  • IT系统对 数据灵活性 要求比较高
  • 对数据库性能要求较高
  • 不需要高度的数据一致性(比如:排行榜,数据错1~2个没关系)

Redis介绍

什么是Redis

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。

Redis 数据在内存中

  • 优点:读写快
  • 缺点:断电消失
  • 解决断电消失的方案:持久化机制(内存数据会定期写入到硬盘中)

Redis特点

  • 高性能key/value内存型数据库

  • 支持丰富的数据模型(String、List、Set等)

  • 支持持久化(内存数据会定期写入到硬盘中)

  • 单进程、单线程 (线程安全,应用场景:分布式锁)

    “分布式锁” 应用场景说明:

    多个请求同时访问共享数据时,可能导致数据不同步

    =》加threading.Lock锁

    但当server部署在多台服务器上时,请求可能访问任意一台服务器上的server,但此时threading.Lock锁会只对其所在的server生效,无法解决多台服务器上的数据不同步的问题

    =》加分布式锁

Redis安装

Ubuntu安装Redis

1
2
apt update
apt install redis-server

Windows安装Redis

官方不提供Windows安装包,需要到github上获取安装包(比如:微软提供的安装包),本地解压即可使用

Redis使用前的准备

修改redis配置文件

1
vim /etc/redis/redis.conf

需要修改的配置信息

1
2
# 允许来自任意一台PC上的客户端访问
bind 0.0.0.0

Windows可视化工具

Redis Desktop Manager

rdm

Redis基本操作

l
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
44
45
46
# 客户端连接redis服务端
redis-cli -h 10.211.55.4


# 选择库(默认16个库,编号:0~15,默认使用0号库):select dbid(库编号)
# 注:库的个数可以通过修改配置文件进行调整。修改配置文件(/etc/redis/redis.conf)中的 databases 对应的值
select 0


# String类型
## 设置
set name Zhangsan
## 获取
get name


# List类型(有序 可重复)
## 设置
lpush nameList name1 name2 name3
## 获取
lrange nameList 0 -1


# Set类型(无序 不可重复)
## 设置
sadd nameSet name1 name2 name3 name2
## 获取
smembers nameSet


# ZSet类型(可排序的set集合,排序 不可重复)
## 设置
zadd nameZSet 85 name1 100 name2 80 name3 90 name1
## 获取
zrange nameSet 0 -1 withscores # 默认升序


# Hash类型(value是 键值结构)
## 设置
hset studentHash name Zhangsan
## 获取
hget studentHash name


# 为key设置生存时间(单位:秒),当key过期时,会自动删除
expire name 10

Redis应用场景举例

  • 具有时效性的功能

    • 验证码:超过60s失效
    • 未付款的订单:超过2h未付款,自动取消
    • token信息(安全令牌)
  • 排行榜功能

  • 分布式缓存

    将频繁被查询的信息,放到缓存中。请求优先从缓存中读取数据,如果没有再读数据库

  • 分布式集群系统中,Session共享

    (Session:会话信息,存储客户端的身份信息,告诉web服务器当前请求是属于哪个会话的,用于区分用户)

  • 分布式集群系统中,分布式锁

Redis持久化机制(2种)

什么是持久化

将内存中的数据写入到磁盘中

【持久化机制1】快照

特点

保存某一时刻的所有数据到磁盘(默认开启),默认保存到文件后缀.rdb,故也被称为RDB方式

(.rdb文件默认存储位置:/var/lib/redis/dump.rdb)

2022-01-16 23.45.24

生成方式

  • 客户端触发(2种方式)

    • BGSAVE:fork一个子进程创建快照,父进程继续处理命令请求

      2022-01-17 00.00.24

    • SAVE(不常用):主进程负责创建快照,创建过程中不能处理命令请求

      2022-01-17 00.09.49

  • 服务器自动配置触发

    配置文件(/etc/redis/redis.conf)中 配置save的触发条件(触发BGSAVE)

缺点

在t1时刻创建了快照,但是在t2时刻宕机,则 t1~t2 之间的数据丢失

【持久化机制2】AOF

特点

AOF(append only file),将所有客户端的写命令依次记录到AOF文件中。

当需要恢复数据时,redis会执行一次AOF文件所包含的所有写命令。

2022-01-17 01.08.49

生成方式

在配置文件(/etc/redis/redis.conf)中,开启AOF持久化(默认没有开启)

1
2
3
4
# 配置文件中 “持久化” 选项
appendonly yes # 开启持久化
appendfilename "appendonly.aof" # 指定持久化文件名称
appendfsync everysec # 指定追加频率

关于追加频率,有3种选项

  • always(谨慎使用)
    • 说明:每个redis写命令都要同步写入磁盘。
    • 优点:在系统崩溃时,丢失的数据减少到最小
    • 缺点:由于频繁的对硬盘进行写入操作,会严重降低redis速度;对于固态硬盘,可能会引发 “写入放大”问题,导致固态硬盘寿命由原来的几年降低为几个月
  • everysec(推荐)
    • 说明:每秒执行一次同步,将多个命令写入磁盘。
    • 优点:兼顾了数据安全和写入性能,redis 每秒同步一次AOF文件时的性能 和 不使用任何持久化特性时的性能 相差无几
    • 缺点:在系统崩溃时,最多丢失1秒内产生的数据
  • no(不推荐)
    • 说明:由操作系统决定何时同步
    • 优点:不会对redis性能产生影响
    • 缺点:系统崩溃时,丢失的数据不可控;当缓存了大量的写命令时,触发同步写入磁盘,redis处理命令速度会变慢

缺点

AOF文件占用大量磁盘空间

修改key为name的值10000次,会在AOF文件中记录10000条写入命令,导致AOF文件变得很大。

AOF重写

只记录最后一次修改key为name的值就好了,可以减少AOF文件的体积

触发重写的方式
  • 客户端触发重写

    执行 BGREWRITEAOF 命令,不会阻塞redis服务

  • 服务器配置自动触发

    1
    2
    3
    4
    5
    # 修改配置文件(/etc/redis/redis.conf)中的参数,触发自动重写
    # eg:*64MB --> 20M --> *40MB --> 18MB --> *36MB
    # 默认的配置:AOF文件体积越小,触发重写频率越高;体积越大,触发重写频率越低
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb
原理

重写AOF文件的操作,并没有读取旧的文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的AOF文件,再替换原有的AOF文件

总结

  • 2种持久化可以同时使用(在redis启动时,会使用AOF文件恢复数据),也可以单独使用
  • 除了将数据持久化到硬盘,建议在不同的地方备份

Redis主从复制架构

是什么

Redis数据的冗余备份

架构&&原理

2022-01-21 00.06.47

Master节点数据变化时, Slave1、Slave2 触发数据同步。

(Slave节点只读,不允许写)

搭建方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1、准备3台服务器并修改Redis配置
# Master 修改配置文件(/etc/redis/redis.conf)中的参数
port 6379
bind 0.0.0.0

# Slave1
port 6380
bind 0.0.0.0
replicaof MasterIp MasterPort

# Slave2
port 6381
bind 0.0.0.0
replicaof MasterIp MasterPort


# 2、启动3台服务器Redis服务
systemctl start redis-server.service

新的问题

主从复制架构只完成了备份。当Master节点挂掉时,Redis无法对外提供服务。

Redis哨兵机制

哨兵Sentinel机制

问题

当Master节点挂掉时,Redis无法对外提供服务。

目的

使Redis高可用

目标

支持自动故障转移功能

解决方案

由一个或多个Sentinel监听任意多个Master服务器、以及这些Master服务器属下的所有Slave服务器。

当被监视的Master服务器进入下线状态时,自动将下线Master属下的某个Slave服务器升级为新的Master服务器。

架构&&原理

2022-01-23_19.36.12

推荐使用多个Sentinel进行监听,原因:

若由于网络延迟,导致Sentinel误认为Master节点宕机,则会新推荐出一个Master,导致出现2个Master(脑裂)。

若存在多个Sentinel,会共同来判断原Master是否已宕机,以决定是否推荐出一个新的Master,降低脑裂发生的概率。

搭建方式

1
2
3
4
5
6
7
8
9
# 1、安装redis-sentinel
apt install redis-sentinel

# 2、配置哨兵,在sentinel.conf(/etc/redis/sentinel.conf)文件中填入内容
# ip:Master节点ip; port:Master节点port; 1:哨兵的个数
sentinel monitor 被监控的主从架构的名字(自定义) ip port 1

# 3、启动哨兵模式进行测试
redis-sentinel /etc/redis/sentinel.conf

新的问题

哨兵机制只完成了自动故障转移功能,还有2个问题无法解决:

单节点并发压力问题

当客户端并发访问量很大,一个Master节点无法支撑业务;

单节点内存和物理硬盘上限问题

由于Redis数据存储在内存中,如果数据量很大,会导致内存溢出;

由于Redis持久化保存在物理磁盘中,如果数据量很大,也会导致物理磁盘被占满。

Redis集群

是什么

Redis在3.0后开始支持Cluster(集群)模式,支持节点的自动发现、Slave-Master选举和容错、在线分片等。

架构&&原理

2022-01-24_01.07.31

详细:

1、所有Redis节点彼此互联(PING-PIONG机制),内部使用二进制协议优化传输速度和带宽

2、节点的失效:集群中超过半数的节点检测失效时,才会判为失效的(因此节点数建议为奇数个)。

3、客户端与redis节点直连,不需要中间的Proxy层,客户端不需要连接集群中所有节点,连接集群中任意一个可用节点即可

4、redis-cluster把所有节点映射到 [0, 16383] slot上,集群负责维护 node <–> slot <–> value

2022-01-24_01.45.37

参考资料

安装

下载

下载最新版本:nvim-macos.tar.gz

解压

1
tar xzvf nvim-macos.tar.gz

将解压文件夹(nvim-osx64)剪切到 /Users/sjm/Tools

配置

修改默认配置文件位置

在/Users/sjm/Tools/nvim-osx64下新建文件夹nvim

在文件夹nvim下,新建文件init.vim

指定nvim别名

执行

1
vim .zshrc

添加

1
alias nvim="XDG_CONFIG_HOME=/Users/sjm/Tools/nvim-osx64 /Users/sjm/Tools/nvim-osx64/bin/nvim"

执行

1
source .zshrc

验证nvim可用

执行

1
nvim

预期出现nvim窗口

参考

解题思路

模拟窗口滑动,利用优先队列获取窗口内的最大值

注意:避免窗口外的数据造成影响

时间复杂度

O(nlogn)

空间复杂度

O(n)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
ret = []

q = []
for i, num in enumerate(nums):
heapq.heappush(q, (-nums[i], i))
if i < k-1:
continue

# 窗口外的元素要移除
while q[0][1] <= i-k:
heapq.heappop(q)

ret.append(-q[0][0])

return ret

什么是Docker?

加速 构建、共享、运行 应用程序的方式

为什么要使用Docker?

问题1

开发时,本机测试环境可以跑,但生产环境跑不起来,很可能的原因:软件环境版本不同。
【Docker解决方案】将 “软件+应用环境” 一起打包 => 一致的运行环境,可以更轻松的构建

问题2

需要部署n台服务器,服务器软件环境可能不同、服务器多
【Docker解决方案】将 “应用+应用环境” 打包为镜像,各服务器下载镜像,运行即可

问题3

server有限,需要在一台server运行2个不同软件环境的应用
【Docker解决方案】Docker打包2个镜像,分别运行(2个应用进程隔离,互不影响)

对比虚拟机

Docker和虚拟机在物理机中的位置


从最后起到的作用看:Docker服务 <=> 虚拟机+linux/window

Docker和虚拟机对比

虚拟机 Docker
磁盘 xG~xxG(安装虚拟机+os) xxM~xxxM(安装docker)
部署 每次费时费力 从第2次部署便捷
启动速度 x分钟(开机->运行项目) x秒(开启容器->运行项目)
CPU、内存占用 高(运行虚拟机+os) 极低(运行Docker服务)
运行速度 慢(调度:应用->linux/windows->虚拟机->os) 快(调度:应用->docker->os)
耦合性 多个app安装在一起,容易相互影响 一个app一个容器,实现隔离
系统依赖 linux内核

Docker核心概念

镜像

一个镜像 相当于 一个软件
特点:只读

容器

基于某个镜像运行一次就生成一个程序实例,一个程序实例即为容器
特点:可读可写

仓库

存储Docker中镜像的位置
远程仓库:远程服务器存储镜像的位置
本地仓库:当前自己本地存储镜像的位置

Dockerfile

定义

构建镜像的文件

为什么需要Dockerfile

虽然官方提供很多镜像,但Dockerfile可以使用户将自己的应用打包为镜像,这样就可以以容器的形式运行自己的应用

Docker架构图

Docker镜像分层原理

镜像

软件运行环境 + 软件
包含:代码、运行时所需的库、环境变量、配置文件

镜像为什么这么大?

image = 自身软件+软件自身依赖+os依赖

镜像为什么分层?


Docker在设计镜像时,每一个镜像都是由n个镜像共同组成,镜像像花卷一层层组成(UnionFs联合文件系统)
通过分层:多个镜像可以实现基础镜像的共享,从而减小远程/本地仓库整体体积

Docker常用命令

镜像命令

查看本地仓库有哪些镜像

1
docker images

下载新镜像

docker pull 镜像名称:版本号

1
docker pull mysql:8.0.26

删除镜像

1
docker rmi mysql:8.0.26

容器命令

运行容器

1
docker run --name ubuntu_v1 -id ubuntu:20.0

查看正在/历史运行的容器

1
docker ps -a

进入容器

1
docker exec -it 容器ID bash

启动|停止|重启容器

1
2
3
docker start 容器ID
docker stop 容器ID
docker restart 容器ID

删除容器

1
docker rm 容器ID

参考资料

解体思路

到达第i个房屋,可以偷窃到的最高金额
dp[i] = max(dp[i-2] + nums[i], dp[i-1])

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution:
def rob(self, nums: List[int]) -> int:
nums_len = len(nums)
dp = [0] * nums_len

dp[0] = nums[0]
if nums_len == 1:
return dp[0]

dp[1] = max(nums[0], nums[1])
if nums_len == 2:
return dp[1]

for i in range(2, nums_len):
dp[i] = max(dp[i-2] + nums[i], dp[i-1])

return dp[nums_len-1]