西维蜀黍

【MySQL】MySQL 的存储引擎(Storage Engines)- MyISAM 与 InnoDB

MySQL 的存储引擎(Storage Engines)

MySQL 有两种存储引擎(Storage Engines):

  • InnoDB’s
  • MyISAM

InnoDB is a general-purpose storage engine that balances high reliability and high performance. In MySQL 8.0, InnoDB is the default MySQL storage engine. Unless you have configured a different default storage engine, issuing a CREATE TABLE statement without an ENGINE clause creates an InnoDB table.

MyISAM

MyISAM不支持聚簇索引,因此,MyISAM中的主键索引(Primary Index) 和辅助索引(Secondary Index),都是非聚集索引(Non-clustered Index)。

在MyISAM的B+Tree的叶子节点上的data域中,并不是保存数据本身,而是保存数据存放在磁盘中的地址。

  ...


【Django】Django 项目部署到 uWSGI + Nginx 作为生产环境

Workflow

当客户端发出一个 HTTP 请求后,是如何转到我们的应用程序处理并返回的呢?

我根据其架构组成的不同将这个过程的实现分为两种情况:

两级结构

在两级结构中:

  • uWSGI可以作为WSGI Server(WSGI 服务器),它基于了 HTTP 协议、 WSGI(Web Server Gateway Interface) 协议和 usgi 协议;
  • Flask应用可以作为 WSGI Application,基于 WSGI 协议实现。

当有客户端发来请求,uWSGI接受请求,调用 WSGI Application (或者说Flask应用)。当WSGI Application处理完成后,将结果返回给uWSGI,uWSGI构造相应的 HTTP Response,最终将结果返回给客户端。

通常来说,Flask等Web框架会自己附带一个 WSGI 服务器(这就是 Flask 应用可以直接启动的原因),但是这只是在开发阶段被用到而言。在生产环境中,仍然需要将 Flask 部署到一个高性能的 WSGI Server 中,比如 uWSGI,alternative 还有 gunicorn (Green Unicorn), wsgiref。

三级结构

在三级结构中:

  • uWSGI Server 作为中间件,它基于WSGI 协议与Nginx通信,同时与实现了 WSGI 协议的 WSGI Application(比如一个 Flask Application)进行通信

当有客户端发来请求,Nginx(Web Server)先做处理静态资源(静态资源是Nginx的强项),无法处理的动态请求以 WSGI 协议的格式转发给 WSGI Application(比如一个 Flask Application)。

这里,我们多了一层反向代理有什么好处?

  • 提高Web server性能(uWSGI处理静态资源不如nginx)nginx会在收到一个完整的http请求后再转发给wWSGI)
  • Nginx可以做负载均衡(客户端是和Nginx交互而不是uWSGI),以提升高可用
  ...


【HTTP】Web Server(Web 服务器)、Web Application Server(Web 应用服务器)和 CGI(Common Gateway Interface)的故事

Web Server(Web 服务器)

在网络刚刚盛行的年代(Web 1.0),绝大部分的 Web 资源都是静态的。这意味着,你访问 www.example.com/a.html,你就可以访问 a 页面,而如果访问 www.example.com/b.html,你就可以访问 b页面。显然,信息量是单向的,即网站上有什么,你就只能看什么,而不能修改网站的内容。我们通常把它称为静态网站。

狭义的Web Server(Web 服务器)概念中,Web Server只能处理静态页面。

典型的Web Server的例子,就是 Apache HTTP Server,Nginx 还有 LightHttpd。

随着互联网的发展,我们更希望访问一个动态网站(Web 1.5),即可以根据用户传入的信息,动态地生成网页的内容。

这时候,我们就需要一个程序来帮忙处理动态请求(如返回特定用户的信息、获取当前时间等),而静态资源的请求,依然由Web Server来负责。

Web服务器程序会将动态请求转发给帮助程序(helper application),帮助程序处理后,返回处理后的静态结果给Web服务器程序。这样,就避免了 Web 服务器程序需要处理动态页面的问题,如下图:

  ...


【Python】WSGI(Web Server Gateway Interface)、uWSGI Server、uwsgi、WSGI Application 的故事

关于Web Server(Web 服务器)、Web Application Server(Web 应用服务器)和 CGI(Common Gateway Interface)的区别,可以访问【HTTP】Web Server(Web 服务器)、Web Application Server(Web 应用服务器)和 CGI(Common Gateway Interface)的故事 。

WSGI(Web Server Gateway Interface)

WSGI是 Web Server Gateway Interface 的缩写。

它不是服务器、Python模块、框架、API或者任何软件,而是一种描述 Web服务器(如Nginx)如何与Web application Server(Web应用程序,如用Django、Flask框架编写的程序)通信的规范。

因此,WSGI 是一种规范,或者说一种接口,或者说一种协议,equivalent 是 Java 世界中的 servlet。

WSGI是在 PEP 333 提出的,并在 PEP 3333 进行补充(主要是为了支持 Python3.x)。

这个协议旨在解决众多 Python Web 框架和Web server软件的兼容问题。有了WSGI,你再也不用因为使用特定的Web 应用框架而不得不选择特定的 Web server软件。

只要遵照了 WSGI 协议的的 Web 应用(Web application,我们称之为WSGI应用),都可以在任何遵照了WSGI协议的 Web server 软件(我们称之为 WSGI server,比如 uWSGI)上运行。

而对于WSGI 应用(WSGI Application),我们理论上可以完全自己从头编写一个 WSGI 应用,但是,我们通常会使用WSGI 应用框架(WSGI Application Framework),或者称为WSGI MiddleWare,比如Django,Flask等,因为框架已经帮我们封装了大量的底层逻辑,而让我们可以更好的专注于业务逻辑上。

