Git 创建本地分支及合并线上生产分支

Git中,创建一个本地分支,在本地分支做修改,等全部修改完成后再同步到线上主分支。这是 Git 版本控制流程中的一个非常常见和推荐的做法。以下是详细步骤:

1.创建本地分支:

首先,你需要从当前分支(通常是 master 或 main,但也可以是其他分支)创建一个新的本地分支。这样做可以让你在一个隔离的环境中工作,而不会影响到其他分支。

git checkout -b your-new-branch-name

这里的 your-new-branch-name 是你想要创建的本地分支的名称。

2.在本地分支上工作:

现在,你可以在这个新创建的本地分支上进行所有的修改、添加文件、提交更改等操作。

# 添加新文件或修改现有文件  
# ... 进行你的工作 ...  

# 将更改添加到暂存区  
git add .  

# 提交更改  
git commit -m "描述你的更改"

同步到远程仓库(可选,但推荐):

在将更改推送到线上主分支之前,通常建议先将你的本地分支推送到远程仓库,以便进行备份和协作。

# 首先确保你已经设置了远程仓库的上游(如果是第一次推送)  
git push -u origin your-new-branch-name

这里的 origin 是远程仓库的默认名称,your-new-branch-name 是你要推送的本地分支的名称。

合并到线上主分支:

当你确定本地分支上的所有更改都已完成并经过测试后,你可以将这些更改合并到线上主分支上。这通常是通过一个 pull request(在 GitHub、GitLab 等平台上)或 merge request(在 GitLab 上)来完成的,但你也可以直接在命令行中执行合并操作(如果你有足够的权限)。
如果你选择使用命令行,并且你有权限直接推送到主分支,你可以这样做:

# 切换到主分支  
git checkout master  

# 从远程仓库拉取最新的主分支更改(确保你的主分支是最新的)  
git pull origin master  

# 合并你的本地分支到主分支  
git merge your-new-branch-name  

# 将合并后的更改推送到远程仓库的主分支  
git push origin master

注意:在实际操作中,直接推送到主分支(或其他受保护的分支)可能需要特殊的权限或配置。在大多数团队协作环境中,推荐使用 pull request 或 merge request 来合并更改,以便进行代码审查和讨论。

(可选)删除本地和远程分支:

如果你的本地分支已经成功合并到主分支,并且你不再需要它,你可以选择删除它。

# 删除本地分支  
git branch -d your-new-branch-name  

# 删除远程分支(如果你已经推送了它)  
git push origin --delete your-new-branch-name

注意:使用 -d 选项删除分支时,Git 会检查该分支是否已经被合并到当前分支。如果还没有合并,Git 会拒绝删除并提示你。如果你确定要强制删除一个未合并的分支,可以使用 -D 选项代替 -d。但在大多数情况下,建议先合并再删除分支。

VSCode 汉化

步骤 1:安装中文语言包

打开 VSCode,按 Ctrl + Shift + P 打开命令面板。
输入 ext install chinese 并按回车键安装中文语言包。

步骤 2:启用汉化

在命令面板中输入 configure display language。
在下拉列表中,选择 Simplified Chinese(简体中文)或 Traditional Chinese(繁体中文)。
步骤 3:重启 VSCode

单击 重启 按钮以重新启动 VSCode 并应用语言更改。
步骤 4:调整默认语言

在命令面板中输入 settings.json。
添加以下行到 settings.json 文件末尾:
"locale":"zh-CN"

如果使用繁体中文,则将 “zh-CN” 替换为 “zh-TW”。
步骤 5:完成

重新启动 VSCode,您的界面现在将显示为中文。

MySQL – AND/OR执行顺序

MySql中,会按以下权重顺序执行NOT > AND > OR,且where后面如果有or,or会自动把左右分组
例如:
SELECT * FROM student WHERE id=10001 ANDname='小A' OR 1=1 ANDage=18;
相当于
SELECT * FROM student WHERE (id=10001 ANDname='小A') OR (1=1 ANDage=18);

