LINUX
gdb 传入main函数参数:
gdb --args 可执行程序名 参数1 参数2 参数3…
set args abc 123
break 10 if args==4
Docker
Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。
Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。
容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。
docker 容器本质:
容器其实就是Linux下一个特殊的进程;
Docker容器通过namespace实现进程隔离通过cgroups实现资源限制;
Docker镜像(rootfs)是一个操作系统的所有文件和目录而不包括内核,Docker镜像是共享宿主机的内核的;
Docker镜像是以只读方式挂载,所有的增删改都只会作用在容器层, 但是相同的文件会覆盖掉下一层,这种方式也被称为"Copy-on-write";
# 启动docker
systemctl start docker
# 关闭docker
systemctl stop docker
# 重启docker
systemctl restart docker
docker pull 镜像名
# 查看镜像列表(查)
docker images
docker search --filter=STARS=9000 mysql 搜索 STARS >9000的 mysql 镜像
# 保存镜像(存)
docker save 镜像名/镜像ID -o 镜像保存在哪个位置与名字
docker save tomcat -o /myimg.tar
# 加载镜像(增)
docker load -i 镜像保存文件位置
docker load -i myimg.tar
# 删除一个
docker rmi -f 镜像名/镜像ID
镜像进行分类或者版本迭代操作
app:1.0.0 基础镜像
# 分离为开发环境
app:develop-1.0.0
# 分离为alpha环境
app:alpha-1.0.0
# 从外部 拷贝文件到容器内
docker cp 容器外路径 容器ID/名称: 容器内路径
# ----------------查看容器日志
docker logs -f --tail=要查看末尾多少行 默认all 容器ID
docker logs -f -t --tail 1000 2ab447816a66
运行:
docker run --name mysql \
-v /myapp/mysql:/var/lib/mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \ 设置环境变量,这里设置的是root密码
-d mysql:8.0.19 后台运行容器
删除:
# 停止运行的 redis 容器
docker stop 容器名/容器ID
#删除一个容器
docker rm -f 容器名/容器ID
#进入容器(方式一)
docker exec -it 容器名/容器ID /bin/bash
# 直接退出 (如果没有添加-d 参数(持久化运行容器) 该容器会被关闭 )
exit
# 优雅退出 (无论是否添加-d 参数 容器都不会被关闭)
Ctrl + p + q
# 构建镜像 (需要在Dockerfile同级目录下构建)
docker build -t cat:1.0 .
# 说明(-t:设置 镜像的名字及tag)(最后的. 为当前目录)
主键索引,普通索引,唯一索引unique,联合索引,全文索引,哈希索引 等值查询
聚簇索引 唯一决定了数据的物理布局顺序 INDEX或PRIMARY KEY
索引失效:查询条件与索引不匹配;使用不等于(!=)或不在范围(<>);使用函数或运算符在索引列上 like;使用OR操作符连接多个索引列,只有第一个索引列会被使用;复合索引顺序使用不当;索引被删除或修改
mysql索引 底层数据结构,为什么会失效
TCP与UDP区别总结:
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保 证可靠交付
3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
重传是TCP为了保证数据的可靠性而采用的机制。在数据传输过程中,如果发送方发送了一个数据包,但没有收到接收方的确认(ACK),发送方会等待一段时间,然后重新发送该数据包。这个过程称为重传。
重传机制确保了即使数据包在传输过程中丢失或损坏,也能够被重新发送,从而保证数据的完整性。如果重传多次后仍然没有收到确认,发送方会根据不同的情况采取不同的措施,比如超时重传或选择性重传。
总结来说,三次握手用于建立TCP连接,而重传机制用于在数据传输过程中确保数据的可靠性。这两个机制共同作用,使得TCP能够提供可靠的、面向连接的通信服务。
超时重传 选择性重传
在Python中处理高并发通常涉及以下几个方面:
1. **使用异步编程**:
- Python 3.4及以上版本引入了`asyncio`库,用于编写异步代码。
- 使用异步编程可以充分利用多核CPU,提高处理并发请求的效率。
2. **使用并发库**:
- `concurrent.futures`:提供了一个简单的异步编程接口,可以利用多线程和多进程来处理并发任务。
- `gevent`:一个基于协程的并发库,可以简化异步编程。
3. **使用Web框架的异步支持**:
- `Flask`:可以使用`Flask-SocketIO`或`Flask-Gevent`来支持异步处理。
- `Django`:可以通过使用`daphne`和`asgi_redis`等库来支持异步处理。
4. **使用消息队列**:
- 如`RabbitMQ`、`Kafka`或`Redis`,可以用来解耦高并发的请求和处理这些请求的服务。
5. **使用缓存**:
- 如`Redis`或`Memcached`,可以用来缓存热点数据,减少数据库的访问压力。
6. **使用数据库连接池**:
- 如`MySQL-Connector-Python`中的`connections`参数,可以减少数据库连接和断开的开销。
7. **使用限流和队列**:
- 如`Flask-Limiter`或`Ratelimit`,可以用来限制客户端对服务的访问频率。
- 使用队列如`celery`的队列可以处理大量的异步任务。
8. **使用多进程或线程**:
- 对于IO密集型任务,可以使用多进程。
- 对于CPU密集型任务,可以使用多线程。
9. **使用无服务器架构**:
- 如`AWS Lambda`或`Google Cloud Functions`,可以处理大量并发请求,而不需要管理服务器。
10. **使用云服务**:
- 如`AWS`、`Google Cloud`或`Azure`提供的服务,可以轻松扩展处理高并发的需求。
通过结合使用这些技术和策略,可以有效地处理Python中的高并发场景。选择最适合你的应用场景和需求的方法是关键。
Python知识点
列表和元组用于存储有序的元素序列,列表可变,元组不可变。
集合用于存储无序的、不重复的元素集合。
字典用于存储键值对,通过键来访问值。
字符串用于存储字符序列,是不可变的。
浅拷贝适用于大多数对象,但对于某些特殊对象(如列表中的列表改了会同步),浅拷贝可能会导致不可预料的结果。
深拷贝适用于需要完全独立副本的情况,但它比浅拷贝更耗时,因为它需要复制对象及其引用的所有对象。
在Python中,深拷贝通常使用copy.deepcopy()函数,而不是通过序列的切片操作。切片操作创建的是浅拷贝。
1.生成器本身是一种特殊的迭代器,也就是说生成器就是迭代器。
2.生成器会自动实现迭代器协议,也就是说只要我们yield后,自动就生成了next对象包括StopIteration等结构。
3.生成器使用yield语句返回一个值。yield语句挂起该生成器函数的状态,保留足够的信息。对生成器函数的第二次(或第n次)调用,跳转到函数上一次挂起的位置。生成器不仅“记住”了它的数据状态,生成还记住了程序执行的位置。
二者区别
1.迭代器是访问容器的一种方式,也就是说容器已经出现。我们是从已有元素拓印出一份副本,只为我们此次迭代使用。而生成器则是,而生成器则是自己生成元素的。也就是前者是从有到有的复制,而后者则是从无到有的生成。
- 在用法上生成器只需要简单函数写法,配合yield就能实现。而迭代器真正开发中很难使用到。我们可以把生成器看做,python给我们提供的特殊接口实现的迭代器。
装饰器是给现有的模块增添新的小功能,可以对原函数进行功能扩展,而且还不需要修改原函数的内容,也不需要修改原函数的调用。闭包函数
装饰器修饰完的函数,在执行的时候先执行原函数的功能,然后再由里到外依次执行装饰器的内容。
POST请求
HTTP协议中,POST请求是一种常见的HTTP方法,用于传输数据到服务器。POST请求可以传输多种类型的数据
1. **表单数据**:
- 最常见的POST请求类型,用于发送表单数据。
- 数据通常被编码为URL编码格式(application/x-www-form-urlencoded),也可以使用其他格式,如多部分/表单数据(multipart/form-data)。
2. **JSON数据**:
- JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。
- POST请求可以发送JSON数据,通常使用application/json作为Content-Type头部。
3. **XML数据**:
- XML(eXtensible Markup Language)是一种用于传输和存储数据的标记语言。
- POST请求可以发送XML数据,通常使用application/xml或text/xml作为Content-Type头部。
4. **文件上传**:
- 用于上传文件到服务器。
- 通常使用multipart/form-data作为Content-Type头部,以支持文件和表单数据的传输。
5. **二进制数据**:
- 用于传输非文本数据,如图片、视频等。
- 可以使用application/octet-stream作为Content-Type头部。
6. **自定义数据格式**:
- 根据具体需求,POST请求可以传输任何自定义格式的数据。
- 服务器需要能够处理并解析这些数据格式。
FastAPI高并发
数据库连接池: 如果你正在使用数据库,确保你使用了连接池来管理数据库连接。连接池允许你预先建立并缓存数据库连接,从而避免了在高并发场景下频繁地建立和关闭连接的开销。
缓存: 对于不经常变化的数据,你可以使用缓存来减少数据库查询或计算的开销。FastAPI 可以与各种缓存解决方案(如 Redis、Memcached 等)集成。
负载均衡: 在高并发的生产环境中,使用负载均衡器(如 Nginx、HAProxy 等)将请求分发到多个 FastAPI 实例上是非常常见的做法。这不仅可以提高吞吐量,还可以提供容错和扩展性。
水平扩展: 如果你发现单个 FastAPI 实例无法满足你的需求,你可以考虑水平扩展,即增加更多的 FastAPI 实例来处理请求。这可以通过在负载均衡器后面添加更多的服务器来实现。
优化代码: 避免在路由处理函数中执行不必要的计算或 I/O 操作。使用性能分析工具(如 Py-Spy、cProfile 等)来识别并优化性能瓶颈。
监控和日志: 使用监控工具(如 Prometheus、Grafana 等)来监控你的 FastAPI 应用程序的性能指标(如请求响应时间、吞吐量、错误率等)。同时,确保你的应用程序生成了足够的日志,以便在出现问题时可以快速定位和解决问题。
更新和打补丁: 保持你的 FastAPI 和相关依赖项(如数据库驱动程序、中间件等)的更新。这些更新通常包含性能改进、安全修复和新功能。
限制和节流: 使用节流和限制策略来保护你的应用程序免受恶意请求或 DDoS 攻击的影响。例如,你可以限制来自单个 IP 地址的请求频率,或者限制请求的大小和复杂性。
测试: 编写性能测试来模拟高并发场景,并测量你的 FastAPI 应用程序的性能。这可以帮助你识别并解决潜在的性能问题。
Pydantic 模型
在 FastAPI 中,可以将 Pydantic 模型用作请求体(Request Body),以自动验证和解析客户端发送的数据。
Pydantic 模型还可以用于验证查询参数、路径参数等。
使用 Pydantic 模型的一个重要优势是,它能够自动为 FastAPI 生成交互式 API 文档。文档会包括模型的字段、类型、验证规则等信息,让开发者和 API 使用者能够清晰地了解如何正确使用 API
Pydantic 模型不仅提供了验证功能,还可以用于将数据转换为特定类型(例如 JSON)或反向序列化。在 FastAPI 中,这种能力是自动的,你无需手动处理。
1、依赖项(Dependencies)
依赖项是在路由操作函数执行前或后运行的可复用的函数或对象。
它们被用于执行一些通用的逻辑,如验证、身份验证、数据库连接等。在 FastAPI 中,依赖项通常用于两个方面:
预处理(Before)依赖项: 在路由操作函数执行前运行,用于预处理输入数据,验证请求等。
后处理(After)依赖项: 在路由操作函数执行后运行,用于执行一些后处理逻辑,如日志记录、清理等。
-
- 依赖注入
在编程中,依赖注入是指一个对象接收它所依赖的其他对象的机制。 其他对象称为依赖项
依赖注入是将依赖项注入到路由操作函数中的过程。
在 FastAPI 中,通过在路由操作函数参数中声明依赖项来实现依赖注入。
FastAPI 将负责解析依赖项的参数,并确保在执行路由操作函数之前将其传递给函数。
重复使用相同的共享逻辑
共享数据库连接
实施身份验证和安全功能
1.2 依赖项的使用
定义依赖项:
实例
from fastapi import Depends, FastAPI
app = FastAPI()
# 依赖项函数/类
def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
在这个例子中,common_parameters 是一个依赖项函数,用于预处理查询参数。
在路由中使用依赖项:
实例
from fastapi import Depends
# 路由操作函数
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
return commons
在这个例子中,read_items 路由操作函数中的参数 commons 使用了 Depends(common_parameters),表示 common_parameters 是一个依赖项。FastAPI 将在执行路由操作函数之前运行 common_parameters 函数,并将其返回的结果传递给 read_items 函数。
2、路径操作依赖项的基本使用
2.1 预处理(Before)
以下实例中,common_parameters 是一个依赖项函数,它接受查询参数 q、skip 和 limit,并返回一个包含这些参数的字典。
在路由操作函数 read_items 中,通过传入 Depends(common_parameters),我们使用了这个依赖项函数,实现了在路由执行前预处理输入数据的功能。
实例
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
# 依赖项函数
def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
# 路由操作函数
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
return commons
2.2 后处理(After)
以下例子中,after_request 是一个后处理函数,用于在路由执行后执行一些逻辑。
在路由操作函数 read_items_after 中,通过传入 Depends(after_request),我们使用了这个后处理依赖项,实现了在路由执行后进行额外操作的功能。
实例
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
# 依赖项函数
def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
# 路由操作函数
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
return commons
# 后处理函数
async def after_request():
# 这里可以执行一些后处理逻辑,比如记录日志
pass
# 后处理依赖项
@app.get("/items/", response_model=dict)
async def read_items_after(request: dict = Depends(after_request)):
return {"message": "Items returned successfully"}
3、多个依赖项的组合
以下例子中,common_parameters 和 verify_token 是两个不同的依赖项函数,verify_token 依赖于 common_parameters,这种组合依赖项的方式允许我们在路由执行前先验证一些参数,然后在进行身份验证。
实例
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
# 依赖项函数1
def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
# 依赖项函数2
def verify_token(token: str = Depends(common_parameters)):
if token is None:
raise HTTPException(status_code=400, detail="Token required")
return token
# 路由操作函数
@app.get("/items/")
async def read_items(token: dict = Depends(verify_token)):
return token
4、异步依赖项
依赖项函数和后处理函数可以是异步的,允许在它们内部执行异步操作。
以下例子中,get_token 是一个异步的依赖项函数,模拟了一个异步操作。
在路由操作函数 read_items 中,我们使用了这个异步依赖项函数。
实例
from fastapi import Depends, FastAPI, HTTPException
from typing import Optional
import asyncio
app = FastAPI()
# 异步依赖项函数
async def get_token():
# 模拟异步操作
await asyncio.sleep(2)
return "fake-token"
# 异步路由操作函数
@app.get("/items/")
async def read_items(token: Optional[str] = Depends(get_token)):
return {"token": token}
通过使用路径操作依赖项,你可以在路由执行前或后执行额外的逻辑,从而实现更灵活、可组合的代码组织方式。
。
FastAPI和Flask
都是Python的Web框架,但它们在设计理念、性能、适用场景等方面有所不同。
### 设计理念
- **Flask**:Flask是一个轻量级的Web框架,它基于Werkzeug WSGI工具箱和Jinja2模板引擎。Flask强调简单性和灵活性,它允许开发者通过扩展来添加更多功能。
- **FastAPI**:FastAPI是一个现代的Web框架,它旨在提供快速、简单和强大的API开发体验。FastAPI使用类型提示和自动API文档生成,以及异步支持,旨在提高开发效率和性能。
### 性能
- **Flask**:Flask是一个同步框架,它的性能受限于单个请求的处理时间。在处理大量并发请求时,性能可能会受到影响。
- **FastAPI**:FastAPI是一个异步框架,它支持使用异步编程来处理并发请求。这使得FastAPI在处理大量并发请求时能够提供更好的性能。
### 适用场景
- **Flask**:适用于中小型Web应用,特别是需要灵活性和简单性的项目。Flask也适用于需要定制化功能和复杂业务逻辑的场景。
- **FastAPI**:适用于需要高性能和高并发处理的API开发。FastAPI适合构建RESTful API、GraphQL API和WebSocket应用。
### 自动API文档生成
- **Flask**:不提供自动API文档生成功能,但可以使用Flask扩展如Flask-RESTful或Swagger UI来生成API文档。
- **FastAPI**:提供自动API文档生成功能,通过使用Urllib模块,可以快速生成API文档。
### 异步支持
- **Flask**:不原生支持异步编程,但可以使用异步中间件或使用异步扩展来实现异步支持。
- **FastAPI**:原生支持异步编程,使用异步功能可以更有效地处理并发请求。
### 类型提示
- **Flask**:不强制使用类型提示,但可以通过使用Python的类型注解来提供类型提示。
- **FastAPI**:鼓励使用类型提示,通过类型提示可以提供更好的代码可读性和错误检查。
总的来说,Flask和FastAPI都是强大的Web框架,它们各自适合不同的应用场景。Flask更适合需要灵活性和简单性的中小型Web应用,而FastAPI更适合需要高性能和高并发处理的API开发。选择哪个框架取决于项目需求和开发者偏好。
Type类型提示,异步,高性能高并发,自动API文档生成
Pydantic,BaseModel,Body,Query,Path
Form 类来处理通过提交 HTML 表单作为请求接收到的数据
FastAPI 中,通过 set_cookie() 方法在 response 响应对象上设置 cookie 参数
要在后续访问时读回 cookie,请使用 FastAPI 库中的 Cookie 对象。
response_model 的帮助下,FastAPI 将输出数据转换为模型类的结构。 它在 OpenAPI 路径操作中验证数据,为响应添加 JSON 模式。优点是,我们可以通过从模型中选择字段来格式化输出,从而将响应转换为输出模型。
数据库隔离级别
Read Uncommitted(读未提交)
这是最低的隔离级别。在这个级别,一个事务可以读取另一个未提交的事务的数据。这可能导致脏读,即读取到其他事务修改但还未提交的数据。由于这种级别的隔离性最弱,因此并发性能最高。
Read Committed(读已提交)
在这个级别,一个事务只能读取已提交的事务的数据。这避免了脏读的问题,但可能出现不可重复读的情况,即一个事务在多次读取同一数据时,由于其他事务的更新和提交,导致读取的结果不一致。
Repeatable Read(可重复读)
在这个级别,事务在其生命周期内可以多次读取同一数据,并始终看到相同的数据行,即使其他事务在此期间对它们进行了修改或删除。这避免了不可重复读的问题,但可能出现幻读的情况,即当一个事务读取某些行时,另一个事务插入新行,导致第一个事务在未来某个时间点看到额外的行。
Serializable(序列化,串行)
这是最高的隔离级别。在这个级别,事务被串行执行,避免了脏读、不可重复读和幻读的问题。每个事务完全独立于其他事务,因此性能开销最大。
在实际应用中,应根据业务需求和性能要求选择适当的隔离级别。例如,对于需要高并发的系统,可能会选择较低的隔离级别以提高性能。而对于对数据一致性要求非常高的系统,可能会选择较高的隔离级别以确保数据的完整性和准确性。
值得注意的是,不同的数据库系统可能默认使用不同的隔离级别。因此,在配置数据库时,应仔细考虑所需的隔离级别并做出适当的调整。
此外,对于已经处于运行状态的事务,更改其隔离级别可能会影响其行为和并发性能。因此,在生产环境中更改隔离级别时应谨慎操作并进行充分的测试。
总之,了解并正确选择事务的隔离级别对于确保数据库的完整性和性能至关重要。通过深入理解不同隔离级别的特性和影响,可以更好地平衡系统需求和资源消耗,从而实现更高效、更可靠的数据处理。
事务四大特征(ACID)
- 原子性(A):事务是最小单位,不可再分
- 一致性©:事务要求所有的DML语句操作的时候,必须保证同时成功或者同时失败
- 隔离性(I):事务A和事务B之间具有隔离性
- 持久性(D):是事务的保证,事务终结的标志(内存的数据持久到硬盘文件中)
Redis
(Remote Dictionary Server)是一个开源的、基于内存的数据结构存储系统,可以用作数据库、缓存和消息中间件。它支持多种类型的数据结构,如字符串、哈希、列表、集合、有序集合等,并提供了对这些数据结构的多种操作。
Redis的主要特点包括:
1. **基于内存**:Redis的数据存储在内存中,因此具有极高的读写速度,非常适合需要快速访问的应用场景。
2. **数据持久化**:虽然Redis是基于内存的,但它提供了数据持久化的功能,可以将内存中的数据保存到磁盘中,以防止数据丢失。
3. **支持多种数据结构**:Redis支持多种数据结构,可以满足不同的应用需求。例如,可以使用字符串存储简单的键值对,使用列表实现消息队列,使用哈希存储对象等。
4. **支持事务**:Redis支持事务,允许一次性执行多个命令,并保证这些命令的原子性。
5. **支持复制**:Redis支持主从复制,可以将数据从一个Redis实例复制到另一个Redis实例,实现数据的备份和扩展。
6. **支持高可用和分区**:Redis支持高可用和分区,可以通过哨兵(Sentinel)系统实现自动故障转移,通过Redis集群实现数据的分区存储。
7. **支持发布/订阅模式**:Redis支持发布/订阅消息模式,可以实现消息的发布和订阅,用于构建消息系统或实时通信应用。
Redis广泛应用于缓存、会话存储、消息队列、排行榜、社交网络等多种场景,是现代应用程序中不可或缺的一部分。由于其高性能和灵活性,Redis成为了许多开发者首选的内存数据库解决方案。
模型结构
1. dictEntry:Redis是Key-Value数据库,因此对每个键值对都会有⼀个dictEntry,⾥⾯存储了指向Key和Value的指针;next指向下⼀个dictEntry,与本Key-Value⽆关。
2. Key:图中右上⻆可⻅,Key(”hello”)并不是直接以字符串存储,⽽是存储在SDS结构中。
- redisObject:Value(“world”)既不是直接以字符串存储,也不是像Key⼀样直接存储在SDS中,⽽是存储在redisObject中。实际上,不论Value是5种类型的哪⼀种,都是通过redisObject来存储的;⽽redisObject中的type字段指明了Value对象的类型,ptr字段则指向对象所在的地址。不过可以看出,字符串对象虽然经过了redisObject的包装,但仍然需要通过SDS存储。实际上,redisObject除了type和ptr字段以外,还有其他字段图中没有给出,如⽤于指定对象内部编码的字段;后⾯会详细介绍。
4bit(类型)+4bit(编码)+24bit(lru)+4Byte(refcount)+8Byte(指针)=16Byte
SDS:
//等于 SDS 保存字符串的⻓度 int len;
//记录 buf 数组中未使⽤字节的数量 int free;
//字节数组,⽤于保存字符串 char buf[];
4. jemalloc:⽆论是DictEntry对象,还是redisObject、SDS对象,都需要内存分配器(如
jemalloc)分配内存进⾏存储。
refcount记录的是该对象被引⽤的次数,类型为整型。refcount的作⽤,主要在于对象的引⽤计数和内存回收
数据模型、存储引擎、性能和应用场景
### MySQL
MySQL是一个关系型数据库管理系统,使用SQL(结构化查询语言)进行数据操作。->电子商务网站
- **关系型数据模型**:数据以表格的形式存储,每个表格包含行和列,行代表记录,列代表字段。
- **ACID属性**:确保数据的一致性和完整性。
- **支持事务处理**: - **强大的数据支持**:支持复杂的数据查询和操作。
- **生态**:有丰富的第三方工具和库支持。
### Redis
Redis是一个开源的内存中的数据结构存储系统,可以作为数据库、缓存和消息代理使用。
高性能的缓存场景-->缓存 消息队列 它的主要特点包括:
- **非关系型数据模型**:数据以键值对的形式存储,可以存储字符串、列表、集合、哈希、有序集合等。
- **支持事务处理**:可以确保一系列操作要么全部成功,要么全部失败。
- **数据持久化**:可以将内存中的数据保存到磁盘。
- **高性能**:所有操作都是在内存中执行,因此非常快。
- **社区支持和生态系统**:有丰富的第三方工具和库支持。
### MongoDB
MongoDB是一个开源的文档数据库,支持数据存储、管理和查询。它的主要特点包括:
硬盘存储,mmap映射到内存->社交网络应用 实时数据分析 内容管理系统
- **文档型数据模型**:数据以JSON对象的形式存储,没有固定的表结构。
- **灵活的数据存储**:可以存储复杂的数据结构,包括数组、对象等。
- **支持事务处理**:从MongoDB 3.6开始,部分支持ACID事务。
- **查询语言**:使用类似JSON的查询语言,支持丰富的查询操作。
- **社区支持和生态系统**:有丰富的第三方工具和库支持。
### 应用场景
- **MySQL**:适用于需要复杂数据操作和事务处理的应用场景,如传统的关系型应用、电商系统等。
- **Redis**:适用于需要高性能的缓存、消息队列、排行榜、计数器等场景。
- **MongoDB**:适用于需要灵活数据模型和快速查询的应用场景,如实时数据分析、内容管理系统、日志系统等。
而MongoDB适合需要灵活数据模型的场景。。
Redis跳跃表
这种链表加多级索引的结构,就是跳跃表
Redis与mysql数据库缓存一致性如何保证
先删除缓存再更新数据库:
更新失败可能导致缓存和数据库的数据是一致的,但仍然是旧的数据
异步重试->如果更新成功可能会导致缓存和数据库数据不一致
我觉得可以通过分布式锁来实现,首先A删除缓存再更新数据库的时候,先建立一个分布式锁,更新数据库成功后释放锁,如果B线程没有读取到缓存,则需要去获取分布式锁,获取到锁之后才能进行读取操作,保证在没有缓存的时候,只能有一个线程操作数据库,从而解决并发操作redis的问题,这种方式的缺点是在锁操作时性能有点影响。
延时双删:
1.删除缓存;
2.更新数据库;
3.sleep N毫秒;
4.再次删除缓存。
阻塞一段时间之后,再次删除缓存,就可以把这个过程中缓存中不一致的数据删除掉
读写分离架构:
异步更新从库,拿到的数据是旧数据
此时的解决办法就是如果是对 Redis 进行填充数据的查询数据库操作,那么就强制将其指向主库进⾏查询。
删除失败了怎么办?
如果删除依然失败,则可以增加重试的次数,但是这个次数要有限制,当超出一定的次数时,要采取报错、记日志、发邮件提醒等措施。
如果第二步出现失败的情况,都可以采用重试机制解决问题。
更新数据库、再删除缓存
是影响更小的方案。先更新数据库,后删除缓存这⼀种情况也会出现问题,
删除失败,那么此时再读取缓存的时候每次都是错误的数据了。采用消息队列补偿
删除成功:最终缓存与数据库的数据是一致的,并且都是最新的数据,可能B也会读到旧数据
此时解决方案就是利用消息队列进行删除的补偿。具体的业务逻辑⽤语⾔描述如下:
请求 线程A 先对数据库进行更新操作;
在对 Redis 进行删除操作的时候发现报错,删除失败;
此时将Redis 的 key 作为消息体发送到消息队列中;
系统接收到消息队列发送的消息后再次对 Redis 进行删除操作;
但是这个方案会有⼀个缺点就是会对业务代码造成大量的侵入,深深的耦合在⼀起,所以这时会有⼀个优化的方法,我们知道对 Mysql 数据库更新操作后再 binlog 日志中我们都能够找到相应的操作,那么我们可以订阅 Mysql 数据库的 binlog 日志对缓存进行操作
多级缓存
Python垃圾回收机制
?(知道即可)
python采用的是
引用计数机制为主,
标记-清除
分代收集(隔代回收、分代回收)两种机制为辅的策略
计数机制:Python的GC模块主要运用了引用计数来跟踪和回收垃圾。在引用计数的基础上,还可以通过“标记-清除”解决容器对象可能产生的循环引用的问题。通过分代回收以空间换取时间进一步提高垃圾回收的效率。
标记-清除::标记-清除的出现打破了循环引用,也就是它只关注那些可能会产生循环引用的对象
缺点:该机制所带来的额外操作和需要回收的内存块成正比。
隔代回收
原理:将系统中的所有内存块根据其存活时间划分为不同的集合,每一个集合就成为一个“代”,垃圾收集的频率随着“代”的存活时间的增大而减小。也就是说,活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率。那么如何来衡量这个存活时间:通常是利用几次垃圾收集动作来衡量,如果一个对象经过的垃圾收集次数越多,可以得出:该对象存活时间就越长。
(1)list是有序集合,即对list的元素进行打印,打印的list顺序与原list中元素的顺序一样。list中的元素可以重复。python是一个动态语言,所以不要求list中的元素必须是同一种类型。可以随时对其做增删改操作。
(2)tuple是有序序列,但不能对tuple元素做增删改操作,即一旦tuple创建成功便不能再做修改,即不能对tuple元素所指的位置做操作。如果tuple中某一元素是一个list时,此时是可以对list的元素做操作的,但也仅限于对list的元素做操作。如 L = (1, 2, ['a', 'b'])
(3)dict是无序的,即创建的dict和打印出的dict顺序不一样,而且不同的机器打印出的dict顺序都可能不一样。dict的元素是可以对其进行增、改操作的。dict中的key不能重复,且不能使用可以更改的。因为dict是无序的,所以不能用下标访问,用for迭代访问。
(4)set持有一系列元素,元素不重复且无序,元素是不变对象(如整型、浮点、tuple等)。因为set是无序的,所以不能用下标访问元素,用迭代访问,用if 1 in S进行判断是否存在。可以对set进行增加、删除操作。
OSI七层
- 应用层(Application Layer):这是网络体系结构中的最顶层,提供用户接口和应用程序之间的通信服务。在这一层,用户可以访问各种网络应用程序,如电子邮件、文件传输和远程登录。 HTTP S FTP
表示层(Presentation Layer):该层负责数据的格式化、加密和压缩,以确保数据在不同系统之间的交换是有效的和安全的。它还提供了数据格式转换和语法转换的功能。
- 会话层(Session Layer):会话层管理应用程序之间的通信会话,负责建立、维护和终止会话。它还提供了数据的同步和检查点恢复功能,以确保通信的完整性和持续性。
- 传输层(Transport Layer):传输层为应用程序提供端到端的数据传输服务,负责数据的分段、传输控制、错误恢复和流量控制。它主要使用 TCP(传输控制协议)和 UDP(用户数据报协议)来实现这些功能。
- TCP(Transmission Control Protocol):提供可靠的、面向连接的数据传输服务,确保数据的可靠性、顺序性和完整性。TCP适用于对数据传输质量要求较高的场景,如文件传输、网页浏览等。
- UDP(User Datagram Protocol):提供无连接的数据传输服务,不保证数据的可靠性,也不保证数据的顺序性和完整性。UDP适用于实时性要求较高、对数据传输质量要求不那么严格的场景,如音视频传输、在线游戏等。
- 网络层(Network Layer):网络层负责数据包的路由和转发,以及网络中的寻址和拥塞控制。它选择最佳的路径来传输数据包,以确保它们能够从源主机到目标主机进行传输。
- 数据链路层(Data Link Layer):数据链路层提供点对点的数据传输服务,负责将原始比特流转换为数据帧,并检测和纠正传输中出现的错误。它还控制访问物理媒介的方式,以及数据帧的传输和接收。
- 物理层(Physical Layer):物理层在物理媒介上传输原始比特流,定义了连接主机的硬件设备和传输媒介的规范。它确保比特流能够在网络中准确地传输,例如通过以太网、光纤和无线电波等媒介。
TCP/IP 四层模型是目前被广泛采用的一种模型,由以下 4 层组成:应用层、传输层、网络层、网络接口层
创
CPU密集:IO密集:资源共享:多线程
服务器瓶颈等待数据库查询相应 多线程每个新来的请求创建一个线程。这些线程可以共享数据库连接池和内存中的数据结构
可扩展性:多进程
如果服务器的主要瓶颈是CPU计算,那么使用多进程可能更合适。多进程可以利用多核CPU的并行处理能力。
为每个新来的请求创建一个进程。每个进程都有自己的内存空间,因此需要通过IPC机制(如管道、消息队列等)进行通信。
Python由于全局解释器锁(GIL)的存在,多而多进程或多线程的并发库(如concurrent.futures)可能是更好的选择。
协程
(Coroutine)是一种程序组件,它允许多个入口点用于暂停和恢复执行的函数,可以在单个线程内实现多任务的并发执行。协程通常用于简化异步编程和协程间的协作。
协程的关键特点包括:
1. **单线程并发**:协程在单个线程内运行,但它们可以并发执行,因为一个协程可以在等待某些操作(如I/O操作)完成时暂停执行,而让其他协程运行。
2. **协作式多任务**:协程通过显式地交出控制权(通常称为“yield”或“await”)来实现多任务。这与操作系统级别的线程或进程不同,后者通常由操作系统以抢占式的方式调度。
3. **轻量级**:协程比线程更轻量级,因为它们共享相同的内存空间,不需要线程上下文切换的开销。
4. **简化异步编程**:协程可以简化异步编程模型,使异步代码看起来和同步代码一样,从而提高代码的可读性和可维护性。
5. **非阻塞I/O操作**:协程通常用于执行非阻塞I/O操作,如网络请求或文件读写,以提高应用程序的性能和响应性。
在不同的编程语言中,协程的实现和语法可能有所不同。例如,在Python中,协程可以通过`async`和`await`关键字实现,而在JavaScript中,协程可以通过`async`函数和`await`表达式实现。
协程是一种强大的编程结构,可以帮助开发人员编写高效、可读的异步代码,特别是在处理I/O密集型任务时。
ORM
是对象关系映射(Object-Relational Mapping)的简称,它是一种程序技术,用于实现面向对象编程语言中不同类型系统的数据之间的转换。ORM的主要作用是在关系型数据库和业务实体对象之间建立映射关系,使得开发人员可以直接操作对象而无需编写复杂的SQL语句。这种技术通过将关系数据库中的业务数据用对象的形式表示出来,并通过面向对象的方式将这些对象组织起来,实现系统业务逻辑的过程。在ORM过程中,最重要的概念是映射(Mapping),通过这种映射可以使业务对象与数据库分离,从而实现数据库层的透明化,使开发人员能够真正面向对象进行开发
RESTful
URL中不能有动词
在Restful架构中,每个网址代表的是一种资源,所以网址中不能有动词,只能有名词,动词由HTTP的 get、post、put、delete 四种方法来表示。
URL结尾不应该包含斜杠“/”
这是作为URL路径中处理中最重要的规则之一,正斜杠(/)不会增加语义值,且可能导致混淆。REST API不允许一个尾部的斜杠,不应该将它们包含在提供给客户端的链接的结尾处
正斜杠分隔符”/“必须用来指示层级关系
rul的路径中的正斜杠“/“字符用于指示资源之间的层次关系。
应该使用连字符”-“来提高URL的可读性,而不是使用下划线”_”
URL路径中首选小写字母
RFC 3986将URI定义为区分大小写,但scheme 和 host components除外。
为了保证url格式的一致性,建议使用复数形式。
对于rest api资源的操作,由HTTP动词表示
CURD操作
GET: 获取资源
POST: 新建资源
PUT:在服务器更新资源(向客户端提供改变后的所有资源)
PATCH: 在服务器更新资源(向客户端提供改变的属性)
DELETE:删除资源
PATCH一般不用,用 PUT
Fielding在论文中提出REST架构的6个限制条件,也可称为RESTful 6大原则, 标准的REST约束应满足以下6个原则:
客户端-服务端(Client-Server): 这个更专注客户端和服务端的分离,服务端独立可更好服务于前端、安卓、IOS等客户端设备。
无状态(Stateless):服务端不保存客户端状态,客户端保存状态信息每次请求携带状态信息。
可缓存性(Cacheability) :服务端需回复是否可以缓存以让客户端甄别是否缓存提高效率。
统一接口(Uniform Interface):通过一定原则设计接口降低耦合,简化系统架构,这是RESTful设计的基本出发点。当然这个内容除了上述特点提到部分具体内容比较多详细了解可以参考这篇REST论文内容。
分层系统(Layered System):客户端无法直接知道连接的到终端还是中间设备,分层允许你灵活的部署服务端项目。
按需代码(Code-On-Demand,可选):按需代码允许我们灵活的发送一些看似特殊的代码给客户端例如JavaScript代码。
REST架构的一些风格和限制条件就先介绍到这里,后面就对具体介绍。
RESTful风格API
1.http状态码
2.路径规范
对资源层级的划分,
"/"不应该出现在URL的末尾,
REST API对URI资源的定义具有唯一性,一个资源对应一个唯一的地址
路径中使用连字符 -代替下划线 _
路径中统一使用小写字母
3.请求操作get。。。。
反向代理是什么 Nginx
代理服务器配置
反向代理是一种代理服务器配置,位于用户与目标服务器之间,使得用户直接访问反向代理服务器即可获得目标服务器的资源。
接收请求:客户端向反向代理发送请求。
转发请求:反向代理根据请求的URL或其他信息,将请求转发到后端服务器。
获取响应:后端服务器处理请求并返回响应。
转发响应:反向代理将后端服务器的响应转发回客户端。
以下是反向代理的主要特点和应用场景:
1
透明性。与正向代理不同,反向代理对客户端是透明的,客户端无法直接访问内部服务器,所有请求都必须通过反向代理服务器转发。
2
负载均衡。反向代理可以将请求分发给多个内部服务器,实现负载均衡,提高系统的可扩展性和可用性。
3
安全保护。通过隐藏内部服务器的真实IP地址,提高系统的安全性,防止攻击者直接访问内部服务器。
4
静态资源缓存。可以缓存静态资源,减轻内部服务器的负载,提高系统的响应速度。
应用层防火墙。为网站提供对基于Web的攻击行为(例如DoS/DDoS)的防护,更容易排查恶意软件等。
5
统一提供加密和SSL加速。为后端服务器提供HTTP访问认证等。
反向代理服务器通常用于Web加速,作为网页服务器的前置机来降低网络和服务器的负载,提高访问效率。在实际应用中,反向代理服务器可以隐藏服务器的IP地址,保证内网的安全,阻止web攻击。大型网站通常将反向代理作为公网访问地址,而Web服务器位于内网中。
URL解析过程
浏览器的地址栏输入URL并按下回车
浏览器查找当前URL是否存在缓存,并比较缓存是否过期
DNS解析URL对应的IP
根据IP建立TCP连接(三次握手)
发送HTTP请求
服务器处理请求
返回HTTP响应
浏览器接受HTTP响应
关闭TCP连接(四次挥手)
浏览器解析HTML
浏览器布局渲染