MySQL 批量插入、批量更新

MySQL 批量插入/更新含有主键/唯一索引或者的解决方案是使用 on duplicate key update

解决方案

insert into database.table (id, key, field)
    values (v1, v2, v3), (v1, v2, v3), (v1, v2, v3)
    on duplicate key update
    id=values(id),key=values(key),field=values(field);

该方案适用范围广,
既可以批量插入,
也可以批量更新,
同时能保证避免插入重复数据,
还能避免影响自增 id,
又不需要创建临时表权限,
更不会修改已存在记录的其他字段。


实际应用中 update 后跟随的字段可以进行调整,保留只需要更新的字段


如果表中有自增 id,这个方案还有一点需要注意,insert into … values 跟随的记录数 (numberA)即为此次自增数 (numberA),即使存在重复唯一索引导致实际插入记录数(numberB)少于跟随的记录数(numberA)。


其他插入方案

1. insert ignore into

insert ignore 遇到重复数据会忽略,可以有效保证不插入重复数据,但也会忽略其他错误。
批量插入之后再进行单条更新。

# 批量插入,需要保证 NOT NULL 字段有值
insert into database.table (id, key, field)
    values (v1, v2, v3), (v1, v2, v3), (v1, v2, v3);

# 单条更新
update database.table set field = v3 where key = v1;
update database.table set field = v3 where key = v1;
update database.table set field = v3 where key = v1;

2. replace into

如果表中有主键或唯一索引,replace into 会删除冲突行(已存在记录),再新插入新行。因此导致 replace into 可能会导致主键自增 1。
如果表中没有主键和唯一索引,replace into 没有意义。

如果表中没有自增主键,使用 replace into 是一个不错的选择。

使用 replace 需要注意,如果 更新的字段 不全,会被设置为缺省值,也就是会清除其他字段。

replace into database.table (id, key, field)
    values (v1, v2, v3), (v1, v2, v3), (v1, v2, v3);

最后,推荐使用 insert into … on duplicate key update

on duplicate key update 是 mysql 特有语法。

MySQL root 密码丢失重置

前几周百度云同步时把重要文件误删,包括 mysql root 密码。

MySQL root 密码丢失,需要以类似 mysqld_safe –skip-grant-tables 方式运行,才能让 mysql -uroot 顺利执行,登录到 mysql 命令行。

如果 mysql 在运行,先将 mysql 运行方式记录下来

> ps aux | grep mysql
// 假设当前 mysql 运行命令如下,实际参数可能更复杂
/mysql/bin/mysqld --pid-file=/mysql/mysql.pid --socket=/tmp/mysql.sock

1. 关闭 mysql 进程

不要使用 kill 命令直接结束,而是通过 stop 参数结束 mysql 进程。

// 假设 mysql 启动命令是
// /etc/init.d/mysql start
// 则应该通过如下方式结束 mysql 进程
> /etc/init.d/mysql stop

2. 以 –skip-grant-tables 方式启动 mysql

在原有 mysql 运行命令基础上加上 –skip-grant-tables &,& 表示放入当前会话 background。

/mysql/bin/mysqld --pid-file=/mysql/mysql.pid --socket=/tmp/mysql.sock --skip-grant-tables &

3. 登录 mysql 修改 root 密码

> mysql -uroot
> update mysql.user set password=PASSWORD('MysqlRoot') where user='root';

4. 参考第 1 步,结束 mysql 进程

5. 执行原有 mysql 启动命令

// 假设 mysql 启动命令如下
> /etc/init.d/mysql start

使用 root 新密码登录 mysql

> mysql -uroot -p
Enter password: 

密码重置完成。

重置过程有两点需要注意:

  1. 不推荐使用 kill 结束 mysql 进程;
  2. 以 –skip-grant-tables 方式运行 mysql 需要保留原有参数

Linux 常见解压与压缩

Linux 压缩与解压命令

格式 解压 压缩
 
tar
(仅打包不压缩)
tar -xvf [原文件名].tar tar -cvf [目标文件名].tar [原文件名/目录名]
 
tar.bz2 bunzip2 [原文件名].tar.bz2 bzip2 [原文件名].tar
tar.bz2
(通过 tar 调用)
tar -jxvf [原文件名].tar.bz2 tar -jcvf [目标文件名].tar.bz2 [原文件名/目录名]
 
tar.gz gunzip [原文件名].tar.gz gzip [原文件名].tar
tar.gz
(通过 tar 调用)
tar -zxvf [原文件名].tar.gz tar -zcvf [目标文件名].tar.gz [原文件名/目录名]
 
tar.xz unxz [原文件名].tar.xz xz [原文件名].tar
tar.xz
(通过 tar 调用)
tar -Jxvf [原文件名].tar.xz tar -Jcvf [目标文件名].tar.xz [原文件名/目录名]
 
tar.Z
(已过时)
uncompress [原文件名].tar.Z compress [原文件名].tar
tar.Z
(通过 tar 调用)
tar -Zxvf [原文件名].tar.Z tar -Zcvf [目标文件名].tar.Z [原文件名/目录名]
 