例如:
select * from book where bid in (1,3,4) or bname like 'Java%' and price = 30
相当于
因为Mysql先处理AND操作符,所以先判断bname like ‘Java%’ and price = 30,没有符合该条件的记录,再和bid in (1,3,4)做或运算.

使用反射函数处理httpresponse object

最近同事在对接接口的时候得到了一个Httpresponse Object对象,这玩意嘛,来,大家感受下

object(HttpResponse)#11 (7) {
  ["content":"HttpResponse":private] => string(1047) "HTTP/1.1 200 OK
Server: Tengine
Date: Fri, 22 Jun 2018 09:52:33 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 52
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
Access-Control-Allow-Headers: X-Requested-With,X-Sequence,X-Ca-Key,X-Ca-Secret,X-Ca-Version,X-Ca-Timestamp,X-Ca-Nonce,X-Ca-API-Key,X-Ca-Stage,X-Ca-Client-DeviceId,X-Ca-Client-AppId,X-Ca-Signature,X-Ca-Signature-Headers,X-Ca-Signature-Method,X-Forwarded-For,X-Ca-Date,X-Ca-Request-Mode,Authorization,Content-Type,Accept,Accept-Ranges,Cache-Control,Range,Content-MD5
Access-Control-Max-Age: 172800
X-Ca-Request-Id: CC7C80AD-3CCB-4F54-B14D-3BDA143AC3BE
Vary: Accept-Encoding
ufe-result: A2
Pragma: no-cache
Cache-Control: no-store
Via: 71aae546d43b[web,200]
Set-Cookie: SERVERID=c0cb0a9f805e8a70fc370b48877e9e8e|1529661153|1529661153;Path=/
X-Ca-Error-Message: OK
X-Ca-Debug-Info: {"TotalLatency":81,"ServiceLatency":55}

{"code":601,"message":"数据不存在","data":null}"
  ["body":"HttpResponse":private] => string(52) "{"code":601,"message":"数据不存在","data":null}"
  ["header":"HttpResponse":private] => string(995) "HTTP/1.1 200 OK
Server: Tengine
Date: Fri, 22 Jun 2018 09:52:33 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 52
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
Access-Control-Allow-Headers: X-Requested-With,X-Sequence,X-Ca-Key,X-Ca-Secret,X-Ca-Version,X-Ca-Timestamp,X-Ca-Nonce,X-Ca-API-Key,X-Ca-Stage,X-Ca-Client-DeviceId,X-Ca-Client-AppId,X-Ca-Signature,X-Ca-Signature-Headers,X-Ca-Signature-Method,X-Forwarded-For,X-Ca-Date,X-Ca-Request-Mode,Authorization,Content-Type,Accept,Accept-Ranges,Cache-Control,Range,Content-MD5
Access-Control-Max-Age: 172800
X-Ca-Request-Id: CC7C80AD-3CCB-4F54-B14D-3BDA143AC3BE
Vary: Accept-Encoding
ufe-result: A2
Pragma: no-cache
Cache-Control: no-store
Via: 71aae546d43b[web,200]
Set-Cookie: SERVERID=c0cb0a9f805e8a70fc370b48877e9e8e|1529661153|1529661153;Path=/
X-Ca-Error-Message: OK
X-Ca-Debug-Info: {"TotalLatency":81,"ServiceLatency":55}

"
  ["requestId":"HttpResponse":private] => string(36) "CC7C80AD-3CCB-4F54-B14D-3BDA143AC3BE"
  ["errorMessage":"HttpResponse":private] => string(2) "OK"
  ["contentType":"HttpResponse":private] => string(31) "application/json; charset=UTF-8"
  ["httpStatusCode":"HttpResponse":private] => int(200)
}

这个数据块,让人有点摸不着头脑,尝试使用了以下方法都失败告终
1.强制转数组,(array) data
2.json_decode(json_encode(
data),true)
3.直接用$object->属性

最后在万能的度娘帮助下,使用了php的反射函数处理了数据

$ref = new ReflectionClass($response);
        $props = $ref->getProperties();
        $arr = [];


        foreach ($props as $prop) {
            $prop->setAccessible(true);
            $arr[$prop->getName()] = $prop->getValue($response);
            $prop->setAccessible(false);
        }

