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

故事背景:搭建测试站点时候,忘记修改数据库连接,删除数据时候直接把正式数据一锅端了。
跑路是不可能跑路的,那只能想办法恢复数据了;
登上服务器一看,上一次备份已经是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

自此,数据恢复完成

php 7.2 安装 mcrypt 扩展: mcrypt 扩展从 php 7.1.0 开始废弃;自 php 7.2.0 起,会移到 pecl

升级 php 7.2 后,使用微信提供的加解密代码时,提示 call to undefined function mcrypt_module_open() ;大脑疯狂运转1秒钟后,得出结论:php 7.2的扩展有变动;查阅相关资料知晓,mcrypt 扩展从 php 7.1.0 开始废弃;自 php 7.2.0 起,会移到 pecl。还好,安装过程不复杂。

环境:centos 7

1.yum 安装依赖包:

yum install libmcrypt libmcrypt-devel mcrypt mhash

2.在 php 官网下载 mcrypt 包,php 扩展官网

# wget http://pecl.php.net/get/mcrypt-1.0.1.tgz

# tar xf mcrypt-1.0.1.tgz

# cd mcrypt-1.0.1

3.编译安装 mcrypt

进入到php安装目录

# /usr/local/php/bin/phpize

# ./configure –with-php-config=/usr/local/php/bin/php-config && make && make install

4.在php.ini加上扩展即可

extension=mcrypt.so

5.重启 php-fpm

/etc/init.d/php-fpm restart #可以根据实际情况修改,你也可能是/usr/local/php/sbin/php-fpm

composer安装laravel Changed current directory to没反应

根据官方手册执行composer global require “laravel/installer”

显示Changed current directory to C:/Users/Administrator/AppData/Roaming/Composer

然后没反应了,因为去国外的镜像地址下载了。

应该先将composer换成国内镜像

composer config -g repo.packagist composer https://packagist.phpcomposer.com

Windows下安装composer出错的原因

需要安装Composer,安装地址:https://pkg.phpcomposer.com/#how-to-install-composer

1.执行了代码1.执行了代码

php -r "copy('https://install.phpcomposer.com/installer', 'composer-setup.php');"

2.出错如下:

Warning: copy(): SSL operation failed with code 1. OpenSSL Error messages:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify faile
d in Command line code on line 1

Warning: copy(): Failed to enable crypto in Command line code on line 1

Warning: copy(https://install.phpcomposer.com/installer): failed to open stream:
 operation failed in Command line code on line 1

3.解决的方法

a.原因是在于没有CA证书导致的,下载CA证书:https://curl.haxx.se/docs/caextract.html

b.修改PHP.ini,指定下载的cacert.pem路径

openssl.cafile=D:\server\composer\certs\cacert.pem

各种场景唤起 App

唤起 App 应该是很常见的问题了。我们在开发 H5 的时候,有一些链路上的功能在 H5 不支持,只能去 App 才能完成。比如,下单、支付等功能。那么在更多的场景能够唤起 App 就显得很重要了。

几步走

  1. 判断浏览器,动态加载对应浏览器的下载逻辑
  2. 通过 universal link、URL Scheme、a 标签、iframe 几种方式找出最适合这个浏览器的唤起方式。
  3. 如果下载了 App,就会走打开逻辑,如果没有下载则走下载逻辑。
  4. 如果已知不能唤起的浏览器引导其它浏览器打开

流程

各个唤起方法对比

没有哪种方式是完美的,每种唤起方式都有它的优势跟劣势,只有将所有的唤起方法在不同浏览器上尝试过才能择优使用。

通用链接(universal link)

通用链接 universal link 是 IOS 9.0 版本以上才可以使用的新的特性。此特性类似于深层链接,并能够方便地通过打开一个 https 链接来直接启动您的客户端应用(手机有安装 App)。对比起以往所使用的 URL Scheme, 这种新特性在实现 web-app 的无缝链接时能够提供极佳的用户体验。

体验一下通用链接

我们可以来体验一下,左边的是通用链接 universal link方式,右边是 URL Scheme 方式。

左边可以直接将 App 打开,而右边需要确认是否打开才可以。

解决的bug:在 ios 12.3 版本之后,Safari 会有一个 bug,就是 URL Scheme 跟 download App 先后一起使用的话,会同时唤起 App 然后再进入 App store,体验极差。使用 universal link 可以完美解决这个问题。

通用链接配置

必须在根目录下或者 域名/.well-known 下面。注意 域名不要使用业务域名添加配置文件,必须要跨域使用,否则会会不能唤起App,直接打开页面。

  • 访问链接 我们假设有三个 App,分别是 appa、appb、appc,我们通过访问 https://jump.test.com/appa/index.html 就可以唤起 appa 了。
  • 配置下载页 如果没有安装 appa 的话就会跳转到 一个 404 页面,因为我们这个页面 https://jump.test.com/appa/index.html 其实并不存在,所以我们需要将这个页面在 nginx 代理到一个下载页面。
  • 疑问 那么你可能有一个疑问,我如果唤起来 app 之后这个页面会不会跳转到下载页呢?答案是不会的,如果唤起了 app 就不会继续跳转页面了,只有在唤不起来的时候才会进入下载页。
apple-app-site-association 配置
{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "ABCD.com.aaa.appa",
        "paths": ["appa/*"]
      },
      {
        "appID": "ABCD.com.aaa.appb",
        "paths": ["appb/*"]
      },
      {
        "appID": "ABCD.com.aaa.appc",
        "paths": ["appc/*"]
      },
      {
        "appID": "ABCD.com.aaa.*",
        "paths": ["*"]
      }
    ]
  }
}
通用链接的优缺点:
  • 优点:提供极佳的用户体验,使用起来也非常方便,解决了同时唤起 app 跟 app store 的问题
  • 缺点:只有在 ios 9 版本之上才能使用,个别浏览器不支持有兼容性问题