7z 7z x [原文件名].7z 7z a [目标文件名].7z [原文件名/目录名]
 
jar jar -xvf [原文件名].jar jar -cvf [目标文件名].jar [原文件名/目录名]
 
zip unzip [原文件名].zip zip -r [目标文件名].zip [原文件名/目录名]

JavaScript 浮点数计算问题

JavaScript 浮点数神坑当属 0.1 + 0.2 == 0.3false

> 0.1 + 0.2 == 0.3
false
> 0.1 + 0.2
0.30000000000000004
>

还有一些比较隐蔽的问题,比如 Math.roundNumber.prototype.toFixed 也都不是能完全正常工作的。

> Math.round(1.105 * 100)
111
> Math.round(1.015 * 100)
101
> Math.round(1.025 * 100)
102
> 0.25.toFixed(1)
'0.3'
> 0.35.toFixed(1)
'0.3'

Math.round、toFixed 计算出错主要因为浮点数不能精确表示。

在这里 0.35 和 1.015 的值都不准确,一个办法是转换成整数计算再除以对应的十百千;另一个办法是采用现有的 lib,比如 accounting

> 1.015 * 100
101.49999999999999

代码运行环境

D:\node -v
v8.1.0

安装 Nginx、Lua,使用 Lua 扩展 Nginx

使用 lua-nginx-module 可以让 Nginx 通过一些指令如 content_by_lua、content_by_lua_file 运行 Lua 脚本,非常方便。

先下载所需文件

1. 安装 luajit,官网 http://luajit.org/install.html

解压之后直接 make

make PREFIX=/usr/local/luajit