最后如愿得到了一个数组类型数据

array(7) {
  ["content"] => string(1048) "HTTP/1.1 200 OK
Server: Tengine
Date: Mon, 25 Jun 2018 02:22:15 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 52
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
Access-Control-Allow-Headers: X-Requested-With,X-Sequence,X-Ca-Key,X-Ca-Secret,X-Ca-Version,X-Ca-Timestamp,X-Ca-Nonce,X-Ca-API-Key,X-Ca-Stage,X-Ca-Client-DeviceId,X-Ca-Client-AppId,X-Ca-Signature,X-Ca-Signature-Headers,X-Ca-Signature-Method,X-Forwarded-For,X-Ca-Date,X-Ca-Request-Mode,Authorization,Content-Type,Accept,Accept-Ranges,Cache-Control,Range,Content-MD5
Access-Control-Max-Age: 172800
X-Ca-Request-Id: 271C3605-1937-4FF4-8D9B-D8E0F530875C
Cache-Control: no-store
X-Ca-Error-Message: OK
Vary: Accept-Encoding
ufe-result: A2
Pragma: no-cache
Via: 71aae546d43b[web,200]
Set-Cookie: SERVERID=c0cb0a9f805e8a70fc370b48877e9e8e|1529893335|1529893335;Path=/
X-Ca-Debug-Info: {"TotalLatency":105,"ServiceLatency":56}

{"code":601,"message":"数据不存在","data":null}"
  ["body"] => string(52) "{"code":601,"message":"数据不存在","data":null}"
  ["header"] => string(996) "HTTP/1.1 200 OK
Server: Tengine
Date: Mon, 25 Jun 2018 02:22:15 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 52
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
Access-Control-Allow-Headers: X-Requested-With,X-Sequence,X-Ca-Key,X-Ca-Secret,X-Ca-Version,X-Ca-Timestamp,X-Ca-Nonce,X-Ca-API-Key,X-Ca-Stage,X-Ca-Client-DeviceId,X-Ca-Client-AppId,X-Ca-Signature,X-Ca-Signature-Headers,X-Ca-Signature-Method,X-Forwarded-For,X-Ca-Date,X-Ca-Request-Mode,Authorization,Content-Type,Accept,Accept-Ranges,Cache-Control,Range,Content-MD5
Access-Control-Max-Age: 172800
X-Ca-Request-Id: 271C3605-1937-4FF4-8D9B-D8E0F530875C
Cache-Control: no-store
X-Ca-Error-Message: OK
Vary: Accept-Encoding
ufe-result: A2
Pragma: no-cache
Via: 71aae546d43b[web,200]
Set-Cookie: SERVERID=c0cb0a9f805e8a70fc370b48877e9e8e|1529893335|1529893335;Path=/
X-Ca-Debug-Info: {"TotalLatency":105,"ServiceLatency":56}

"
  ["requestId"] => string(36) "271C3605-1937-4FF4-8D9B-D8E0F530875C"
  ["errorMessage"] => string(2) "OK"
  ["contentType"] => string(31) "application/json; charset=UTF-8"
  ["httpStatusCode"] => int(200)
}

MySql执行过程、执行顺序、执行计划

前言:MySql是我们平常用的比较多的数据库,但是时间久了后总会或多或少忘记了一条sql完整的执行过程,今天查资料,重新理解了一下MySql语句执行的全过程。

导引:
一、执行过程
二、执行过程中的状态
三、执行的顺序
四、执行的计划
五、EXPLAIN的局限
六、总结

一、执行过程

MySql整体的执行过程如下图所示:

1.1:连接器

连接器的主要职责就是:

①负责与客户端的通信,是半双工模式,这就意味着某一固定时刻只能由客户端向服务器请求或者服务器向客户端发送数据,而不能同时进行,即同一时间只能进行一个动作

②验证请求用户的账户和密码是否正确,如果账户和密码错误,会报错:Access denied for user ‘root’@’localhost’ (using password: YES)