URL Scheme 方式

用法

根据协议来判断是哪个 app 唤起。

location.href = "testa://test.aaa.com/home";
判断是否唤起 app

因为唤起 app 并没有回调让我们知道唤起了 app,所以我们只能通过监听页面是否隐藏来实现判断是否唤起 app 的逻辑。当然这种做法还是有很大缺陷的,比如低端机型唤起速度很慢,高端机型要快得多,所以 delay 的时间要设置一个可以接受的值,我们暂时定的是 2500。还有如果用户没有安装 app,也会有 2500 的延时才能安装。

const timer = setTimeout(() => {
  this.__download(options);
}, options.delay);

const visibilitychange = function() {
  const tag = document.hidden || document.webkitHidden;
  tag && clearTimeout(timer);
};

document.addEventListener("visibilitychange", visibilitychange, false);
document.addEventListener("webkitvisibilitychange", visibilitychange, false);
window.addEventListener(
  "pagehide",
  function() {
    clearTimeout(timer);
  },
  false
);
URL Scheme 的优缺点:
  • 优点 1. 兼容性好,大部分机型都能兼容 2. 使用方便
  • 缺点 1. 不能判断是否已经下载了 app,需要自己写逻辑判断 2. 用户体验不如通用链接的方式。

a 标签跟 iframe 方式

这两种方式都是通过创建元素,通过元素属性的特性来做的

a 标签
const a = document.createElement("a");
a.setAttribute("href", __SCHEMA_PATH);
a.click();
iframe
const ifr = document.createElement("iframe");
iframe.src = path;
ifr.src = nativeUrl;
ifr.style.cssText = "display:none;border:0;width:0;height:0;";
document.body.appendChild(ifr);
setTimeout(function() {
  document.body.removeChild(ifr);
}, 1000);
优缺点
  • 优点 在个别场景中如果发现唤起 app 有问题,可以尝试这两种。
  • 缺点 iframe 基本上已经被废弃了,a 标签测试结果只在个别机型中可用。

总结

  1. 微信 必须配置白名单,或者使用 universal link 也可以在 IOS 唤起,表现形式有一点差别
const wxconfig = {
  debug: false,
  appId: conf.appId, // 公众号的 appid
  timestamp: conf.timestamp,
  nonceStr: conf.noncestr,
  signature: conf.signature,
  beta: true,
  jsApiList: ["launchApplication", "getInstallState"]
};
(window.wx && window.wx.config(wxconfig)) || (window.wxconfig = wxconfig);
  1. 百度浏览器必须用 universal link 方式才能在 IOS 唤起
  2. QQ 在 IOS 要使用 a 标签 的方式,即使通用链接的方式也不能将其唤起