2. 下载 ngx_devel_kit(github)、nginx_lua_module(github

3. 进入 nginx 源码目录准备 configure

如果已经安装 nginx,也可以再次 configure,但一定要涵盖上次使用的参数。

4. 导出 LUAJIT_LIB、LUAJIT_INC 环境变量

export LUAJIT_LIB=/usr/local/luajit/lib
export LUAJIT_INC=/usr/local/luajit/include/luajit-2.0

LUAJIT_LIB 对应 libluajit-5.1.so 文件所在目录
LUAJIT_INC 对应 lua.h 文件所在目录

目录可能不一样,但参考 LUAJIT_LIB、LUAJIT_INC 含义。

5. 在 nginx-1.10.2 目录执行 ./configure

./configure \
--prefix=/usr/local/nginx \
--with-http_flv_module \
--with-http_gzip_static_module \
--with-http_realip_module \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_v2_module \
--with-ipv6 \
--with-ld-opt="-ljemalloc,-Wl,-rpath,/usr/local/luajit/lib" \
--with-openssl=/opt/src/openssl-1.0.2l \
--with-pcre=/opt/src/pcre-8.40 \
--with-pcre-jit \
--add-module=/opt/src/ngx_devel_kit-0.3.0 \
--add-module=/opt/src/lua-nginx-module-0.10.8

这里用到了 ljemalloc、openssl、pcre 可以预先安装

安装 jemalloc,进入 jemalloc-4.2.1

./configure
make
make install

安装 openssl pcre,或者进入源码目录安装

yum install openssl openssl-devel
yum install pcre pcre-devel

6. 测试执行 lua

在 Nginx 某个 server 下增加

location /hello { 
    content_by_lua 'ngx.say("Hello, Lua")'; 
}

尝试访问

[root@CentOS-58 06]# curl lua.zhengxianjun.com/hello
Hello, Lua!

安装成功并且正常运行。

如果对 nginx 安装目录进行版本管理,会发现 make install 完成之后只有 nginx/sbin/nginx 文件有改动。

安装完成之后切记要执行 sbin/ngxin -t 检查语法是否正确。
一定要安装正确才能执行 sbin/ngxin -s reload 。

因为对于已经安装的 nginx,如果 ./configure 没有覆盖上一次的参数,会导致某些功能失效。
第一次 configure 时没有添加 –with-http_ssl_module 导致 nginx 提示 unknown directive “ssl”。

JavaScript const、let、var 对比

ECMAScript 6 新增 const 和 let 命令,用来声明变量。

声明方式 变量提升 作用域 初始值 重复定义
const 块级 需要 不允许
let 块级 不需要 不允许
var 函数级 不需要 允许

变量提升:const 和 let 必须先声明再使用,不支持变量提升

console.log(c1, l1, v1);
// 报错
// Uncaught ReferenceError: c1 is not defined

const c1 = 'c1';
let l1 = 'l1';
var v1 = 'v1';

作用域:const,let 支持块级作用域,有效避免变量覆盖

const c21 = 'c21';
let l21 = 'l21';
var v21 = 'v21';

if (0.1 + 0.2 != 0.3) {
    const c21 = 'c22';
    let l21 = 'l22';
    var v21 = 'v22';

    console.log(c21, l21, v21);
    // 输出 c22 l22 v22
}

console.log(c21, l21, v21);
// 输出 c21 l21 v22

块级作用域,在外层不能直接访问内层变量

if (0.1 + 0.2 != 0.3) {
    const c22 = 'c22';
    let l22 = 'l22';
    var v22 = 'v22';

    console.log(c22, l22, v22);
    // 输出 c22 l22 v22
}

console.log(c22, l22, v22);
// 报错
// Uncaught ReferenceError: c22 is not defined
// 同样地, l22 is not defined

const 定义常量,该常量不能赋值,但该常量的属性可以赋值

const c231 = {};
const c232 = [];

c231.name = 'seven';
c232.push(27);

console.log(c231, c232);
// 输出 {name: "seven"} [27]

// 禁止给对象赋值,应该使用 Object.freeze

const c233 = Object.freeze({});
const c234 = Object.freeze([]);

c233.name = 'seven';
// 普通模式下不报错
// 严格模式下报错
// Uncaught TypeError: Cannot add property name, object is not extensible
    
c234.push(27);
// 普通模式下就会报错
// Uncaught TypeError: Cannot add property 0, object is not extensible

console.log(c233, c234);
// 输出 {} []

全局变量不再设置为顶层对象(window)的属性,有效避免全局变量污染

const c24 = 'c24';
let l24 = 'l24';

console.log(c24, l24);
// 输出 c24 l24

console.log(window.c24, window.l24);
// 输出 undefined undefined

符合预期的 for 循环

for (var i = 0; i != 3; i++) {
    setTimeout(function() {
        console.log(i);
    },10);
}
// 依次打印
3
3
3

for (let i = 0; i != 3; i++) {
    setTimeout(function() {
        console.log(i);
    },10);
}
// 依次打印,为啥呢
0
1
2

可以看到在 for 循环中使用 let 方式声明变量才是符合预期。
在 for 中每一次循环,let 都是重新声明变量,并且因为 JavaScript 引擎会记住上一次循环的值,初始化 i 时在上一轮的基础上计算。

可以看到在 for 循环中至少有两层作用域,看下面的例子更容易理解。

for (let i = 0; i != 3; i++) {
    let i = 'seven';
    console.log(i);
}
console.log('eight');
// 依次打印
seven
seven
seven
eight

初始值:const 声明的变量必须设置初始值,且不能重复赋值。

const c3 = 'c3';
let l3 = 'l3';
var v3 = 'v3';

console.log(c3, l3, v3);
// 输出 c3 l3 v3

c3 = 2; // Uncaught TypeError: Assignment to constant variable
l3 = 2;
v3 = 2;

console.log(c3, l3, v3);
// 输出 c3 2 2

const c32;
// 报错
// Uncaught SyntaxError: Missing initializer in const declaration

重复定义:const 和 let 不支持重复定义

const、let 缩小了变量作用域,完美避免变量污染;const 固定变量(即固定变量类型),对于弱类型 JavaScript 来说,可以明显提升性能。推荐在应用中使用 const、let 声明变量。

babel-loader 生成多处 /******/ 前缀

使用 babel-loader 处理 .js 文件之后会在公共文件里多处行首添加 /******/,不明白为什么要这样做。

难道仅仅是为了区别框架代码和用户代码?

/******/ (function(modules) { // webpackBootstrap
/******/    // install a JSONP callback for chunk loading
/******/    var parentJsonpFunction = window["webpackJsonp"];
/******/    window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) {
/******/        // add "moreModules" to the modules object,
/******/        // then flag all "chunkIds" as loaded and fire callback
/******/        var moduleId, chunkId, i = 0, callbacks = [];
/******/        for(;i < chunkIds.length; i++) {
/******/            chunkId = chunkIds[i];
/******/            if(installedChunks[chunkId])
/******/                callbacks.push.apply(callbacks, installedChunks[chunkId]);
/******/            installedChunks[chunkId] = 0;
/******/        }
/******/        for(moduleId in moreModules) {
/******/            modules[moduleId] = moreModules[moduleId];
/******/        }
/******/        if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules);
/******/        while(callbacks.length)
/******/            callbacks.shift().call(null, __webpack_require__);
/******/        if(moreModules[0]) {
/******/            installedModules[0] = 0;
/******/            return __webpack_require__(0);
/******/        }
/******/    };

目前应该没有参数可以控制移除,因为在文件 ./node_modules/webpack/lib/MainTemplate.js 中硬编码了这一段前缀

this.plugin("render", function(bootstrapSource, chunk, hash, moduleTemplate, dependencyTemplates) {
    var source = new ConcatSource();
    source.add("/******/ (function(modules) { // webpackBootstrap\n");
    source.add(new PrefixSource("/******/", bootstrapSource));
    source.add("/******/ })\n");
    source.add("/************************************************************************/\n");
    source.add("/******/ (");
    var modules = this.renderChunkModules(chunk, moduleTemplate, dependencyTemplates, "/******/ ");
    source.add(this.applyPluginsWaterfall("modules", modules, chunk, hash, moduleTemplate, dependencyTemplates));
    source.add(")");
    return source;
});

babel-loader 配置参考: API · Babel