③如果用户的账户和密码验证通过,会在mysql自带的权限表中查询当前用户的权限:

mysql中存在4个控制权限的表,分别为user表,db表,tables_priv表,columns_priv表,mysql权限表的验证过程为:

1:User表:存放用户账户信息以及全局级别(所有数据库)权限,决定了来自哪些主机的哪些用户可以访问数据库实例;
Db表:存放数据库级别的权限,决定了来自哪些主机的哪些用户可以访问此数据库;
Tables_priv表:存放表级别的权限,决定了来自哪些主机的哪些用户可以访问数据库的这个表;
Columns_priv表:存放列级别的权限,决定了来自哪些主机的哪些用户可以访问数据库表的这个字段;
Procs_priv表:存放存储过程和函数级别的权限。

2:先从user表中的Host,User,Password这3个字段中判断连接的ip、用户名、密码是否存在,存在则通过验证。

3:通过身份认证后,进行权限分配,按照user,db,tables_priv,columns_priv的顺序进行验证。即先检查全局权限表user,如果user中对应的权限为Y,则此用户对所有数据库的权限都为Y,将不再检查db, tables_priv,columns_priv;如果为N,则到db表中检查此用户对应的具体数据库,并得到db中为Y的权限;如果db中为N,则检查tables_priv中此数据库对应的具体表,取得表中的权限Y,以此类推

4:如果在任何一个过程中权限验证不通过,都会报错

1.2:缓存

mysql的缓存主要的作用是为了提升查询的效率,缓存以key和value的哈希表形式存储,key是具体的sql语句,value是结果的集合。如果无法命中缓存,就继续走到分析器的的一步,如果命中缓存就直接返回给客户端 。不过需要注意的是在mysql的8.0版本以后,缓存被官方删除掉了。之所以删除掉,是因为查询缓存的失效非常频繁,如果在一个写多读少的环境中,缓存会频繁的新增和失效。对于某些更新压力大的数据库来说,查询缓存的命中率会非常低,mysql为了维护缓存可能会出现一定的伸缩性的问题,目前在5.6的版本中已经默认关闭了,比较推荐的一种做法是将缓存放在客户端,性能大概会提升5倍左右。

1.3:分析器

分析器的主要作用是将客户端发过来的sql语句进行分析,这将包括预处理与解析过程,在这个阶段会解析sql语句的语义,并进行关键词和非关键词进行提取、解析,并组成一个解析树。具体的关键词包括不限定于以下:select/update/delete/or/in/where/group by/having/count/limit等.如果分析到语法错误,会直接给客户端抛出异常:ERROR:You have an error in your SQL syntax.

比如:select * from user where userId =1234;

在分析器中就通过语义规则器将select from where这些关键词提取和匹配出来,mysql会自动判断关键词和非关键词,将用户的匹配字段和自定义语句识别出来。这个阶段也会做一些校验:比如校验当前数据库是否存在user表,同时假如User表中不存在userId这个字段同样会报错:unknown column in field list.

1.4:优化器

能够进入到优化器阶段表示sql是符合mysql的标准语义规则的并且可以执行的,此阶段主要是进行sql语句的优化,会根据执行计划进行最优的选择,匹配合适的索引,选择最佳的执行方案。比如一个典型的例子是这样的:

表T,对A、B、C列建立联合索引,在进行查询的时候,当sql查询到的结果是:select xx where B=x and A=x and C=x.很多人会以为是用不到索引的,但其实会用到,虽然索引必须符合最左原则才能使用,但是本质上,优化器会自动将这条sql优化为:where A=x and B=x and C=X,这种优化会为了底层能够匹配到索引,同时在这个阶段是自动按照执行计划进行预处理,mysql会计算各个执行方法的最佳时间,最终确定一条执行的sql交给最后的执行器。

1.5:执行器

在执行器的阶段,此时会调用存储引擎的API,API会调用存储引擎,主要有一下存储的引擎,不过常用的还是myisam和innodb:

引擎以前的名字叫做:表处理器(更直观)负责对具体的数据文件进行操作,对sql的语义比如select或者update进行分析,执行具体的操作。在执行完以后会将具体的操作记录到binlog中,需要注意的一点是:select不会记录到binlog中,只有update/delete/insert才会记录到binlog中。而update会采用两阶段提交的方式,记录都redolog中。

二、执行过程中的状态

可以通过命令:show full processlist,展示所有的处理进程,主要包含了以下的状态,表示服务器处理客户端的状态,状态包含了从客户端发起请求到后台服务器处理的过程,包括加锁的过程、统计存储引擎的信息,排序数据、搜索中间表、发送数据等。囊括了所有的mysql的所有状态,其中具体的含义如下图

三、执行的顺序

事实上,sql并不是按照我们的书写顺序来从前往后、左往右依次执行的,它是按照固定的顺序解析的,主要的作用就是从上一个阶段的执行返回结果来提供给下一阶段使用,sql在执行的过程中会有不同的临时中间表,一般是按照如下顺序:
例子:
select distinct s.id from T t join S s on t.id=s.id where t.name="Yrion" group by t.mobile having count(*)>2 order by s.create_time limit 5;

3.1:from

第一步就是选择出from关键词后面跟的表,这也是sql执行的第一步:表示要从数据库中执行哪张表。

实例说明:在这个例子中就是首先从数据库中找到表T

3.2:join on

join是表示要关联的表,on是连接的条件,先通过join确认要关联的表,再通过on确认关联条件。通过from和join on选择出需要执行的数据库表T和S,产生笛卡尔积,生成T和S合并的临时中间表Temp1。on:确定表的绑定关系,通过on产生临时中间表Temp2。

实例说明:找到表S,生成临时中间表Temp1,然后找到表T的id和S的id相同的部分组成成表Temp2,Temp2里面包含着T和Sid相等的所有数据。

3.3:where

where表示筛选,根据where后面的条件进行过滤,按照指定的字段的值(如果有and连接符会进行联合筛选)从临时中间表Temp2中筛选需要的数据,注意如果在此阶段找不到数据,会直接返回客户端,不会往下进行.这个过程会生成一个临时中间表Temp3。注意在where中不可以使用聚合函数,聚合函数主要是(min\max\count\sum等函数)。

实例说明:在temp2临时表集合中找到T表的name=”Yrion”的数据,找到数据后会成临时中间表Temp3,temp3里包含name列为”Yrion”的所有表数据。

3.4:group by

group by是进行分组,对where条件过滤后的临时表Temp3按照固定的字段进行分组,产生临时中间表Temp4,这个过程只是数据的顺序发生改变,而数据总量不会变化,表中的数据以组的形式存在。

实例说明:在temp3表数据中对mobile进行分组,查找出mobile一样的数据,然后放到一起,产生temp4临时表。

3.5:Having

对临时中间表Temp4进行聚合,这里可以为count等计数,然后产生中间表Temp5,在此阶段可以使用select中的别名。

实例说明:在temp4临时表中找出条数大于2的数据,如果小于2直接被舍弃掉,然后生成临时中间表temp5

3.6:select

对分组聚合完的表挑选出需要查询的数据,如果为*会解析为所有数据,此时会产生中间表Temp6

实例说明:在此阶段就是对temp5临时聚合表中S表中的id进行筛选产生Temp6,此时temp6就只包含有s表的id列数据,并且name=”Yrion”,通过mobile分组数量大于2的数据。

3.7:Distinct

distinct对所有的数据进行去重,此时如果有min、max函数会执行字段函数计算,然后产生临时表Temp7。

实例说明:此阶段对temp5中的数据进行去重,引擎API会调用去重函数进行数据的过滤,最终只保留id第一次出现的那条数据,然后产生临时中间表temp7。

3.8:order by

会根据Temp7进行顺序排列或者逆序排列,然后插入临时中间表Temp8,这个过程比较耗费资源。

实例说明:这段会将所有temp7临时表中的数据按照创建时间(create_time)进行排序,这个过程也不会有列或者行损失。

3.9:limit

limit对中间表Temp8进行分页,产生临时中间表Temp9,返回给客户端。

