#463 Redis 客户端缓存
Redis 2021-01-17对于一些热点数据,我们从 Redis 获取之后,常常会在本地线程中也存一份(或者根据语言不同,有些别的第三方组件来专门做这个),也可以有效减小网络消耗。
不过这也算是增加了一级缓存,不可避免的需要涉及数据的同步问题。如果需要比较强的实时性保障,我们就只能放弃采用这个缓存。
可现如今,Redis 能在数据过期
或数据更新
(包括重新设置导致 TTL 变化)之后通知客户端了,这也太贴心了吧。
coding in a complicated world
对于一些热点数据,我们从 Redis 获取之后,常常会在本地线程中也存一份(或者根据语言不同,有些别的第三方组件来专门做这个),也可以有效减小网络消耗。
不过这也算是增加了一级缓存,不可避免的需要涉及数据的同步问题。如果需要比较强的实时性保障,我们就只能放弃采用这个缓存。
可现如今,Redis 能在数据过期
或数据更新
(包括重新设置导致 TTL 变化)之后通知客户端了,这也太贴心了吧。
官方库里面只看到 PlainAuth 和 CramMd5Auth 两种认证方法。
type loginAuth struct {
username, password string
}
func LoginAuth(username, password string) smtp.Auth {
return &loginAuth{username, password}
}
func (a *loginAuth) Start(server *smtp.ServerInfo) (proto string, toServer []byte, err error) {
return "LOGIN", []byte{}, nil
}
func (a *loginAuth) Next(fromServer []byte, more bool) (toServer []byte, err error) {
if more {
switch strings.ToUpper(string(fromServer)) {
case "USERNAME:":
return []byte(a.username), nil
case "PASSWORD:":
return []byte(a.password), nil
default:
return nil, fmt.Errorf("unknown command %v", fromServer)
}
}
return nil, nil
}
package main
import (
"crypto/tls"
"errors"
"fmt"
"net"
"net/smtp"
)
// ============================================================================
type Transaction struct {
Host string
Port uint16
LocalName string
TlsEnable bool
Username string
Password string
MailFrom string
RcptTo []string
Data []byte
}
func NewTransaction() Transaction {
trans := Transaction{}
return trans
}
func (trans Transaction) Send() error {
addr := fmt.Sprintf("%s:%d", trans.Host, trans.Port)
// SendMail(addr string, a Auth, from string, to []string, msg []byte) error
c, err := smtp.Dial(addr)
if err != nil {
return err
}
defer c.Close()
c.Hello(trans.LocalName)
if ok, _ := c.Extension("STARTTLS"); ok {
serverName, _, _ := net.SplitHostPort(addr)
config := &tls.Config{
InsecureSkipVerify: true,
ServerName: serverName,
}
if err = c.StartTLS(config); err != nil {
return err
}
} else {
fmt.Printf("smtp: server doesn't support STARTTLS\n")
}
if trans.Username != "" {
if ok, _ := c.Extension("AUTH"); !ok {
return errors.New("smtp: server doesn't support AUTH")
}
auth := smtp.PlainAuth("", trans.Username, trans.Password, trans.Host)
if err = c.Auth(auth); err != nil {
fmt.Println("smtp: authentication failed")
return err
}
}
if err = c.Mail(trans.MailFrom); err != nil {
return err
}
for _, addr := range trans.RcptTo {
if err = c.Rcpt(addr); err != nil {
return err
}
}
w, err := c.Data()
if err != nil {
return err
}
_, err = w.Write(trans.Data)
if err != nil {
return err
}
err = w.Close()
if err != nil {
return err
}
return c.Quit()
}
// ============================================================================
func main() {
from := "ninedoors@126.com"
to := "ninedoors@qq.com"
msg := []byte{}
fmt.Println("============================================================")
trans := Transaction{
"smtp.126.com", 25, "peach", false,
from, "password",
from, []string{to},
msg,
}
trans.Send()
}
net/smtp
没有发现有好的日志实现,我只能定制了一个版本实现了日志smtp.Client
-> textproto.Conn
-> textproto.Writer
通过 apt 和 apt-file 列出 mysql 提供的所有可执行文件。
说明:
DEFAULT current_timestamp() ON UPDATE current_timestamp()
YEAR(2)
被抛弃,新建时会自动转换成 YEAR(4)
分区表的增强
最大可用分区数增加至 8192
操作时显式指定分区,比如:
SELECT * FROM employees PARTITION (p0, p2)
Undo Log 可保存在独立表空间中,因其是随机 IO,更适合放到 SSD 中。但仍然不支持空间的自动回收。
innodb_log_file_size
)。mysql.user
的 password_expired
))复制和日志(replication)
1)支持延时复制,MySQL现在支持延迟复制,默认是0秒。使用CHANGE MASTER TO参数 MASTER_DELAY 来设置延迟。可以让slave跟master之间控制一个时间间隔,方便特殊情况下的数据恢复。(以前是使用第三方工具可以做到) 2)行级复制功能加强,可以降低磁盘、内存、网络等资源开销(只记录能确定行记录的字段即可)。3)现在支持多线程复制。如果开启,sql线程作为协调者协调多个工作线程,数量取决于slave_parallel_workers。现在多线程复制以单库为基础,特定库的更新的相对顺序和主库一样。不过,没有必要协调不同库之间的事务。事务可以被分布到每个库,意味着一个复制从库的工作线程可以顺序执行事务而不必等待其它库的更新完成。4)支持以全局统一事务ID(GTID)为基础的复制。当在主库上提交事务或者被从库应用时,可以定位和追踪每一个事务。GTID复制是全部以事务为基础,使得检查主从一致性变得非常简单。如果所有主库上提交的事务也同样提交到从库上,一致性就得到了保证。5)支持启用GTID,对运维人员来说应该是一件令人高兴的事情,在配置主从复制,传统的方式里,你需要找到binlog和POS点,然后change master to指向,而不是很有经验的运维,往往会将其找错,造成主从同步复制报错,在mysql5.6里,如果使用了GTIDs,启动一个新的复制从库或切换到一个新的主库,就不必依赖log文件或者pos位。需要知道master的IP、端口,账号密码即可,因为同步复制是自动的,mysql通过内部机制GTID自动找点同步。
6)binlog的读写现在是崩溃安全的,因为只有完整的事件(或者事务)才会被记录和读取。默认会记录事件的大小以及事件本身,使用大小来验证事件被正确记录。你也可以使用参数binlog_checksum设置使用crc32记录事件的校验值。使用参数master_verify_checksum可以让服务读取校验值。slave-sql-verify-checksum参数使从库读relay日志的时候读取校验值。
MySQL支持在表中保存主库连接信息了。使用参数–master-info-repository和 –relay-log-info-repository来设置。设置 –master-info-repository为表,会记录连接信息到slave_master_info表。设置–relay-log-info-repository为表,会记录relay log信息到slave_relay_log_info表。这几个表都是自动建立在mysql系统库。
增强Performance Schema数据库(Mysql performance schema)
1)记录表的输入与输出,操作包括行级访问表和临时表,如insert,upate,delete. 2)表的事件过滤,以库或者表名为基础。3)线程的事件过滤,更多关于线程的信息被搜集4)表和索引I/O以及表锁的统计表。5)记录命令以及命令的阶段。
不允许在存储过程中或者函数参数中或者存储程序本地变量中使用default来指定(如:SET var_name = DEFAULT命令),但可以在指定系统变量时使用default。
组复制
InnoDB Cluster
多源复制
增强半同步(AFTER_SYNC)
基于WRITESET的并行复制。
在线开启GTID复制。
在线设置复制过滤规则。
在线修改Buffer pool的大小。
在同一长度编码字节内,修改VARCHAR的大小只需修改表的元数据,无需创建临时表。
可设置NUMA架构的内存分配策略(innodb_numa_interleave)。
透明页压缩(Transparent Page Compression)。
UNDO表空间的自动回收。
查询优化器的重构和增强。
可查看当前正在执行的SQL的执行计划(EXPLAIN FOR CONNECTION)。
引入了查询改写插件(Query Rewrite Plugin),可在服务端对查询进行改写。
EXPLAIN FORMAT=JSON会显示成本信息,这样可直观的比较两种执行计划的优劣。
引入了虚拟列,类似于Oracle中的函数索引。
新实例不再默认创建test数据库及匿名用户。
引入ALTER USER命令,可用来修改用户密码,密码的过期策略,及锁定用户等。
mysql.user表中存储密码的字段从password修改为authentication_string。
表空间加密。
优化了Performance Schema,其内存使用减少。
Performance Schema引入了众多instrumentation。常用的有Memory usage instrumentation,可用来查看MySQL的内存使用情况,Metadata Locking Instrumentation,可用来查看MDL的持有情况,Stage Progress instrumentation,可用来查看Online DDL的进度。
同一触发事件(INSERT,DELETE,UPDATE),同一触发时间(BEFORE,AFTER),允许创建多个触发器。在此之前,只允许创建一个触发器。
InnoDB原生支持分区表,在此之前,是通过ha_partition接口来实现的。
分区表支持可传输表空间特性。
集成了SYS数据库,简化了MySQL的管理及异常问题的定位。
原生支持JSON类型,并引入了众多JSON函数。
引入了新的逻辑备份工具-mysqlpump,支持表级别的多线程备份。
引入了新的客户端工具-mysqlsh,其支持三种语言:JavaScript, Python and SQL。两种API:X DevAPI,AdminAPI,其中,前者可将MySQL作为文档型数据库进行操作,后者用于管理InnoDB Cluster。
mysql_install_db被mysqld --initialize代替,用来进行实例的初始化。
原生支持systemd。
引入了super_read_only选项。
可设置SELECT操作的超时时长(max_execution_time)。
可通过SHUTDOWN命令关闭MySQL实例。
引入了innodb_deadlock_detect选项,在高并发场景下,可使用该选项来关闭死锁检测。
引入了Optimizer Hints,可在语句级别控制优化器的行为,如是否开启ICP,MRR等,在此之前,只有Index Hints。
GIS的增强,包括使用Boost.Geometry替代之前的GIS算法,InnoDB开始支持空间索引。
最重要的是窗口函数和 CTE
其他MySQL 8.0计划更新的特性包括:
在锁定行方面增加了更多选项,如SKIP LOCKED和NOWAIT两个选项。其中,
SKIP LOCKED允许在操作中不锁定那些需要忽略的行;NOWAIT则在遇到行的锁定的时候马上抛出错误。
MySQL能根据可用内存的总量去伸缩扩展,以更好利用虚拟机的部署。
新增“隐藏索引”的特性,这样索引可以在查询优化器中变为不可见。索引在标记为不可用后,和表的数据更改同步,但是优化器不会使用它们。对于使用隐藏索引的建议,是当不决定某个索引是否需要保留的时候,可以使用。
引入了原生的,基于InnoDB的数据字典。数据字典表位于mysql库中,对用户不可见,同mysql库的其它系统表一样,保存在数据目录下的mysql.ibd文件中。不再置于mysql目录下。
Atomic DDL。
重构了INFORMATION_SCHEMA,其中,部分表已重构为基于数据字典的视图,在此之前,其为临时表。
PERFORMANCE_SCHEMA查询性能提升,其已内置多个索引。
不可见索引(Invisible index)。
降序索引。
直方图。
角色(Role)。
资源组(Resource Groups),可用来控制线程的优先级及其能使用的资源,目前,能被管理的资源只有CPU。
引入了innodb_dedicated_server选项,可基于服务器的内存来动态设置innodb_buffer_pool_size,innodb_log_file_size和innodb_flush_method。
快速加列(ALGORITHM=INSTANT)。
自增主键的持久化。
可持久化全局变量(SET PERSIST)。
默认字符集由latin1修改为utf8mb4。
默认开启UNDO表空间,且支持在线调整数量(innodb_undo_tablespaces)。在MySQL 5.7中,默认不开启,若要开启,只能初始化时设置。
备份锁。
Redo Log的优化,包括允许多个用户线程并发写入log buffer,可动态修改innodb_log_buffer_size的大小。
默认的认证插件由mysql_native_password更改为caching_sha2_password。
默认的内存临时表由MEMORY引擎更改为TempTable引擎,相比于前者,后者支持以变长方式存储VARCHAR,VARBINARY等变长字段。从MySQL 8.0.13开始,TempTable引擎支持BLOB字段。
Grant不再隐式创建用户。
SELECT ... FOR SHARE和SELECT ... FOR UPDATE语句中引入NOWAIT和SKIP LOCKED选项,解决电商场景热点行问题。
正则表达式的增强,新增了4个相关函数,REGEXP_INSTR(),REGEXP_LIKE(),REGEXP_REPLACE(),REGEXP_SUBSTR()。
查询优化器在制定执行计划时,会考虑数据是否在Buffer Pool中。而在此之前,是假设数据都在磁盘中。
ha_partition接口从代码层移除,如果要使用分区表,只能使用InnoDB存储引擎。
引入了更多细粒度的权限来替代SUPER权限,现在授予SUPER权限会提示warning。
GROUP BY语句不再隐式排序。
MySQL 5.7引入的表空间加密特性可对Redo Log和Undo Log进行加密。
information_schema中的innodb_locks和innodb_lock_waits表被移除,取而代之的是performance_schema中的data_locks和data_lock_waits表。
引入performance_schema.variables_info表,记录了参数的来源及修改情况。
增加了对于客户端报错信息的统计(performance_schema.events_errors_summary_xxx)。
可统计查询的响应时间分布(call sys.ps_statement_avg_latency_histogram())。
支持直接修改列名(ALTER TABLE ... RENAME COLUMN old_name TO new_name)。
用户密码可设置重试策略(Reuse Policy)。
移除PASSWORD()函数。这就意味着无法通过“SET PASSWORD ... = PASSWORD('auth_string') ”命令修改用户密码。
代码层移除Query Cache模块,故Query Cache相关的变量和操作均不再支持。
BLOB, TEXT, GEOMETRY和JSON字段允许设置默认值。
可通过RESTART命令重启MySQL实例。
ssh // 包含 ssh
ssh login // 包含 ssh 或者 login
"ssh login" // 包含 ssh login
// 正则匹配
/ethernet[0-9]+/
type:(ssh OR login) // 字段 type 包含 ssh 或者 login
// 必须 (不) 包含字段
_exists_:type
NOT _exists_:type
// 支持 `*`, `?` 两种通配符
source:xxx?yyy
source:xxx*yyy
// PS: 默认配置,通配符不能放在最前面,避免内存消耗太多
// 可以这样开启:
// allow_leading_wildcard_searches = true
// 模糊匹配 ~
ssh logni~ // 可以搜索到 ssh login
"foo bar"~5 // Damerau–Levenshtein distance
// 范围
http_response_code:[500 TO 504]
http_response_code:{400 TO 404}
bytes:{0 TO 64]
http_response_code:[0 TO 64}
// 大小
http_response_code:>400
http_response_code:<400
http_response_code:>=400
http_response_code:<=400
http_response_code:(>=400 AND <500)
// 时间范围
timestamp:["2019-07-23 09:53:08.175" TO "2019-07-23 09:53:08.575"]
otherDate:[now-5d TO now-4d]
此外,还有 AND,OR,NOT,括号等逻辑运算符可用。
转义符:
& | : \ / + - ! ( ) { } [ ] ^ " ~ * ?
SHOW VARIABLES LIKE '%engine%';
SHOW ENGINES;
以 MySQL 8.0 为例:
除了 PERFORMANCE_SCHEMA,
Engine | Support | Comment | Transactions | XA | Savepoints |
---|---|---|---|---|---|
ARCHIVE | YES | Archive storage engine | NO | NO | NO |
BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO |
MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO |
FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL |
MyISAM | YES | MyISAM storage engine | NO | NO | NO |
InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES |
MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO |
CSV | YES | CSV storage engine | NO | NO | NO |
Percona (8.0) 和 MySQL 保持一致,不过 InnoDB 实际被替换成他们自己开发的兼容引擎 XtraDB。
MariaDB (10.1.45) 也使用 XtraDB 替换了 InnoDB,而且没有 BLACKHOKE, ARCHIVE,但是多了:
Engine | Support | Comment | Transactions | XA | Savepoints |
---|---|---|---|---|---|
TokuDB | DEFAULT | Percona TokuDB Storage Engine with Fractal Tree(tm) Technology | YES | YES | YES |
SEQUENCE | YES | Generated tables filled with sequential values | YES | NO | YES |
Aria | YES | Crash-safe tables with MyISAM heritage | NO | NO | NO |
在 2010 年发布的 MySQL 5.5.5 之前,一直是 MySQL 的默认引擎。
InnoDB 是甲骨文的。
TokuTek 公司开发,后来被 Percona 公司收购,现在已被 Percona 废弃(仓库归档)。
JSON Schema 是一个 JSON 数据的校验规范,其规则的定义本身也是 JSON 格式。
目前有 RFC 草案。最新是 2022-06-10 提交的 draft-bhutton-json-schema-01。
Update @ 2023-02-28: 现在还是没有成为标准。最新的那份草案早已过期(提交之后半年)。
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": {
"type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
}
}
}
string
字符串integer
整数number
数字,包括整数、浮点数boolean
布尔类型:true
,false
object
对象array
数组null
空值format
格式,默认只是注释,验证器实现也可以根据这个来进行校验。
date
time
date-time
uri
email
ipv4
ipv6
pattern
正则表达式
minimum
大于等于exclusiveMinimum
大于,部分版本中,这个值是 bool 型maximum
小于等于exclusiveMaximum
小于,部分版本中,这个值是 bool 型multipleOf
倍数采用 Julian/jsonschema
PS:现在仓库地址转向了 python-jsonschema/jsonschema
>>> from jsonschema import validate
>>> # A sample schema, like what we'd get from json.load()
>>> schema = {
... "type" : "object",
... "properties" : {
... "price" : {"type" : "number"},
... "name" : {"type" : "string"},
... },
... }
>>> # If no exception is raised by validate(), the instance is valid.
>>> validate(instance={"name" : "Eggs", "price" : 34.99}, schema=schema)
>>> validate(
... instance={"name" : "Eggs", "price" : "Invalid"}, schema=schema,
... ) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
ValidationError: 'Invalid' is not of type 'number'