image frame

脑洞大开の乱乱想

放下碎碎念,重新出发

缓存

  1. service worker
  2. memory cache
  3. disk cache
  4. push cache 主要是http2,缓存时间短
  5. 网络请求

浏览器缓存

强缓存,协商缓存,通过http header实现。

强缓存

通过 expirescache-control 实现
通过Cache-control过期30秒

expires是http1的,过期时间是本地时间。

1
2
Expires: Web,22 xxx
Cache-control: max-age=30

常见的值

协商缓存

如果缓存过期了,就需要发请求验证是否有更新。

  • last-modified 最后修改时间,问题不能打开缓存文件,最小时间秒
  • eTag指纹。比较新。

频繁变动的资源 cache-control: no-cache,每次都会请求服务器,然后根据 eTag 觉得是否重新拉取数据。虽然数据请求多,但是返回的内容少。

http缓存

  1. service worker
  2. memory cache
  3. disk cache
  4. push cache 主要是http2,缓存时间短
  5. 网络请求

浏览器缓存

Cache-Control:
请求时候的值:

  • no-cache
  • no-store
  • max-age
  • max-stable
  • min-fresh
  • only-if-cached

响应时候的值:

  • public private
  • no-cache no-store
  • no-transform
  • must-revalidate
  • proxy-revalidate
  • max-age
  • s-maxage

强缓存,协商缓存,通过http header实现。

强缓存

通过 expirescache-control 实现
通过Cache-control过期30秒

expires是http1的,过期时间是本地时间。

1
2
Expires: Web,22 xxx
Cache-control: max-age=30

常见的值

协商缓存

如果缓存过期了,就需要发请求验证是否有更新。

  • last-modified 最后修改时间,问题不能打开缓存文件,最小时间秒
  • etag指纹。比较新。

频繁变动的资源 cache-control: no-cache,每次都会请求服务器,然后根据 etag 觉得是否重新拉取数据。虽然数据请求多,但是返回的内容少。

Puppeteer 学习笔记

Puppeteer 学习笔记

查看中文文档:

中文文档 官方推荐的

可以在线使用puppeteer

安装

首先初始化项目 npm init -y

在安装 puppeteer的时候会自动下载一个 Chromium,但网址被墙没办法下载,先安装中国镜像源。

1
2
3
4
npm i -g mirror-config-china --registry=https://registry.npm.taobao.org
# 检查是否安装成功
npm config list
npm i puppeteer

后来推出了puppeteer-core包,不在捆绑下载浏览器,可以使用电脑自带的chrome,只要保证本机的chrome版本不要太低就可以。

1
npm i puppeteer-core

在项目启动的时候设置chrome路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const getDefaultOsPath = () => {
if (process.platform === 'win32') {
return 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'
} else {
return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
}
}


const getDefaultOsPath = require('./getDefalutOsPath')
module.exports = {
executablePath: getDefaultOsPath(),
userDataDir: 'test-profile-dir',
headless: false,
defaultViewport: {
width: 1000,
height: 800
},
args: [
'–disable-gpu',
'–disable-dev-shm-usage',
'–disable-setuid-sandbox',
'–no-first-run',
'–no-sandbox',
'–no-zygote',
'–single-process'
]
}

跑一个demo

创建个 demo1.js 来试跑一下。

1
2
3
4
5
6
7
8
9
10
// demo1.js
const puppeteer = require('puppeteer-core');

(async () => {
const browser = await puppeteer.launch(launchOptions);
const page = await browser.newPage();
await page.goto("http://www.baidu.com");
await page.screenshot({ path: "baidu.png" });
browser.close();
})();

bash页面执行:

1
node demo1.js

如果没有报错的话,就是打开了浏览器,并截图。这样就安装成功了。
代码文件命名为 demo1.js 可以在目录里查找。

如果下载有问题,就可以考虑使用本地的chrome,参考上面。

这样就掌握基本的用法了。

经验技巧

sleep函数

1
2
3
4
5
6
7
8
9
10
11
12
13
// 延时器
let timeout = function (delay) {
console.log('延迟函数:', `延迟 ${delay} 毫秒`)
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(1)
} catch (error) {
reject(error)
}
}, delay);
})
}

Linux下缺少依赖

有可能在Linux下因为缺少依赖,安装不上,考虑安装下面的依赖文件:

1
gconf-service libasound2 libatk1.0-0 libatk-bridge2.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget

Linux 下中文不支持

考虑安装以下依赖,来支持中文,避免乱码:

1
sudo apt-get install -y --force-yes --no-install-recommends fonts-wqy-microhei ttf-wqy-zenhei

模拟手机

模拟手机打开页面

1
2
3
4
5
6
7
8
const puppeteer = require("puppeteer");
const devices = require("puppeteer/DeviceDescriptors");
const iPhone6 = devices["iPhone 6"];

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.emulate(iPhone6);
await page.goto("http://baidu.com");
1
await page.setViewport({ width: 1280, height: 800 });

拦截请求

不想加载图片和js,想让页面打开更快。可以考虑拦截

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 页面设置拦截
await page.setRequestInterception(true);

// 过滤拦截
page.on("request", interceptedRequest => {

// 符合匹配就 abort ,其他 continue
// 这里是操作的 url 字符串
if (
interceptedRequest.url().endsWith(".png") ||
interceptedRequest.url().endsWith(".jpg") ||
interceptedRequest.url().endsWith(".js") ||
["image", "script"].indexOf(interceptedRequest.resourceType()) !== -1
) {
interceptedRequest.abort();
} else {
interceptedRequest.continue();
}
await page.goto("http://www.wangxiao.cn/cfe/dt_bm/");

//demo2

const browser = await puppeteer.launch();
const page = await browser.newPage();
const resList = []
await page.setRequestInterception(true);
page.on('request', request => {
if (request.resourceType() === 'image')
// request.abort();
resList.push(request.url())
request.continue();
// else
// request.continue();
});
await page.goto('https://news.google.com/news/');
// await page.screenshot({ path: 'news.png', fullPage: true });
await browser.close();

获取页面源代码

1
2
await page.goto("https://www.youtube.com", { waitUntil: "networkidle0" });
const html = await page.content();

屏幕滚动

解决懒加载和数据滚动加载,这就是我认为 puppeteer 最有魅力的地方了。
以往只能去慢慢分析页面结构、研究接口api ,来获取异步加载的数据,现在直接无脑模拟浏览器,滚动页面来获取页面内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
await page.goto("https://www.jd.com/", {
waitUntil: "networkidle2"
});
await autoScroll(page);
async function autoScroll(page) {
await page.evaluate(async () => {
await newPromise((resolve, reject) => {
var totalHeight = 0;
var distance = 200;
var timer = setInterval(() => {
var scrollHeight = document.body.scrollHeight;
window.scrollBy(0, distance);
totalHeight += distance;
if (totalHeight >= scrollHeight) {
clearInterval(timer);
resolve();
}
}, 200);
});
});
}

操作页面

在浏览器中使用自己的js 这里好像没办法传入自己的function

1
2
3
4
5
6
7
8
const result = await page.$eval("#hotwords", el => {
return el.innerText;
});
// 或者
const result = await page.evaluate(() => {
let element = document.querySelector("#hotwords");
return element.innerText;
});

键盘方法

1
2
3
4
5
6
7
8
9
await page.type("#search input", "macbook pro", {
delay: 300
});
await page.keyboard.press("Enter");
// 或者
const inputEle = await page.$("#search input");
await inputEle.type("macbook pro", {
delay: 300
});

键盘大写、快捷键

1
2
3
4
await page.focus("input");
await page.keyboard.down("Shift");
await page.keyboard.press("KeyM");
await page.keyboard.up("Shift");

等待节点

想等到某个元素出现

1
await page.waitForSelector('div.title > a'); //等待节点出现

launch 选项

headless: false
slowMo: 200 减速显示,有时候为了模拟人会特意减速
devtools: true 显示dev工具

页面中的iframe

如果网页内有用iframe等标签,这时page对象是无法读取 iframe 里面的内容的,需要用到page.frames()。返回一个Frame对象数组。 通常iframe会有name属性,判断name属性可以快速获取单个Frame对象的内容。

1
2
let iframe = await page.frames();
iframe.find(f => f.name() === 'name')

监听 console的输出

想看页面里的console,可以监听

1
2
3
4
page.on('console',msg => console.log(msg.text());
await page.evaluate(() =>{
console.log()
})

使用 coverage trace

可以使用 chrome自带的分析工具来分析页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const puppeteer = require("puppeteer");
puppeteer.launch().then(async browser => {
const page = await browser.newPage();
//start coverage trace
await Promise.all([
page.coverage.startJSCoverage(),
page.coverage.startCSSCoverage()
]);
await page.goto("https://www.cnn.com");
//stop coverage trace
const [jsCoverage, cssCoverage] = await Promise.all([
page.coverage.stopJSCoverage(),
page.coverage.stopCSSCoverage()
]);
let totalBytes = 0;
let usedBytes = 0;
const coverage = [...jsCoverage, ...cssCoverage];
for (const entry of coverage) {
totalBytes += entry.text.length;
for (const range of entry.ranges)
usedBytes += range.end - range.start - 1;
}
const usedCode = ((usedBytes / totalBytes) * 100).toFixed(2);
console.log("Code used by only", usedCode, "%");
await browser.close();
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const puppeteer = require("puppeteer");
const devices = require("puppeteer/DeviceDescriptors");
const iPhonex = devices["iPhone X"];
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.emulate(iPhonex);
//start the tracing
await page.tracing.start({ path: "trace.json", screenshots: true });
await page.goto("https://www.bmw.com");
//stop the tracing
await page.tracing.stop();
await browser.close();
})();

简言之,在goto页面之前,打开特定的功能

监控页面

监控页面。写一个app.js setInerval 每隔5分钟运行一次,截图页面,如果有问题可以存下来,再说其他task
也可以使用 node-cron 来制作定时任务

关于新开页面

如果a链接,新开了页面。
如果a标签target属性为_blank,新开了页面,可以使用let pages = await browser.pages(),它返回当前的页面的page类数组集合,想要哪个页面数组中拿就行,不过原来的page还是一直指向原来的页面。

wsl 使用

Hi! Here is how I got it running under WSL:
install chromium browser through apt-get
$ sudo apt-get install chromium-browser
then:
const browser = await puppeteer.launch({executablePath:’/usr/bin/chromium-browser’});

从网络请求到浏览器渲染优化

网络是如何连接的?

发送过程

这个看《http》书上有,先简单整理一下。

dns

发出请求之后,通过dns服务器查找对应的ip
有了dns,把域名转换成ip

浏览器渲染过程

DOM tree – Render Tree – Layout

收到html字符串转为 dom tree

很显然,有些计算机基础知识。收到的是01字节,从字节变成字符串,然后按照规则 token 词法分析变成node节点,然后变成 dom树。

加载的过程中会遇到 css和js

css文件变成 cssom tree

css查找消耗资源,而且 div > p >span 是倒着走的,不断递归,所以尽可能扁平化。

渲染

有了dom,但不一定展示出来,比如display 。 这就是布局部分了。

dom为什么慢

都说代价昂贵。啥意思?
通过js修改dom,js要改,渲染引擎要改,涉及到了两线程通信。
插入一万个列表,如何不卡顿?
分批次渲染,视野外的不显示。

阻塞渲染

js会停下来阻塞渲染,所以一般把js当道最后。也可以添加 async异步, defer并行下载

渲染优化

  • 重排 回流 reflow 减少
  • 重绘repaint 不可避免
  • 布局layout

重排 reflow

DOM中每个元素都有自己的盒子模型,需要浏览器根据样式来计算,放到该出现的位置,这个过程叫 reflow

触发 reflow:

  • 增加删除修改 DOM节点,会导致reflow repaint
  • 移动DOM的位置,或做动画
  • 修改css样式
  • resize窗口时候,或滚动时候
  • 修改默认字体时候

重绘 repaint

等到各种盒子位置等属性确定之后,浏览器把元素绘制页面

  • DOM 改动
  • CSS改动

降低重绘次数。创建节点少,一次完成。

运行机制

setTimeout 0 会放到任务队列。单线程。

当 while 和 setTimeout 在一起会怎样?while是同步。

《css世界》笔记

css世界,张鑫旭,人民邮电出版社,2017-12

微信读书上。

流式布局,往往具有自适应性。

srcset object-fit

长度单位。 <number> + 长度单位 = length

未定义行为,比如firefox 认为 先 mousedown 再 css:active

流 元素 基本尺寸

块级元素

http协议细节

HTTP

特点

无连接,无状态。简单快速,灵活。
连接一次就断开。每次连接无法区分状态。

超文本传输协议。

请求过程

这里我们看一个请求的发出和响应。去请求百度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
➜  curl -v http://www.baidu.com/ #用户发起一个请求
* Trying 182.61.200.7...
* TCP_NODELAY set
* Connected to www.baidu.com (182.61.200.7) port 80 (#0) # 接下来是请求报文
> GET / HTTP/1.1 # 请求行
> Host: www.baidu.com # 请求头
> User-Agent: curl/7.58.0
> Accept: */*
> # 空行
< HTTP/1.1 200 OK # 响应报文
< Accept-Ranges: bytes # 以下都是响应头
< Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
< Connection: Keep-Alive
< Content-Length: 2381
< Content-Type: text/html
< Date: Mon, 04 Nov 2019 14:00:38 GMT
< Etag: "588604c8-94d"
< Last-Modified: Mon, 23 Jan 2017 13:27:36 GMT
< Pragma: no-cache
< Server: bfe/1.0.8.18
< Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
< # 空行
<!DOCTYPE html> # 以下是响应体
</html>
* Connection #0 to host www.baidu.com left intact # 断开

请求内容

  • 请求行
  • 实体

    方法

状态码

  • 1xx
  • 200 ok
  • 206 客户端range。 video 时候206
  • 301 永久重定向
  • 302 临时重定向
  • 304 有缓存
  • 400 语法错误
  • 401 未授权
  • 403 禁止访问
  • 404 不存在
  • 5xx 服务器问题

持久连接

http1.1 通过 keep-alive中间不断开。

管线化。先打包请求,打包一次响应。只有get和head可以进行管线化,由于各种问题,现代浏览器默认不开启管线化。

TLS

加密,对称加密,非对称加密
对称加密:两边都有密钥,都知道怎么加密解密。如果密钥在网络传输中被拦截,就不行了

非对称加密:公钥 私钥。用公钥加密,用私钥解密,私钥只有发公钥的才知道。

http/2

多路复用

以前为了优化,会使用雪碧图,小图内联,多域名cdn。
有了 多路复用,一个tcp可以传输所有数据,解决了统一域名请求数量限制的问题。

二进制传输

把文本变成二进制编码

header 压缩

相同的内容就压缩了,减少了后续header的大小

服务端push

收到某个请求后,主动推送其他资源。兼容性的话,可以 prefetch

HTTP/3

如果多路复用时候丢包了,那性能就差了。

后来google基于udp搞了一个quic协议,使用 http/3
http3最大的改造就是 使用了 quic
细节有点儿没掌握。

nvm 学习笔记

nvm, Node Version Manager。控制切换语言版本的工具。通过nvm我们可以很方便地切换全局node版本,还有个好处是避免了sudo的使用。

安装

路径在这里:路径在此

到官方 github 上找最新的安装脚本。类似于

1
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash

这行命令是拉取nvm仓库到本地的 ~/.nvm中。把下面的命令放到.bashrc或者.zshrc中。

1
2
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

再执行

1
2
source ~/.bashrc #取决于用哪个
nvm --version

注意:
mac 上可能会安装失败。可能是应为你没有 .bash_profile 文件。首先确定用的终端是 bash 还是 zsh,然后把刚才的两行代码添加到对应的 .zshrc 里。如果安装失败,查看官网。

如果是 Windows,官方明确声明,不会官方支持,但提到了一个 nvm-windows

用法

安装完之后,可以安装指定版本的 node,比如

1
2
3
nvm install node
nvm install --lts
nvm install 11

然后这样使用

1
2
nvm use node
nvm use 11

常用功能一览:

1
2
3
4
5
6
7
nvm install --lts # 下载最新的 lts 版本的 node
nvm ls # 查看已经安装的版本
nvm install 8.9.4 # 安装指定版本
nvm install 8 # 安装 8.x 的最新版本

nvm use node 8 # 使用 8.x 最新版本
nvm use node 10

如果之前已经安装了 node 怎么办?
不用慌。nvm ls 会有一个 system,那个是原来的。

如果嫌弃node下载速度慢,可以参考 cnpm 设置阿里镜像。

1
2
export NVM_NODEJS_ORG_MIRROR=http://npm.taobao.org/mirrors/node
nvm install --lts

这样下载就走淘宝源了,速度很快。

帮助链接:https://npm.taobao.org/mirrors

组长,为啥我的node项目关闭命令行窗口就不能访问了

主标题:组长,为啥我的node项目关闭命令行窗口就不能访问了

副标题:node高可用,pm2的时候和背后原理

概念

pm2是守护进程的管理器,可以稳定运行 node python sh 等程序。相比直接运行node app.js,pm2可以后台运行,有问题记录和自动重启功能,更适合生产环境使用。

使用

安装

1
npm i -g pm2

快速使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 开始
pm2 start app.js
pm2 start a.sh
pm2 start a.py
pm2 start npm --name xxx -- dev # 跑 npm dev

#
关闭
pm2 restart app
pm2 reload app
pm2 stop app
pm2 delete apppm2 startup

#
维护
pm2 [list|ls|status] 查看状态
pm2 logs 日志
pm2 monist 实时仪表
pm2 startup 开机自启
# 自动重启
pm2 start a.js --watch --ignore-watch="node_modules"

集群模式,自动负载均衡 pm2 start app.js -i max

也可以使用 ecosystem 配置多个应用程序

1
2
3
4
5
6
# 配置文件
apps:
- script: app.js
instances:2
watch:true
env:

完事通过配置文件启动`pm2 start a.yml``

参数

可以在cli中加入一些参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 指定一个app名字
--name <app_name>
# 监听文件变化并自动重启
--watch
# 设置内存最大限度,超出重启
--max-memory-restart <200MB>
# 设定日志文件
--log <log_path>
# 给脚本传递额外参数
-- arg1 arg2
# 自动重启间隔
--restart-delay <delay in ms>
# 日志前缀带时间
--time
# 不自动启动app
--no-autorestart
# 其他

如何接入pm2

执行:

1
pm2 ecosystem

会获得一个 ecosystem.config.js配置文件,进行自定义修改。

然后执行:

1
pm2 deploy config.js production setup

这会执行 deploy 命令,到目标主机上执行下载。我刚才试没有执行安装,感觉没啥用呢。

因为上一步没有自动执行命令,我们还需要到目标主机上执行 pm2 deploy pm2.config.js production

这样就好了。

pm2原理

pm2 的原理:如何部署一个高可用的node服务。

node存在问题: 可靠性低,单线程,单进程,支持单核cpu,不能充分利用cpu,如果进程崩掉,整个web挂起。

如何解决?
方案一 fork,启动多个实例,使用多个端口,使用负载均衡
方案二 cluster,如何进程death事件触发,就立马起一个

fork

利用nginx强大的反向代理,启动多个node进程,绑定不同的端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
http{
upstream cluster{
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}
server{
listen 80;
server_name a.com
location / {
proxy_pass httpL//cluster;
}
}
}

缺点,和nginx耦合度高

cluster

部署到多核服务器时候,为了充分利用多核cpu,一般启动多个node进程,这就用到 cluster模块了。cluster 对 child_process 提供了一层封装。

参考 https://segmentfault.com/a/1190000014701988

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const cluster = require('cluster')
const os = require('os')
const process = require('process')

const cpuNum = os.cpus().length
const workers={}
if(cluster.isMaster){
// 主进程。
// 如果death就再起一个
cluster.on('death',(worker)=>{
worker = cluster.fork()
worker[work.pid] = worker
}
// 和cpu一致
for(let i=0;i<cpunum;i++){
const worker=cluster.fork()
workers[worker.pid] = worker
}
}else{//守护分支

}
// 当主进程终止时候发出信号
process.on('SIGTERM',()=>{
for(var pid in workers){
process.kill(pid)
}
process.exit(0)
})

pm2 就提供了这两种方式。

pm2 windows

如何在windows中使用,坑

在windows中需要注意两点,一个开机启动,一个作为服务后台启动

前者简单

1
2
3
npm install pm2-windows-startup -g
pm2-startup install
pm2 save

后者参考这个https://javascript.net.cn/article?id=428

1
2
3
4
5
6
7
8
npm i -g pm2-windows-service
# 手动添加环境变量
#右键 [我的电脑] - [属性] - [高级系统设置] - [环境变量] - 新建 [系统变量]
#PM2_HOME=D:\.pm2
# 这个用来指定pm2的配置信息存放位置
pm2-service-install # 选择否,比较省事
#此时, PM2服务已安装成功并已启动, 可以通过 [win + r] - [services.msc] 来查看
#下面用 pm2 来启动我们自己的服务程序 app.js, 然后告诉 pm2 开机后自动运行我 app.js

mysql 杂糅笔记

快速入门

docker启动

1
2
3
4
# 第一次启动
docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=12345678 -v ~/data-mysql:/data --name simple-mysql mysql:5.7
# 后续启动
docker start simple-mysql

概念

ACID:原子性 一致性 隔离 持久

PostgreSQL 最符合标准,但 mysql 一直火,原因是性能出众,在标准和性能之间选择了性能

使用

1. 安装

  • 软件包安装。略
  • 命令行安装。略
  • docker。见快速入门

    2. 运行

    开启关闭服务

Windows
windows 可以通过系统服务查看。
在命令行中

1
2
net start mysql
net stop mysql

Linux

1
netstat --nlp

关闭启动

1
2
3
service mysql start
service mysql restart
service mysql stop

不如docker好使。

3. 配置

4. 可视化工具

  • workbench 官方免费工具
  • sequel pro Mac免费
  • navicat 大而全,但付费

以下进入正文。

语法

SQL 结构化查询语言。

  1. DDL data Definitionlanguage 数据定义语言。定义库 表 列 字段 索引等。create drop alter 这个 DBA 常用,开发人员很少用,定义内部结构。
  2. DML Manipulation 数据操纵语句。增删改查 insert update delete select
  3. DCL Control 数据控制语句。控制权限和安全级别

1. 数据定义

DDL data Definitionlanguage 数据定义语言。定义库 表 列 字段 索引等。

create drop alter 这个 DBA 常用,开发人员很少用,定义内部结构。

创建数据库

  • 数据库编码必须是 utf-8 or utf8mb4 二选一。后者支持emoji表情

  • 数据库引擎,必须是 InnoDB 支持事务

  • create语句创建

  • use db 选择默认数据库

  • desc 看表结构

1
create database db1 default charcter set utf8mb4;

查询

1
2
3
4
5
6
7
8
9
10
# one
SELECT ID as num, post_title from wp_posts;
# two
select *
from table
where 条件
group by 分组字段
having 分段之后,少了字段再进行过滤
order by desc
limit 5
  • 查询不重复的值
    1
    SELECT DISTINCT id from a;
  • 限制结果 limit offset
  • order by
  • desc
  • where 过滤
    • price between 5 and 10
    • price is null 检查空值
  • and or 不行就圆括号分组
  • in 推荐,更清晰,效率高,可嵌套select
  • not

通用匹配

  • like模糊查询。like能用,不滥用
    • % 任意字符出现任意次数
    • 不会匹配null
    • _ 匹配单个字符
    • 1
      2
      3
      like '六%'
      like '%六'
      like '%六%‘

计算字段
函数
函数在跨数据库时候,兼容不同,是否使用特定平台的函数需要做决定

  • rtrim() 去除右边的空格
  • upper(a) 大写
  • 时间处理函数。可移植很差
  • 数值函数比较统一,绝对值 sin 等
  • 聚集函数,汇总而不用检索
    • avg count max min sum

数据分组

  • group by
  • having 相比where,这个能分组

order by 和 group by 的区别
order by

  • 对产生的输出排序
  • 任意列

group by

  • 对行分组,但输出不一定分组顺序
  • 如果用到 having之类的必须带着它

子查询

跨表。

第一条select返回的结果,第二条需要用到。

1
2
3
4
select a from orders where b in (select c from d where e=1);
select a from orders where b in (1,2);
# join
select a from b,d,

不断嵌套sql语句,可能会影响性能,有更好的方式。后面的join

联结

联结。分表的好处,减少不必要的修改,可伸缩性好, scale well
信息不重复,不会浪费时间和空间。关系数据有效存储和处理,因此关系型数据库的可伸缩性远比非关系数据库要好。

1
2
select a, b, c from A,B where
A.a = B.c;

如果 列名字有歧义,应当使用完全限定名。
这个叫 equijoin 等值 联结,也叫 inner join 内连接。基于两个表之间的相等测试。

1
select a,b,c from A inner join B on A.id=B.id;

和刚才from俩表效果一样,但这个是一个表。用 inner join 指定部分from

用哪种都正确

1
2
3
4
5
6
7
8
9
10
11
12
13
select 商品名, 品牌名, 价格, 质量 from OrderItem, Products, Vendors  where Products.vend_id = Products.prod_id and A.bID = B.bID and e=1;

select ID,post_title, post_content from wp_posts where post_status='publish' and ID in
(select object_id from wp_term_relationships where term_taxonomy_id=
(select term_id from wp_terms where name='企业简介'));

select ID,post_title from wp_posts, wp_term_relationships,wp_terms
where (
wp_posts.post_status='publish' and
ID = wp_term_relationships.object_id and
wp_term_relationships.term_taxonomy_id = wp_terms.term_id and
wp_terms.name='企业简介'
)

显示订单e=1中的物品。物品不再

高级联结

as 可以起别名
除了 inner join ,还有三种

  • self-join
  • natural join
  • outer join

给小张同一个公司的人推送信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 子查询
select id,name,contact from A where name=(select name from A where name='小张');
-- 联结,自己查自己,self-join 性能考虑一般选这个
select c1.id,c1.name,c1.contact frome A as c1, A as c2 where c1.name=c2.name and c2.concat='小张'
-- 自然联结 ??
select C.*, O.order_num, O.order_date, OI.prod_id, OI.quantiy, OI.item_price
from Customers as C, Orders as O, OrderItems as OI
where x
-- 外联结
-- 连接的表中没有关联行,这叫外联结
select Customers.id, Orders.num from Customers left outer join
Orders on Customers.id = Orders.id -- 左,左边表C选中所有行
select Customer.cust_id,Orders.order_num from Customers right outer join Orders on Orders.cust_id = Customers.cust_id; -- 右
-- 全外联结 full outer join
-- mysql 不支持

outer join 指定连接类型。提供left right 指定所有行的表

组合查询

union 组合数条sql查询

数据插入

存储过程

事务

要么完全执行,要么不执行

  • 事务transaction 一组sql语句
  • 回退rollback 撤销执行的sql。撤销
  • 提交 commit 为存储的sql结果写入数据库。保存
  • 保留点savepoint 设置的临时占位符,可以对他回退

回退的是 insert update delete

1
2
3
4
5
6
start transaction -- 只有开始,没有结束,会一直存在直到终端
--delete from Orders;
--rollback;

delete a where x
commit transaction

简单的 rollback 和 commit 可以写入或撤销整个书屋

复杂的事务,可能需要部分提交或回退,这就是 savepoint了

外键

外键是一列,值必须在另一个表的主键中。

索引

切当的排序。空间换时间

数据类型

varchar 可变长度,比较灵活

内置函数

数学

  • ceil 向上取整
  • floor 向下取整
  • div 取整数
  • mod取余
  • round 四舍五入

截断

如果想清空计数器, truncate

建模要求

设计库

  • 数据库编码必须是 utf-8 or utf8mb4 二选一。后者支持emoji表情
  • 数据库引擎,必须是 InnoDB 支持事务

设计表

  • 所有字段都必须是 not null 设定default,方便未来数据迁移
  • 表里的blob text 大字段垂直拆分到其他表,只需要读的时候才select
  • 反范式设计。经常join的,再其他表里荣誉一份。比如 user_name 再 user-account 里荣誉,减少join

    设计列

  • 自增ai,最大值在21亿左右,int注意范围
  • 选择状态少的,建议 tinytint smallint 节省空间
  • ip可以转换后方int 比如ip2long 这样省空间
  • 金钱有分,int,使用100放大缩小。
  • varchar 不建议超过 2700
  • 时间类型尽量选 timestap 4字节。也可以用int存时间,用 unix_timestamp() from_unixtime() 来转换

连接池

直接访问数据库,改为从pool里获取连接,连接已经预先创建好,直接分分配使用,减少创建新连接消耗的资源

假设我们不考虑磁盘 IO 和网络 IO,就很好定论了,在一个 8 核的服务器上,数据库连接数/线程数设置为 8 能够提供最优的性能,如果再增加连接数,反而会因为上下文切换导致性能下降。
考虑磁盘,ssd无需寻址和没有旋回耗时的确意味着更少的阻塞,所以更少的线程(更接近于CPU核心数)会发挥出更高的性能。只有当阻塞密集时,更多的线程数才能发挥出更好的性能。
连接数 = ((核心数 * 2) + 有效磁盘数)
如果说你的服务器 CPU 是 4核 i7 的,连接池大小应该为 ((4*2)+1)=9。
你的系统同时混合了长事务和短事务,这时,根据上面的公式来计算就很难办了。正确的做法应该是创建两个连接池,一个服务于长事务,一个服务于”实时”查询,也就是短事务


https://www.yiibai.com/mysql/basic-mysql.html
https://www.w3cschool.cn/sql/
https://app.bytescout.com/sql-trainer/index.html

浅谈用户养成运营模型:读《进化式运营》

浅谈用户养成运营模型:读《进化式运营》

一个理想的用户养成模型什么样?

以下阶段从浅入深:

  1. 接触。初次接触产品
  2. 认知。初步认知产品价值
  3. 关注。关注产品
  4. 体验。初次体验产品
  5. 使用。决定使用产品
  6. 付费。为产品付费
  7. 习惯。习惯使用产品
  8. 分享。心甘情愿想其他人分享
1
2
3
4
5
6
7
8
graph LR
接触 --> 认知
认知 --> 关注
关注 --> 体验
体验 --> 使用
使用 --> 付费
付费 --> 习惯
习惯 --> 分享

一些产品用户使用的成本很高,比如理财,比如付费等,如何打消用户顾虑,加强信任?

  • 广告品牌曝光。各种渠道
  • 明星代言,kol用户发文。年轻类群体明星,垂直类领域使用kol
  • 稳定的价格。买贵了怎么办?促销会转化一批围观用户,但频繁的促销会延长付费用户转化时间,保持价格稳定能够产生信任
  • 保险。和保险公司合作
  • 用户主动推荐。
  • 限时低价。比如试用7天,一分钱体验等,不同于限时免费。

如果产品效果不够好,该怎么分析?

每个环节步步为营

用户接触后离去

如果曝光和用户数不成比例,从阅读 新增 新增下载等方式获知。通常有三个层次的原因:

  • 渠道不匹配
  • 未吸引用户注意力
  • 接触内容的文案和潜在用户不匹配

渠道。渠道除了价格,还要考虑人群,比如社区氛围活跃。
注意力。比如楼宇公告
文案。比如老人用品需要注意老人的喜好

认知缺不关注

下载渠道安装。音频节目文字是否混淆。

关注后离开

需要持续吸引用户关注

关注不体验,使用不付费

参考 高决策成本产品上,打通用户信任

体验后离开

根据用户行为轨迹判断,看停在哪里。这就需要事先数据埋点。

付费后离开

  • 后续售后体验
  • 是否描述夸张,渲染过度

使用后难以形成习惯

参考低决策成本产品。

常规运营策略:

  • 价格敏感用户,小恩小惠
  • 好胜心强,攀比刺激
  • 活跃用户提供特权
  • 用户习惯行为进行倍增激励

非常规运营思路:

  • 精细化激励。高频活跃用户较小的奖励,低频用户较高的奖励,比如美团红包。
  • 限制用户使用时间。游戏类使用体力限制,初衷是为了提高用户付费率
  • 引入UGC属性。社区建设
  • 启发式借鉴。看看别人怎么做的再优化

习惯用户流失

  • 竞品出现
  • 替代品出现
  • 用户生命周期自然衰亡

最大化分享

用户分享的心理驱动力:

  • 利己驱动,满足需求。比如分享送xx
  • 利他驱动,互助,尤其是安全,食品安全等
  • 谈资驱动,心理归属
  • 塑形驱动,尊重,与众不同
  • 表达驱动,借权威人之口表达观点
  • 攀比驱动,满足尊重,比如超越了90%的用户
  • 情绪驱动,心理归属,安全。比如敬畏 兴奋 警惕 愤怒 愉悦等

用户养成进阶:幸福感

从习惯更进一步,幸福感,方式举例:

  • 让用户感到有控制力,
    • 控制自己的权限。比如微信公众号的白名单黑名单
    • 让用户能够影响产品部分功能的走向。
    • 让用户决定商品品种甚至价格。比如今日头条
  • 感到可量化的进步
    • 比如早期的qq等级
    • keep历史记录
  • 赋予更高意义
    • 乐趣 激情 使命

以上是用户养成攻略

请我喝杯咖啡吧~