实例说明:在temp7中排好序的数据,然后取前五条插入到Temp9这个临时表中,最终返回给客户端

ps:实际上这个过程也并不是绝对这样的,中间mysql会有部分的优化以达到最佳的优化效果,比如在select筛选出找到的数据集。

四、执行的计划

4.1:什么是执行计划

执行计划就是sql的执行查询的顺序,以及如何使用索引查询,返回的结果集的行数

4.2:执行计划的内容

id

包含一组数字,表示查询中执行select子句或操作表的顺序。id一样,按照顺序执行;id越大,执行的优先级就越高(如子查询)

id相同,执行顺序由上至下


如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行


id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行

select_type

表示查询中每个select子句的类型(简单 OR复杂)

a.SIMPLE:查询中不包含子查询或者UNION
b.查询中若包含任何复杂的子部分,最外层查询则被标记为:PRIMARY
c.在SELECT或WHERE列表中包含了子查询,该子查询被标记为:SUBQUERY
d.在FROM列表中包含的子查询被标记为:DERIVED(衍生)
e.若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在 FROM子句的子查询中,外层SELECT将被标记为:DERIVED
f.从UNION表获取结果的SELECT被标记为:UNION RESULT

type

MySQL在表中找到所需行的方式,又称“访问类型”,常见类型如下:

由左至右,由最差到最好

  • a.ALL:
    Full Table Scan, MySQL将遍历全表以找到匹配的行

  • b.index:
    Full Index Scan,index与ALL区别为index类型只遍历索引树

  • c.range:
    索引范围扫描,对索引的扫描开始于某一点,返回匹配值域的行,常见于between、<、>等的查询,但是要控制查询的时间范围,一般查询数据不要超过数据总数的 15%

range访问类型的不同形式的索引访问性能差异

  • d.ref:
    非唯一性索引扫描,返回匹配某个单独值的所有行。常见于使用非唯一索引即唯一索引的非唯一前缀进行的查找。

  • e.eq_ref:
    唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描

  • f.const、system:
    当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量


system是const类型的特例,当查询的表只有一行的情况下, 使用system

  • g.NULL:
    MySQL在优化过程中分解语句,执行时甚至不用访问表或索引

possible_keys

指出MySQL能使用哪个索引在表中找到行,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用

key

显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。
TIPS:查询中若使用了覆盖索引,则该索引仅出现在key列表中。
覆盖索引:查询数据只需要通过索引就可以查询出,如55万条数据,使用索引,立刻可以查询出 2000条数据,同时Extra字段是Using index

key_len

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度


key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的

ref

表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

rows

表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数

Extra

包含不适合在其他列中显示但十分重要的额外信息

  • a.Using index

该值表示相应的select操作中使用了覆盖索引(Covering Index)


TIPS:覆盖索引(Covering Index)

MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件

包含所有满足查询需要的数据的索引称为 覆盖索引(Covering Index)

注意:
如果要使用覆盖索引,一定要注意select列表中只取出需要的列,不可select *,因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降

  • b.Using where

表示MySQL服务器在存储引擎受到记录后进行“后过滤”(Post-filter),
如果查询未能使用索引,Using where的作用只是提醒我们MySQL将用where子句来过滤结果集

  • c.Using temporary

表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询

  • d.Using filesort

MySQL中无法利用索引完成的排序操作称为“文件排序”
很多场景都是索引是一个字段,order by 排序的字段与索引字段不一致,导致的Using fileSort;
此时可以给排序字段和where条件字段,添加为组合索引,同时保证索引查询的数据不超过总量的15%,避免fileSort

注:回表的含义是,先根据索引查询数据,然后在根据确定的数据id和查询条件再去查询具体的数据的过程

五、EXPLAIN的局限

•EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况
•EXPLAIN不考虑各种Cache
•EXPLAIN不能显示MySQL在执行查询时所作的优化工作
•部分统计信息是估算的,并非精确值
•EXPALIN只能解释SELECT操作,其他操作要重写为SELECT后查看执行计划

六、总结