为了更好的理解 WSGI 应用(WSGI Application),我们接下来会写一个非常原始的 WSGI 应用。

  ...


【Python】Error Handling

Error Return

If service can not handle request for internal error or upstream error, it should response proper error information to client.

Service should write log for every error return.

The error response structure should be consistent for all APIs in one service.

  • The error code should be either a short string code or an integer, defined by service. This is to help client understand the situation and choose different ways to handle the error.
  • The detailed error message is optional. This is to help client debug the issue.
  • The error code need to be categorized to 2 types, and allow client side easily identify the type of error.
    • Occasional error, such as upstream down, internal error, etc. This kind of errors means client can safely retry the request.
    • Logic error, such as invalid argument. Client can cached the result and should not directly retry the request.
  ...


【Design】Error Handling

Returned Errors

If service can not handle request for internal error or upstream error, it should response proper error information to client.

Service should write log for every error return.

The error response structure should be consistent for all APIs in one service.

  • The error code should be either a short string code or an integer, defined by service. This is to help client understand the situation and choose different ways to handle the error.
  • The detailed error message is optional. This is to help client debug the issue.
  • The error code need to be categorized to 2 types, and allow client side easily identify the type of error.
    • Occasional error, such as upstream down, internal error, etc. This kind of errors means client can safely retry the request.
    • Logic error, such as invalid argument. Client can cached the result and should not directly retry the request.

Error Codes

Google APIs must use the canonical error codes defined by google.rpc.Code. Individual APIs should avoid defining additional error codes, since developers are very unlikely to write logic to handle a large number of error codes. For reference, handling an average of 3 error codes per API call would mean most application logic would just be for error handling, which would not be a good developer experience.

  ...


【Design】API Design

HTTP REST APIs

URL

Example

The following sections present a few real world examples on how to apply resource-oriented API design to large scale services. You can find more examples in the Google APIs repository.

In these examples, the asterisk indicates one specific resource out of the list.

Gmail API

The Gmail API service implements the Gmail API and exposes most of Gmail functionality. It has the following resource model:

  • API service: gmail.googleapis.com

    • A collection of users:users/*. Each user has the following resources.
      • A collection of messages: users/*/messages/*.
      • A collection of threads: users/*/threads/*.
      • A collection of labels: users/*/labels/*.
      • A collection of change history: users/*/history/*.
      • A resource representing the user profile: users/*/profile.
      • A resource representing user settings: users/*/settings.

Cloud Pub/Sub API

The pubsub.googleapis.com service implements the Cloud Pub/Sub API, which defines the following resource model:

  • API service:pubsub.googleapis.com

    • A collection of topics: projects/*/topics/*.
  • A collection of subscriptions: projects/*/subscriptions/*.

NOTE: Other implementations of the Pub/Sub API may choose different resource naming schemes.

Cloud Spanner API

The spanner.googleapis.com service implements the Cloud Spanner API, which defines the following resource model:

  • API service:spanner.googleapis.com
    • A collection of instances:projects/*/instances/*.
      • A collection of instance operations: projects/*/instances/*/operations/*.
      • A collection of databases: projects/*/instances/*/databases/*.
      • A collection of database operations: projects/*/instances/*/databases/*/operations/*.
      • A collection of database sessions: projects/*/instances/*/databases/*/sessions/*.
  ...


【Django】Django 使用 Redis

安装 Dependency

$ pip install django-redis

作为 cache backend 使用配置

为了使用 django-redis , 在你项目下的setting.py中添加如下代码:

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}

为了更好的互操作性并使连接字符串更加 “标准”, 从 3.8.0 开始 django-redis 使用 redis-py native url notation 作为连接字符串.

  ...


【Redis】自动过期

自动过期

Redis中有个设置时间过期的功能,通过 setex 或者 expire 关键字来实现。

redis 127.0.0.1:6379> hset expire:me name tom
(integer) 0
redis 127.0.0.1:6379> hget expire:me name
"tom"
 
redis 127.0.0.1:6379> expire expire:me 20
(integer) 1
redis 127.0.0.1:6379> ttl expire:me     # 获得还有多久过期,比如这里就是就有 19 秒才过期
(integer) 19
 
...
...
...
 
redis 127.0.0.1:6379> ttl expire:me
(integer) -1
redis 127.0.0.1:6379> hget expire:me name

即对存储在Redis数据库中的键值对(key-value pair)可以设置一个过期时间。作为一个缓存数据库,这是非常实用的。如我们一般项目中的token、登录信息或者短信验证码都是有时间限制的,按照传统的数据库处理方式,一般都是自己判断过期,这样无疑会严重影响项目性能。

Redis对存储值的过期处理实际上是针对该值的键(key)处理的,即时间的设置也是设置key的有效时间。

Expires字典保存了所有键的过期时间,Expires也被称为过期字段。

  ...


【Python】变量作用域

Python的作用域分类

Python的作用域一共有4种,分别是:

  • L (Local) 局部作用域
  • E (Enclosing) 闭包函数外的函数中
  • G (Global) 全局作用域
  • B (Built-in) 内建作用域

以 L –> E –> G –>B 的顺序去查找,即:当在局部作用域中找不到时,便会去局部作用域之外的作用域去找(例如闭包),再找不到就会去全局作用域中找,再者去内建作用域中找。最终,如果仍没有找到,则会抛出NameError错误。

下面举一个实用LEGB法则的例子:

globalVar = 100           #G

def test_scope():
    enclosingVar = 200    #E
    def func():
        localVar = 300    #L
print __name__            #B
  ...