if (isIOS) {
  const a = document.createElement("a");
  a.setAttribute("href", __SCHEMA_PATH);
  return a.click();
}
location.href = __SCHEMA_PATH;
  1. 微博,测试京东跟淘宝都可以调起,猜测是设置了白名单,目前没有找到好的办法,只能引导到其它浏览器中打开

测试结果

目前只有微博不能唤起,其它都可以正常唤起

场景 IOS 安卓 是否需要配置白名单
微信 ✔️ ✔️
qq ✔️ ✔️
chrome ✔️ ✔️
小米 browser ✔️ ✔️
uc ✔️ ✔️
qq browser ✔️ ✔️
360 browser ✔️ ✔️
猎豹 ✔️ ✔️
sogou ✔️ ✔️
baidu browser ✔️ ✔️
safari ✔️ ✔️
微博 X X

[1]各种场景唤起 App 骚操作

Mysql查询某个字段包含某个值

查询字段某一字段中包含某一ID的记录我们可以用以下的方法:

首先创建表:

CREATE TABLE users(id int(6) NOT NULL AUTO_INCREMENT,PRIMARY KEY (id),name VARCHAR(20) NOT NULL,limits VARCHAR(50) NOT NULL);

添加数据:

1、 INSERT INTO users(name, limits) VALUES('小张','1,2,12');

2、 INSERT INTO users(name, limits) VALUES('小王','11,22,32');

如何查询出limits字段中含有ID=2的记录呢,我们可以用以下的方法:

第一种方法:

SELECT * FROM users WHERE limits like "%2%";

执行后您会发现两条数据都被查询了出来,而第2条数据是我们不想要的,所以我们需要对查询进行优化,考虑所有情况:

SELECT * FROM users WHERE limits like "%,2,%," or "2,%" or "%,2" or "2";

另一种方法就是利用mysql 字符串函数 find_in_set()。

第二种方法:
SELECT * FROM users WHERE find_in_set('2', limits);

这一句即可解决。。

另需注意:mysql字符串函数 find_in_set(str1,str2)函数是返回str2中str1所在的位置索引,str2必须以”,”分割开。

Mysql中存储过程或函数使用select into语句给变量赋值没有匹配记录时的结果

前言

对select into语句感兴趣是因为看了项目中的一个存储过程引起的,在程序运行之前看了存储过程的逻辑,本以为没有数据时会报错,结果程序却正常运行,这说明我对select into语句理解的问题,同时也暴露了一个知识盲点,所以写了个小例子测试一下,并把测试的过程记录方便日后查找。

创建测试表格

为了更清楚的表明问题,我们创建的表格尽可能的简单,同时为了测试空值的情况,数据列我们不设置默认值,表格命名为’intotest’,创建语句如下:

CREATE TABLE `intotest` (
  `id` int(4) NOT NULL AUTO_INCREMENT,
  `number` int(4),
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=binary ROW_FORMAT=DYNAMIC;

插入测试数据

表格建立完成可以使用可视化工具或者insert语句插入测试数据,插入测试数据后查询结果如下:

mysql> select * from intotest;
+----+--------+
| id | number |
+----+--------+
|  1 |      1 |
|  2 |      2 |
|  3 |   NULL |
+----+--------+
3 rows in set (0.00 sec)

建立一个存储过程

我们建立一个用于测试的存储过程,主要的逻辑就是看看当select into语句找不到匹配记录时,被赋值的变量会怎么样,建立存储过程的代码如下:

CREATE PROCEDURE `select_into_value2`()
BEGIN
    DECLARE _value INT DEFAULT 0;

    SELECT number FROM intotest WHERE id=1 INTO _value;
    SELECT _value;
END

这个存储过程运行正常,配合刚才我们插入表格的记录可以知道,运行后的结果为1:

mysql> call select_into_value();
+--------+
| _value |
+--------+
|      1 |
+--------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

测试过程

当查询结果中不存在符合条件的记录时会怎样,修改存储过程定义,然后查看运行结果:

CREATE PROCEDURE `select_into_value2`()
BEGIN
    DECLARE _value INT DEFAULT 0;

    SELECT number FROM intotest WHERE id=5 INTO _value;
    SELECT _value;
END
mysql> call select_into_value();
+--------+
| _value |
+--------+
|      0 |
+--------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

结果为0,也就是说当查不到匹配结果时,不会执行select into的赋值效果。

当匹配到查询结果但是查询出来的数值为null会怎样,修改存储过程定义,然后查看运行结果:

CREATE PROCEDURE `select_into_value2`()
BEGIN
    DECLARE _value INT DEFAULT 0;

    SELECT number FROM intotest WHERE id=3 INTO _value;
    SELECT _value;
END
mysql> call select_into_value();
+--------+
| _value |
+--------+
|   NULL |
+--------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

结果为NULL,也就是说当查到匹配结果时,不管结果时什么都会赋值到指定的变量中(类型不匹配的sql错误除外)。

当连续查询赋值中间出现不匹配会怎样,修改存储过程定义,然后查看运行结果:

CREATE PROCEDURE `select_into_value2`()
BEGIN
    DECLARE _value INT DEFAULT 0;

    SELECT number FROM intotest WHERE id=2 INTO _value;
    SELECT number FROM intotest WHERE id=5 INTO _value;
    SELECT _value;
END
mysql> call select_into_value();
+--------+
| _value |
+--------+
|      2 |
+--------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

想必明白了前两种情况,这第三种也应该明白了,两条语句顺序执行,找到匹配的就赋值,找不到就放弃操作,结果就保留了上一次成功赋值的结果。

总结

关于select into语句赋值的规则就一句话,找到了符合条件的记录就赋值,找不到就算了。
在找到记录的前提下,如果类型不匹配会导致赋值失败并报错,比如查询到字符串赋值给整型变量。
————————————————
版权声明:本文为CSDN博主「AlbertS」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/albertsh/article/details/84176421

MySQL的 FOUND_ROWS() 函数和 ROW_COUNT() 函数

1.先填个坑
如果你要测试 FOUND_ROWS() 和 ROW_COUNT() 这两个函数,最好就不要用那些MySQL的图形化管理工具软件了(例如,SQLYog)。因为当你使用些工具软件执行某条SQL语句时,可能实际上并不仅仅是执行了这条SQL,这些软件同时会在后台自己执行一些其他SQL语句。所以有时你可能会发现这两个函数返回的结果和你预期的并不一样。所以呢,最好还是用 cmd 窗口来执行SQL进行测试。

2.FOUND_ROWS() 函数
(1) FOUND_ROWS()函数返回的是上一条 SELECT 语句(或 SHOW语句等)查询结果集的记录数。

注意,是上一条 SELECT 语句(即执行该函数前的最近一条SELECT语句),而不是上一条 SQL 语句;因为上一条SQL语句不一定是 SELECT 语句。

且,像 SELECT ROW_COUNT() 这种语句也是 SELECT 语句,它们的结果集也会被 FOUND_ROWS() 函数查出来。

(2)如果上一条 SELECT 语句查询结果为空,则返回 0。

(3)SHOW XXX(例如,show tables、show databases、show status)语句也会被 FOUND_ROWS() 函数查出来。

  1. ROW_COUNT() 函数
    (1)FOUND_ROWS()函数返回的是上一条SQL语句,对表数据进行修改操作后影响的记录数。

如果上一条SQL语句不是修改操作语句(INSERT/UPDATE/DELETE 等),而是查询语句(SELECT/SHOW 等)则返回-1。如果是修改操作语句,则返回修改(增/删/该)影响的记录数。

注意,这里是上一条SQL语句(即执行该函数前的上一条SQL语句),和上面有所区别。

(2)如果上一条SQL语句是UPDATE语句,但是UPDATE后所有数据的值并没有改变,则返回 0。

(3)如果上一条SQL语句是建表语句(创建表或临时表),但创建的是空表,则返回 0。

如果是删除表(DROP语句),则返回的还是 0。

(4)如果是创建临时表,但使用的是 AS 关键字直接将查询出来的值赋值给新建的临时表的话(其实就相当于新建了一个空表,紧接着使用了一条INSERT语句而已),则返回插入的记录数。

CREATE TEMPORARY TABLE tmp_sal AS
SELECT 
    *
FROM 
    employee 
WHERE 
    salary < 12000;

————————————————

原文链接:https://blog.csdn.net/zhou520yue520/article/details/81155248

MySql基础回顾之表定义

1.创建表

CREATE [TEMPORARY] TABLE tbl_name
(
字段名1 数据类型 [列级完整性约束条件] [默认值],
[字段名2 数据类型 [列级完整性约束条件] [默认值]]
[,…]
[表级完整性约束]
)[ENGINE=引擎类型]

CREATE TABLE  user
(
    `uid` int(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `name` varchar(10) NOT NULL DEFAULT '' COMMENT '姓名',
    `age` int(4) NOT NULL DEFAULT 0 COMMENT '年龄'
)

注:其中“TEMPORARY”为临时表选项,临时表不永久保留,断开连接即自动删除

2.更新表

在MySQL中,通过ALTER TABLE语句来更改原有表结构,常见操作有增加或删减列,创建或取消索引,更改原有列的数据类型、默认值,重命名表名或列名,更改标记注释或引擎类型,更改索引、外键等等

(1)ADD[COLUMN]子句

用于向表中增加列,可同时增加多列

ALTER TABLE USER
ADD `sex` CHAR(1) DEFAULT 1 COMMENT '1为男,0为女' AFTER 'age',
ADD `idcard` VARCHAR(20) COMMENT '身份证'

注:可用关键字“FIRST”将新列置于第一列,也可用关键字“AFTER” 使新列置于某列之后,如“AFTER ”

(2)CHANGE[COLUMN]子句

用于修改列的名称或数据类型

ALTER TABLE user
CHANGE age ages int(4)

注:使用change子句必须写上修改后的数据类型和数据长度,修改列的数据类型可能导致数据丢失或字符串长度不足被截断

(3)ALTER[COLUMN]子句

用于修改或删除列的默认值

ALTER TABLE user
ALTER age SET DEFAULT '1'

(4)MODIFY[COLUMN]子句

与CHANGE子句类型,但MODIFY只修改列数据类型,不修改列名;且可通过关键字“FIRST”,”AFTER”修改列的位置

ALTER TABLE user
MODIFY sex char(2)

(5)DROP[COLUMN]子句

用于删除列

ALTER TABLE user
DROP sex

(6)RENAME[TO]子句

用于为表重新命名

ALTER TABLE user
RENAME TO userinfo

3重命名表

(1)RENAME TABLE

除了使用上面ALTER TABLE语句,还可直接使用RENAME TABLE语句重命名表

RENAME TABLE tbl_name TO new_tbl_name
[,tbl_name2 TO new_tbl_name2]

4删除表

DROP[TEMPOPARY] TABLE[IF EXISTS]
tbl_name[,tal_name2][,...]

5查看表

(1)显示表名

SHOW[FULL] TABLES [{FROM|IN} db_name]
[LIKE 'pattern'|WHERE expr]

(2)显示表结构

SHOW [FULL] COLUMN {FROM|IN} tbl_name [{FROM|IN} db_name]
[LIKE 'pattern'|WHERE expr]
#或者
{DESCRIBE|DESC} tbl_name [col_name|wild]
#例如
DESC user;
DESC user name

通过IP获取IP内含信息

class Taobaoip {

/**
 * 淘宝IP地址库 Reset API
 * @author 
 * @param  [string] ip [IP地址]
 * @return [type]     [只返回获取成功的ip数据]
 */
    public function getLocation(ip) {
        if (empty(ip))ip = get_client_ip();
        taobaoUrl = "http://ip.taobao.com/service/getIpInfo.php?ip=";url       = taobaoUrl .ip;
        data      = self::httpRequest(url);
        data      = preg_replace("#\\\u([0-9a-f]{4})#ie", "iconv('UCS-2BE', 'UTF-8', pack('H4', '\\1'))",data);
        data      = json_decode(data, true);
        return data[data];
    }

    Static Private function httpRequest(url) {
        ch = curl_init();
        curl_setopt(ch, CURLOPT_URL, url);
        curl_setopt(ch, CURLOPT_RETURNTRANSFER, 1);
        output = curl_exec(ch);
        curl_close(ch);
        if (output === FALSE) {
            return "cURL Error: " . curl_error(ch);
        }
        returnoutput;
    }


}
#成功返回
#{"code":0,"data":{"ip":"113.70.24.1","country":"中国","area":"","region":"广东","city":"佛山","county":"XX","isp":"电信","country_id":"CN","area_id":"","region_id":"440000","city_id":"440600","county_id":"xx","isp_id":"100017"}}