本文从MySql执行过程、执行过程的状态,到MySql的执行顺序、MySql的执行计划,一路做了详细的总结,理解这些有助于提高我们对mysql的总体理解和把握,有助于我们对sql语句进行优化,以及明白mysql中的sql语句从写出来到最终执行的轨迹。希望能帮到大家。

参考链接:
1.mysql执行过程以及顺序
2.MySql执行顺序及执行计划
3.MySQL执行计划解读

记一次数据库误删后恢复过程

故事背景:搭建测试站点时候,忘记修改数据库连接,删除数据时候直接把正式数据一锅端了。
跑路是不可能跑路的,那只能想办法恢复数据了;
登上服务器一看,上一次备份已经是3天前了。好家伙,自动备份也没设。╮(╯▽╰)╭
但是恢复还是可以恢复的,现在理清思路:
1.先把现在误删后的数据库备份一份(还是有部分记录没删的)
2.找到服务器上mysql二进制文件,查找上一次备份对应节点,和刚才删除操作前一个节点位置
3.找到到对应节点后,导入3天前备份,并利用二进制文件恢复这3天数据;
理清思路后,开干!

首先,我们把数据库上的mysql-bin拷贝到本地-.-
内心os:不然在服务器上看几百M的文件真的耗不起……….

cd /www/server/data  //cd到mysql data目录
ls(ll)指令查看data目录下文件信息  //cd到data目录后执行该指令

可见,最新的mysql-bin文件是000005
到此为止我们已经完美的查询到了mysql-bin文件,尔后我们对所需要的文件进行转存,代码如下:

//先进入到mysql bin目录
cd /www/server/mysql/bin
//将000005文件转存到/root目录中,且重命名为log05.log
./mysqlbinlog /www/server/data/mysql-bin.000005 > /root/log05.log

获取到log日志后,下载到本地,并找到对应节点后,先把3天前的备份导入
然后就可以开始恢复数据了

./mysqlbinlog  --start-position=31758225 --stop-position=53741915 -d youdatasbase /www/server/data/mysql-bin.000005|mysql -uroot -pxxx youdatasbase

自此,数据恢复完成

利用第三方翻译API翻译整个网站

最新一个项目需要全站翻译,但是有没有做国际化。改起来比较麻烦。所以就想有没有什么好的方法可以直接翻译整个网站。

思路:

利用第三方翻译API,把网页中所有的中文发过去,拿到返回值之后把所有中文替换成英文。

1 百度翻译开发平台注册

官网有很详细的问题,一步一步走就OK啦。附上它的官网。
http://api.fanyi.baidu.com/api/trans/product/index

2 怎么使用

直接上代码吧,注释也还算清晰。

<!doctype html>
<head>
    <meta charset="utf-8" />
</head>
<body>
    <div>测试
        <div>橘子</div>
    </div>
    <div>梨</div>
    <div>测试</div>
    <div>零食</div>
    <div>水果</div>
    <div>上班</div>
    <script src="js/jquery-1.7.1.min.js" type="text/javascript" charset="utf-8"></script>
    <script src="./js/md5.js"></script>
    <script type="text/javascript">
        transformLanguage('en');
        function transformLanguage(newLanguage) {
            // 获取所有dom元素中文
            let transformStr = '';
            // 获取所有dom元素
            function getChildDom(dom, type, data = {}) {
                if(type == 'read') {
                    [...dom.children].forEach(v => {
                        // 判断中文
                        // /^[\u0391-\uFFE5]+/
                        let re= /[\u4e00-\u9fa5]/g;
                        // 防止某些标签有内容并且有标签 ,或者有空格                        let vHtml =(v).contents().filter(function (index, content) {return content.nodeType === 3}).text().trim();
                        // 跳过script标签
                        if (re.test(vHtml) && v.tagName != 'SCRIPT') {
                            transformStr += `{vHtml},`
                        }
                        // 递归获取元素
                        getChildDom(v, type, data);
                    })
                }else {
                    let transOld = data.trans_result[0].src.split(',');
                    let transNew = data.trans_result[0].dst.split(',');
                    [...dom.children].forEach(v => {
                        // 判断中文
                        // /^[\u0391-\uFFE5]+/
                        let re= /[\u4e00-\u9fa5]/g;
                        let vHtml = (v).contents().filter(function (index, content) {return content.nodeType === 3}).text().trim();
                        // 跳过script标签
                        if (re.test(vHtml) && v.tagName != 'SCRIPT') {
                            // 防止标签里面还有标签,所以只替换里面的html,使用replace(v).html(
                                (v).html().replace(
                                transOld[transOld.findIndex(arrList => arrList == vHtml)]
                                ,
                                transNew[transOld.findIndex(arrList => arrList == vHtml)]
                                )
                            )
                        }
                        // 递归获取元素
                        getChildDom(v, type, data);
                    })
                }
            }
            getChildDom(document,'read');
            getTranslateData();
            // 获取翻译
            function getTranslateData() {
                let appid = '*******************';   // 百度翻译API的appid
                let key = '*********************';   // 百度翻译API的key
                let salt = (new Date).getTime();
                let query = transformStr;
                let from = 'zh';
                let to = newLanguage;
                let str1 = appid + query + salt + key;
                let sign = MD5(str1);.ajax({
                    url: 'http://api.fanyi.baidu.com/api/trans/vip/translate',
                    type: 'get',
                    dataType: 'jsonp',
                    data: {
                        q: query,
                        appid: appid,
                        salt: salt,
                        from: from,
                        to: to,
                        sign: sign
                    },
                    success: function(data) {
                        data.trans_result && getChildDom(document,'write',data);
                        console.log(data);
                    }
                });
            }
        }
    </script>
</body>

网上随便找一个jq,和md5.js放进去就好啦。
参考:利用第三方翻译API翻译整个网站

防止ssh暴力破解,自动提交黑名单

近日系统发现多例ssh登录失败记录,明显是有人ssh暴力破解,百度了下[1]参考,做了以下处理
一、系统:Centos

二、方法:读取/var/log/secure,查找关键字 Failed,例如(注:文中的IP地址特意做了删减):

Sep 17 09:08:09 localhost sshd[29087]: Failed password for root from 13.7.3.6 port 44367 ssh2
Sep 17 09:08:20 localhost sshd[29087]: Failed password for root from 13.7.3.6 port 44367 ssh2
Sep 17 09:10:02 localhost sshd[29223]: Failed password for root from 13.7.3.6 port 56482 ssh2
Sep 17 09:10:14 localhost sshd[29223]: Failed password for root from 13.7.3.6 port 56482 ssh2

从这些行中提取IP地址,如果次数达到10次(脚本中判断次数字符长度是否大于1)则将该IP写到 /etc/hosts.deny中。

三、步骤:

1、先把始终允许的IP填入 /etc/hosts.allow ,这很重要!比如:
sshd:19.16.18.1:allow
sshd:19.16.18.2:allow

2、脚本 /usr/local/bin/secure_ssh.sh

#! /bin/bash
cat /var/log/secure|awk '/Failed/{print (NF-3)}'|sort|uniq -c|awk '{print2"="1;}'>/usr/local/bin/black.list
for i in `cat  /usr/local/bin/black.list`
do
  IP=`echoi |awk -F= '{print 1}'`
  NUM=`echoi|awk -F= '{print 2}'`
  if [{#NUM} -gt 1 ]; then
    grep IP /etc/hosts.deny>/dev/null
    if [? -gt 0 ];then
      echo "sshd:$IP:deny" >> /etc/hosts.deny
    fi
  fi
done

3、将secure_ssh.sh脚本放入cron计划任务,每1分钟执行一次。

crontab -e

*/1 * * * * sh /usr/local/bin/secure_ssh.sh


这样脚本检查到暴力破解ip就会自动提交到黑名单禁止访问
最后做出总结:
– 修改默认22端口
– 禁止root用户登录,使用其他用户操作root命令[2]
– 使用秘钥登录ssh,不使用密码登录
– 添加上诉脚本自动提交黑名单,封禁IP