jyhjn1

jyhjn1 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

jyhjn1 赞了文章 · 2019-03-25

rem, vw, 还是...? 各凭本事的移动端适配方案

前言

2018年最后的法定假期都已经结束了,我相信大部分正在进行或曾经进行过移动端页面开发的同学都或多或少的了解过使用rem进行移动端页面适配的方案以及使用vw的方案,(没了解过的同学可以参见大漠老师的这两篇文章 使用Flexible实现手淘H5页面的终端适配再聊移动端页面的适配)也面临过在不同适配方案间进行抉择的思考,我个人最近对于移动端适配方案也进行了一轮重新的研究,期间,对各种适配方案也有一些自己的见解,正好记录下来与大家一起分享。

vw与rem方案中的一些思考

所以,移动端适配 = vw or rem ?

当然不是。并不是所有场景下的移动端页面都适合采用vw或rem的方案,这类方案的本质是布局等比例的缩放,让页面在不同尺寸的屏幕下有类似于矢量图片缩放的效果,即保证页面各元素之间位置尺寸的比例关系,并让元素可以清晰地展现。
这样的方案更适合于视觉组件种类较多,视觉设计对元素位置的相对关系依赖较强的移动端页面。其实大部分页面都可以使用rem或vw的方案进行适配,但对于文本内容较多,或者说希望引导用户沉浸浏览的页面,我个人并不推荐使用等比缩放的方案,至少并不推荐完全使用等比缩放的方案,对于文本内容还是应该直接使用px这种绝对长度单位,毕竟在大屏手机上用户希望看到的是更多的内容而不是更大的内容。实际上很多这类的网站也确实是直接使用px结合flex等布局方式进行移动端适配的,这个在后面讨论具体技术方案的时候我会举几个例子。

rem方案到底在做什么?

在各种rem适配方案的实现中,有两个核心的点

  • 设置<meta name="viewport" content="xxx">(可以根据dpr缩放viewport,也可以直接使用1倍的视口大小)
  • 根据当前布局的宽度(通常是viewport宽度, 也可以是被限制的最大/最小布局宽度)来设置html元素的font-size

之前我已经提到过,vw和rem的方案的本质都是“等比例缩放”,因为vw和rem都是相对长度单位(relative length unit),可以很好的满足这个需求。区别是vw是viewport width的1%,而rem是html元素的font-size。当我们让html元素的font-size与viewport width产生了关联后,rem就可以模拟出使用vw进行布局的效果了。所以在rem方案中,我们只是在把rem当做是vw的影子。写作rem,读作vw...(剧情似乎狗血起来了... rem: 当然是选择原谅你们啊)
我rem有话说

那直接用vw不就完事儿了吗?

且慢

且慢,当初之所以使用rem的方案流行开来正是因为在那时viewport units的浏览器支持程度不甚理想(IOS 8+, Android 4.4+ 参见viewport units的caniuse)。而相比较之下rem就好多了(IOS 4.1+, Android 2.1+ 参见caniuse),所以对于vw,在当时的大环境下前端想说爱你不容易。

我想这时候有人要说了:“醒醒兄弟 已经8102年了!”
是的,8102年都快要过去了,对于兼容性要求不是特别高的情况下vw也算是可以见天日了,并且也有一些针对vw的补丁方案,但还有一个问题我们要稍微讨论一下...

vw可以完全替代rem吗?

回答依旧是否定的。单纯使用vw进行布局不能限制布局的宽度,对于有这个需求的场景至少还是需要将vw和rem混用来处理边界情况。下文也会更详细地提到这种方案,这里先按下不表。

现有生产环境中移动端适配方案的一点总结

当我们在苦苦地寻找适合自己的道路时,不妨先抬头看看别人是怎么做的。那么现实世界里各家互联网公司的移动端页面都采用了什么样的适配方案呢?有没有一些比较有特色的绝活儿呢?以下我按照页面实际使用的css长度单位作为划分标准,为大家举一些栗子。

px方案

就像开篇提到的,并不是说移动端就一定要使用相对长度单位,传统的响应式布局依然是很好的选择,尤其在新闻,社区等可阅读内容较多的场景直接使用px单位可以营造更好地体验。px方案可以让大屏幕手机展示出更多的内容,更符合人们的阅读习惯。采用这种方案的网站有:

  • 腾讯

    移动端首页主要是新闻内容,需要更好的阅读体验,适合直接使用px进行布局。

  • 知乎

    知乎也是比较典型的追求阅读体验的场景,不出意外的也是直接使用px。

  • 点评

    视觉元素较丰富,依旧采用了px方案,页面基本是flex布局,适配效果很好

  • 头条

    头条的这个方案有点特色,依然会设置html元素的font-size也会加上data-dpr属性并且会对viewport进行scale, 但是最终css的输出还是px,并没有使用rem,并且会对不同dpr下的样式单独定义,如下图所示:

头条的适配方案
这样可以解决1px border的问题,文字大小也不会随屏幕尺寸变动(毕竟文本内容较多),虽然我暂时没找到使用到rem的地方,但确实可以在需要的时候对特殊元素做rem方案的布局,不过这种方案应该会造成css文件大小倍增,而且输出这样的css肯定也少不了构建流程插件的支持,算是一种特定的解决方案吧。

看到这里你以为最终输出px就不能做类似于rem/vw的弹性布局了吗,下面就给大家看一手绝活儿...

  • 淘宝

    什么?给我们看了半天文章结果用的是px?
    其实聪明的你一定很快就会发现在效果上淘宝移动端的适配方案和rem/vw的方案其实是差不多的,元素的样式都是通过js生成的,虽然单位确实是px,但是方案依旧是以375px宽度的尺寸为基准进行缩放的。原理上应该是一种css in js的方案,只不过把rem方案中设置html元素font-size的过程内化到使用js计算元素style的过程中去了。这样的方案涉及到整体的开发框架上的统一与支持,并不算是一个特别通用的方案。好处可能是直接使用px单位结果更为精确。可以说是一手绝活儿了。当然淘宝旗下还是有非常多的产品线的,也未必是同样的适配方案(比如大漠老师文中的例子),这里只针对这个移动端首页来说。

rem方案

rem方案可以说是比较成熟了,出镜率也较高,也就不再赘述了,总的来说rem方案主要分为两种,一种是缩放viewport的方案,如当年的lib-flexible,可以对1px border等细节问题较方便的处理,但会影响到media query。另一种就是不缩放viewport,对1px boder等问题单独引入处理方案。然后对于基准尺寸下的html元素fong-size也有很多不同的定义方式,这个说起来没什么标准可言,我就随便举几个例子说明吧:

不缩放viewport

(以下说明的rem与px的对应关系都是在屏幕宽度为375px的情况下)

缩放viewport

(以下说明的rem与px的对应关系都是在屏幕宽度为375px, viewport scale 0.5的情况下)

vw方案

来了,终于来了!前面说了这么多关于vw的问题,到底有没有现有的产品在大规模的使用vw的方案呢?兼容方案又是怎么做的呢?

  • 京东

    京东的移动端首页采用了vw+rem的布局方式,元素布局上依然使用rem单位,没有缩放viewport, html元素的font-size则使用vw + px fallback的形式,并且使用media query来限制布局宽度,如下图所示

    正常情况下:

京东适配方案 正常情况下

边界情况下

京东适配方案 边界情况下

  • 网易

    网易的方案和京东基本相同,没有缩放viewport,使用media query,只处理html元素的font-size,并限制布局宽度。

  • 饿了么

    饿了么也是采用的vw+rem的方案,不过对viewport进行了缩放,也没有限制布局宽度,html元素的font-size依然由px指定,但是具体元素的布局上使用vw + px fallbak的形式,如下图所示:

饿了么适配方案

可以看到,使用上述两种vw+rem的方案对现有的rem方案的改动都不会很大,都采用了vw + fallback的方式,兼容性问题得到了保证,只是饿了么的方案可能更需要构建过程中的插件支持(关于这个,后面我给你们解释解释什么叫惊喜)。这样来看,对于大漠老师提出的vw方案中使用viewport-units-buggyfill库进行兼容的做法,我个人就并不是很推荐了,因为该库使用了css content属性进行兼容处理,官方文档中就指出了对部分浏览器的img标签有影响 ,需要全局引入一条css规则。且对于需要正常使用content的情况(如:图标字体)也会引起不可避免的冲突,另外也不支持伪元素的兼容。所以从我个人的角度来说,如果你一定要问我使用怎样的vw适配方案,我会推荐给你上述两种vw + rem的方案。

这就是全部了吗?

当然不是,我只是列举了几个比较典型的移动端适配方案,其实在具体实现的细节上可以自行把握的点还是很多的,适合的终归才是最好的,那颗银弹或许不会出现,但我们的手中也从未缺少过利器。

彩(an)蛋(li)部分

相信大多数同学也是有想法在实际开发中把vw融入到现有的移动端适配方案中的。如我上述提到的两种vw+rem方案,只修改html元素font-size的方案对于已经在使用rem方案的同学来说改动的成本并不大,只需要在原本的media query 里(或js生成style时)在font-size: *px后面加上font-size: *vw就可以了,如需限制布局宽度则需多加一点判断。

而对于饿了么那种在使用到长度单位时同时输出rem+vw的方式,肯定是要通过一点额外的插件来做了。如果你和我一样刚好在使用Stylus作为css预处理器,那我专门写了一个Stylus的插件用来帮你处理这个问题。
这个插件可以让你在开发流程使用px书写css, 和现有的部分插件不同的是,它同时支持多种适配方案的输出,目前支持rem,纯vw方案以及刚才提到的vw+rem方案的输出。并且对不希望转换px的场景做了很方便的处理。也就是说,如果你现在使用rem方案,可以直接替换使用该插件,当你需要切换到vw或vw+rem方案时基本可以做到无缝切换。

具体的使用方式和示例请参见pandaGao/stylus-px-to-relative-unit

懂我的意思吧

查看原文

赞 41 收藏 25 评论 4

jyhjn1 提出了问题 · 2018-12-29

aaaaa111111

asdasdasd787878

问题出现的环境背景及自己尝试过哪些方法

相关代码

// 请把代码文本粘贴到下方(请勿用图片代替代码)

你期待的结果是什么?实际看到的错误信息又是什么?

关注 1 回答 1

jyhjn1 提出了问题 · 2018-12-29

aaaaa111111

asdasdasd787878

问题出现的环境背景及自己尝试过哪些方法

相关代码

// 请把代码文本粘贴到下方(请勿用图片代替代码)

你期待的结果是什么?实际看到的错误信息又是什么?

关注 1 回答 1

jyhjn1 收藏了文章 · 2018-12-15

基于zepto的移动端轻量级日期插件

前言

做过移动Web开发的同学都知道,移动端日期选择是很常见的需求。在PC端,我们有很丰富的选择,比较出名的就有MobiscrolljQuery UI Datepicker。个人看来,这些插件存在的两个显而易见的问题,第一是过重的依赖,对于jQuery的插件,已经强制依赖了80多k的庞然大物,把很多移动端项目拒之门外;第二是太过强大的功能,很多插件花百分之八十的时间去完善插件百分之二十炫酷的额外功能,导致代码量变大,配置繁杂。所以一款少依赖轻量级使用简单的移动端日期选择插件是非常必要的。本文简单介绍近来写的一款基于zepto的移动端轻量级日期插件--date_picker

插件设计原则

只保留最必要的功能

日期选择插件无非就是可以进行年、月、日选择,并且提供必要的年月切换动画特效,至于什么最小时间、最大时间、主题定制,概不在本插件功能范围。

保留必要的依赖

本插件虽说是基于zepto,实际上还隐性依赖了Github上面一个比较牛的库fastclick。我们知道,移动端点击事件处理有两个常见的问题:(1)移动端click事件有300ms,通常采用touch事件来代替click事件来提高灵敏性;(2)touch事件会存在穿透的问题,一般插件都不用touch事件;基于这两个问题,fastclick做了兼容,只需要简单调用它提供的api就能够愉快得和原本一样调用click事件,所以这个依赖不能省。至于依赖zepto,实际上是可有可无的,一来博主平时工作都是写原生js的,不用插件也没多大的感觉,二来插件受众面就会小一些。不过鉴于zepto在移动端已然和zepto在PC端一样如鱼得水,就毫不客气采用了。

既能够支持模块化又能本地引用文件使用的

久远一点的插件基本都是让你下载一个文件,然后用script去引用,这样本无可厚非,只不过放着最大的包管理器npm不用,岂不是对不起页面仔这个称号。所以本插件支持文件引用也支持CMD方式的模块引用。

功能介绍

直接上图:
图片描述

技术细节

transitionEnd事件

插件的主面板是当前月份的天数详情,如果点击上一个月或者下一个月插件需要计算出上一个月或者下一个月的天数信息,然后插入到DOM节点中。在插入到DOM节点之后,就需要采用动画效果来显示最新的一月并且褪去老的一个月,采用的方式是CSS2d转化加过渡。我们需要处理的就是在旧的一个月褪去看不见的时候及时从DOM树中删除,不然如果用户一直点下一个月或者上一个月的话,内存会炸的。为了实现这个移除功能,一个办法是采用setTimeout事件在特定的时间去删除节点,经过尝试,发现由于Javascript定时器不准确的特性和前后一个月切换带来的逻辑复杂度增加,这种方案很不可行。
于是,本插件采用了第二个方案:transitionEnd事件。直接引用一下MDN的介绍:

The transitionend event is fired when a CSS transition has completed. In the case where a transition is removed before completion, such as if the transition-property is removed, then the event will not fire. The event will also not fire if the animated element becomes display: none before the transition fully completes.

也就是只要你不去随便乱动元素的CSS属性,在动画完成的时候,你就可以执行相应的操作(删除节点)。
再来看看兼容性:
图片描述
对于移动Web开发足矣!

最后就是在绑定事件的兼容性问题,不同厂商对于这个事件的定义并不一致,比如IOS里面监听的是transitionend事件,但是在安卓里面监听transitionend事件完全没反应,经过一番Google,发现安卓需要监听webkitTransitionEnd事件。为了解决绑定事件时候的兼容性问题,就需要检测浏览器到底支持哪种事件。下面贴上Stackoverflow上面一个问答的代码片段:

    function whichTransitionEvent() {
        var t,
            el = document.createElement('fakeelement');
            transitions = {
                'OTransition'       :'oTransitionEnd',
                'MSTransition'      :'msTransitionEnd',
                'MozTransition'     :'transitionend',
                'WebkitTransition'  :'webkitTransitionEnd',
                'transition'         :'transitionEnd'
            };

        for(t in transitions){
            if( el.style[t] !== undefined ){
                return transitions[t];
            }
        }

        return false;
    }

安装使用

安装

支持下面两种方式

  • git clone之后直接拷贝引用bin文件夹下面的datepicker.min.cssdatepicker.min.js

  • 从npm下载安装:npm install --save date_picker

使用

  • 引用样式datepicker.min.css

  • 引用datepicker.min.js或者引用模块var DatePicker = require('date_picker');

  • 实例化组件,绑定插件日期选择完成之后的回调函数

var _date = document.getElementById('date');

    var datePicker = new DatePicker({
        confirmCbk: function(data) {
            _date.value = data.year + '-' + data.month + '-' + data.day;
        }
    });

    _date.onfocus = function(e) {
        _date.blur();
        datePicker.open();
    };

插件外置两个API: openclose,其中特别注意上面demo中_date在获取焦点之后里面强制去除了焦点,是为了避免安卓下面为input标签设置readonly属性并不能禁止原生键盘弹出的问题。

在线Demo

点击链接在PC上查看效果或者手机扫码可直接在手机上查看效果:
图片描述

小结

本插件代码托管在Github上面,仓库地址为:https://github.com/yuanzm/simple-date-picker
npm上面的仓库地址为:https://www.npmjs.com/package/date_picker
最后打个小广告,欢迎follow我的github: https://github.com/yuanzm

参考资料

http://stackoverflow.com/questions/13823188/android-4-1-change-transition-and-webkittransition-defiend-how-to-properly-de

查看原文

jyhjn1 收藏了文章 · 2018-12-15

3d transform的坐标空间及位置

css里的3d理念

使用css3的3d transform,就可以在平面的网页里添加炫酷的三维视觉效果,这很令人愉悦。

需要注意的是,3d transform只是css的一部分,它并不是一个三维引擎(3d engine)。三维引擎一般是这样的(游戏引擎Unity3D):

Unity3D

包括JavaScript 3D库three.js在内,简单来说,它们这些可以称为三维引擎的,都会包括:

  • 独立的三维坐标系统。

  • 几何图形和材质贴图。

  • 光照和摄像机。

3d transform和它们相比起来,是缺少这些内容的。毕竟,css的关注点是网页样式,而不是创建虚拟空间。

尽管3d transform的三维空间能力有所不足,但它仍然可以创建出很棒的三维效果。这就需要我们开发者来用心了。

在下面的内容开始之前,如果你还对perspective等3d transform相关的css属性完全不了解,可以阅读我以前写的css三维变换的文章

3d transform的坐标系统

我们很熟悉的网页是平面的,一个DOM元素,比如一个<div>,它会有一个初始坐标系initial coordinate system):

初始坐标系

每一个DOM元素都有一个这样的初始坐标系。其中,原点位于元素的左上角,z轴指向观察者(也就是屏幕外的我们)。初始坐标系的z轴并不算是三维空间,而是像z-index那样作为参照,决定网页元素的绘制顺序,绘制顺序靠后的元素将覆盖绘制顺序靠前的。

在使用transform的时候,情况则有所不同。transform所参照的并不是初始坐标系,而是一个新的坐标系:

变换坐标系

transform所用的这个坐标系,相比初始坐标系,x、y、z轴的指向都不变,只是原点位置移动到了元素的正中心。如果想要改变这个坐标系的原点位置,使用transform-origintransform-origin的默认值是50% 50%,因此,默认情况下,transform坐标系的原点位于元素中心。

transform的顺序

我们都可能像transform: rotateY(45deg) translateX(100px);这样使用多个变换函数。这种时候,需要意识到变换函数的顺序。这是因为,每一个变换函数不仅改变了元素,同时也会改变和元素关联的transform坐标系,当变换函数依次执行时,后一个变换函数总是基于前一个变换后的新transform坐标系。

例如,下面一个包含两个变换函数的transform的效果(gif):

transform顺序1

如果交换这两个变换函数的顺序,是这样的效果:

transform顺序2

可以看到,由于坐标系会随着每一次变换发生改变,因此不同顺序的情况下,元素最终的位置也不同。

对此还有一种解释,即变换函数是通过数学上的矩阵乘法运算完成的,而矩阵的乘法是不满足交换律的。任意坐标空间内的变换函数或者变换函数的组合,都可以转换为一个矩阵(还有一个矩阵小工具可以帮你做这个转换)。

创建三维物体

前面已经提到,3d transform并没有像三维引擎那样为你创建三维场景提供全面的资源。因此,就以创建一个三维物体来说,我们只能利用网页目前已有的内容,自己想办法。

在网页里,你并不能直接定义一系列坐标为(x, y, z)的空间中的点,然后基于这些点来生成三维图形。网页里有的,是平面图形。不管是<div>还是其他html元素,它们都是平的,没有厚度,像纸片一样。但纸片就可以搭东西,所以,一个DOM元素用作三维物体的一个“面”,把这些“面”有序地组织起来,得到的就是三维物体了!

事实上,在三维引擎里,三维物体也不是实体,它们都是由一系列平面(多边形)所围成的(并可以在平面上添加纹理和贴图)。

正方体

现在来做一个正方体,现在先不用考虑perspective。正方体有六个面,然后需要用一个元素来装这六个面,所以html是:

<div class="cube">
    <div class="surface surface-1">1</div>
    <div class="surface surface-2">2</div>
    <div class="surface surface-3">3</div>
    <div class="surface surface-4">4</div>
    <div class="surface surface-5">5</div>
    <div class="surface surface-6">6</div>
</div>

对应的css是(边长120px,省略浏览器私有前缀,后文同):

.cube{
    position: absolute;
    transform-style: preserve-3d;
}
.cube .surface{
    position: absolute;
    width: 120px;
    height: 120px;
    border: 1px solid #ccc;
    background: rgba(255,255,255,0.8);
    box-shadow: inset 0 0 20px rgba(0,0,0,0.2);
    line-height: 120px;
    text-align: center;
    color: #333;
    font-size: 100px;
}
.cube .surface-1 {
    transform: translateZ(60px);
}
.cube .surface-2 {
    transform: rotateY(90deg) translateZ(60px);
} 
.cube .surface-3 {
    transform: rotateX(90deg) translateZ(60px);
}
.cube .surface-4 {
    transform: rotateY(180deg) translateZ(60px);
}
.cube .surface-5 {
    transform: rotateY(-90deg) translateZ(60px);
}
.cube .surface-6 {
    transform: rotateX(-90deg) translateZ(60px);
}

其中,transform-style: preserve-3d;保证所有子元素都处于同一个三维空间(这里是三维渲染上下文3D rendering context)内,也就是告诉浏览器你是想用这些元素做一个三维场景,而不仅仅只是要单个元素的简单三维效果。

position: absolute;是一个习惯做法,因为三维物体并不符合一般平面网页内容的排版,所以我们会比较多地希望它不要占据布局空间。

6个面位置都不一样,但却都有translateZ(60px);,你已经知道这是因为巧妙搭配了在它之前的变换函数。

一旦构成正方体的6个div.surface的位置确定后,就可以操作它们的父元素div.cube来整体移动、旋转这个正方体。

将三维物体添加到可视区域

只是有了这样的一个三维物体,我们就可以说有了一个三维场景了。但是,三维场景和平面图不同,比如这个正方体,我从不同的位置,不同的角度去观察它,我眼里看到的都是不一样的:

从不同角度观察三维物体

那么,这样的三维场景要如何呈现在平面的网页里呢?

这就是三维引擎里的摄像机的概念来源了。就像电影里表现同一个场景会用不同的镜头那样,我们需要定义场景里的摄像机来生成最终呈现在屏幕里的二维画面。比如three.js里的摄像机是这样的感觉:

three.js的摄像机基本参数

这个图里标明的是摄像机定义时使用的参数,其中包括视野范围,图像宽高比等。可以感受到还是有很多内容的。

相比之下,网页里的三维场景摄像机就弱多了,你需要用的是perspectiveperspective-origin

perspective定义摄像机(也就是作为观众的我们)到屏幕的距离,perspective-origin定义摄像机观察到的画面中的灭点(vanishing point)的位置。虽然它们并不能方便地让你直接定义摄像机的位置和观察角度等,但只要适当地应用它们,是可以一定程度上控制摄像机的画面效果的。

控制摄像机画面

网页里的摄像机一般是这样用的:

<div class="camera">
    <div class="cube1"></div>
    <div class="cube2"></div>
    <!-- more 3d objects... -->
</div>
.camera{
    position: relative;
    perspective: 1200px;
    perspective-origin: 50% 50%;
    transform-style: preserve-3d;
}

在网页里,无论你搭建了怎样的三维场景,只要你希望它显示出来,就应该像这样把构成场景的三维物体都放在一个容器元素里,然后为容器元素添加摄像机属性(perspectiveperspective-origin)。

此外,还需要注意添加transform-style: preserve-3d;以保证多个三维物体都位于同一空间(这样才有三维引擎的味道,对吧?)。

下面这个场景里有三个正方体,然后摄影师正在做弹跳练习(限支持3d transform的浏览器):

http://runjs.cn/detail/daqoq5tf

这段动作的动画代码是这样:

.camera{
    animation: cameraMove 2s ease-out infinite alternate both;
}
@keyframes cameraMove{
    0%{
       perspective-origin: 50% 180px;
    }
    100%{
       perspective-origin: 50% -200px;
    }
}

可以看出,perspective-origin虽然是指三维透视的灭点的位置,但它的确和我们理解的摄像机的位置是紧密关联的。如果摄像机在空间里的位置是(x, y, z)的话,perspective-origin的两个值有一点像指定xy的感觉。这里只说“有一点像”,是因为灭点位置和摄像机的位置毕竟是不同的概念,这可能还需要多看一些三维空间来体会。

那么,在上面的例子中,摄影师不只是这样跳起来,而是想要向更深处前进,应该怎么做呢?

答案是,在网页里,你不能这样移动摄像机,你需要换一个思路,参照相对运动的关系,改为让整个三维场景向你移动。不过,说到这里,前面提到的摄像机的另一个属性,perspective,为什么它不行呢?

perspective代表摄像机距离屏幕的距离,看上去和z轴深度非常近似。但是,它并不等同于摄像机的z坐标位置(perspective还只能取正值),而是会影响摄像机本身的其他属性。下面用这个图说明perspective的值变化的效果(修改自w3c的配图):

perspective的值变化的效果

图中d1d2分别表示两个不同的perspective的值,其中d2小于d1。然后,你会惊奇地发现,一个原本位于屏幕之后(z坐标为负值)的物体,竟然是随着“走近”而变得更小了!显然,这不符合我们在三维空间里运动的基本感受。其原因是,网页的三维投影平面是固定的,perspective在改变摄像机的位置的同时,也同时改变了摄像机本身的其他属性(类似前面的three.js的摄像机那张图里的各种参数)。

所以,一般来说,perspective应维持一个固定的值。想要用3d transform做出在三维空间里自由移动的效果(就像各种3d游戏),应该通过相对运动的方法实现。

其他补充

transform对布局的影响

transform影响的是视觉渲染,而不是布局。因此,除以下情况外,transform不会影响到布局:

生成滚动条而影响布局的例子

这个因为overflow生成滚动条从而影响布局的反例,也发生于position: relative;再进行偏移的情况。

left、top等常规属性对3d transform的影响

相对于transform的translate3d()这类改变空间位置的变换函数,原来css里就有的定位属性lefttop似乎会让情况变得很复杂。

对此,有一个比较推荐的分析方式:就三维空间的位置而言,常规属性lefttop,甚至margin-left等,是先生效的,它们的效果其实只有一个,就是改变元素的初始位置,从而改变元素的transform-origin的那个原点位置,然后三维空间的transform是后生效的,它会再基于前面的transform-origin继续改变位置。

perspective-origin和transform-origin的区别

现在你已经了解到,perspective-origin是一个摄像机的属性,定义的是透视画面的灭点,而transform-origin是任意元素都有的,定义的是的元素的transform坐标系的原点。

结语

本文是我对目前网页里用3d transform创建三维空间的总结,其中混入了一些三维引擎的知识,并对比着来一一说明。感觉3d transform还是有自己比较明确的定位的,虽然和三维引擎相比很简陋,但它已经足够用来为网页添加吸引人的三维效果。

如果你也想过用3d transform做稍复杂的三维空间,希望本文能有所帮助。

(重新编辑自我的博客,原文地址:http://acgtofe.com/posts/2015/12/xyz-3d-in-css

查看原文

jyhjn1 收藏了文章 · 2018-12-15

ios9.3为前端带来哪些新特性

CSS font-feature-settings

IEBlog
由于提供了对 CSS3 @font-face 规则和 WOFF 字体封装格式的跨浏览器支持,现代的 Web 版式已远远超出了“Web 安全字体”所定义的范畴,像 New Yorker 这样的知名杂志就使用了 Web 字体来彰显标题版式的独特风格,而在美国总统奥巴马的竞选连任活动中,也使用了 Web 字体服务 Typekit 来托管其独具一格的字体。

妨碍 Web 设计人员利用字体的全部特性的一项限制是:无法访问许多字体中内置的大量可选 OpenType® 特性。这些特性定义了字形替代和定位选项,以支持基本的版式特性(如字距调整、上标和下标)、上下文字形(如连写、数字样式)以及访
问可选的东亚字形和花饰字体。

是不是很晕

举个例子

html

<p>
    <span>Th</span>e <span>fi</span>x was in for the <span>fi</span>ve<span>fl</span>irting <span>fl</span>eurons.
</p>
<button>Turn ligatures</button>

scss

p {
  font-size: 40px;
  margin-bottom: 10px;
  padding-left: 10px;
  font-style: italic;
}

body {
  background-color: #ddd;
  font-family: "chaparral-pro";
  margin: 0;
  padding: 0;
}

span {
  transition: color .3s ease;
}

.liga {
  font-feature-settings: "sups" 1; 
  
  span {
    color: lighten(red, 10);
  }
}

button {
  cursor: pointer;
  margin-left: 10px;
  
  &:after {
    content: " on";
  }
  
  &.on:after {
    content: " off";
  }
}

js

var btn = document.querySelector("button"),
    body = document.documentElement,
    activeClass = "liga";

btn.addEventListener("click", function(e){
  e.preventDefault();
  body.classList.toggle(activeClass);
  btn.classList.toggle('on');
});

按钮未改变时效果
图片描述

按钮按下添加liga类

图片描述

咦 为什么会是这样呢 font-feature-setting为我们提供了更多的可以兼容opentype的功能
图片描述

该特性兼容性
图片描述

CSS will-change

will-change属性,允许作者提前告知浏览器的默认样式,那他们可能会做出一个元素。它允许对浏览器默认样式的优化如何提前处理因素,在动画实际开始之前,为准备动画执行潜在昂贵的工作。

之前我们希望调用gpu进行优化时需要这么写

.accelerate {
  -webkit-transform: translate3d(0, 0, 0);
}

不过这样浏览器就会耗费很多资源 will-change可以有助于浏览器进行优化

该特性兼容性
图片描述

CSS touch-action

图片描述

该特性兼容性
图片描述

CSS all property

CSS all简写属性重设除了unicode-bidi 和direction之外的所有属性至它们的初始值或继承值。

举个例子

html

<blockquote id="quote">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</blockquote> Phasellus eget velit sagittis.

css

html { font-size: small; background-color: #F0F0F0; color:blue; }
blockquote { background-color: skyblue;  color: red; }

然后这样子
图片描述

css加上一句

blockquote { all: unset; }

图片描述

就可以屏蔽全局的影响

该特性兼容性
图片描述

CSS unset value

顾名思义 就是撤销继承过来的值

图片描述

该特性兼容性
图片描述

CSS revert value

不太懂啊 返回浏览器原始? 没啥用啊

该特性兼容性
图片描述

CSS variables

ios支持css变量了

这里我们有一篇seagmentfault文章

该特性兼容性
图片描述

CSS font-variant-alternates property

可以自己组装字体了 不过浏览器兼容性不太好

该特性兼容性
图片描述

picture element

这次ios9.3最大的更新就是提供了picture element了

这里我给大家提供几篇文章
whatwg介绍
浏览器内建支持的响应式图像
响应式图片101(六):picture元素

该特性兼容性
图片描述

查看原文

jyhjn1 收藏了问题 · 2018-12-15

移动端像左右滑动元素的效果怎样不出现滚动条?

想要实现的效果 : https://jsfiddle.net/oa4f8077/

pc端的这样的效果如果在手机上呈现肯定不能出现像demo的底部出现滚动条,那莫我该如何用css方法实现?

如果css方法行不通使用js?我引入的是zepto,zepto有左右滑动事件,但是给他绑定的方法如何顺滑的滑动,比如向右滑动时left 自增,向左left自减,这个速率我该如何控制?

jyhjn1 收藏了文章 · 2018-12-15

巧用SASS之如何遍历n个子元素并为其设置属性

最近在新项目中引入了 SASS 来编写样式代码,心想既然用到了这种高端货,就要用得巧用得妙,不能单纯地只是把常用属性定义成变量或定义重用的代码块等等。因此在编写样式时,都要时刻提醒自己是不是可以巧用SASS来解决一些问题。

这次遇到的需求是,<ul></ul> 里有7个重复的 <li></li> ,这7个 <li></li> 需要应用7中不同颜色的 background-color ,需求很简单,如下是简易的效果图。

clipboard.png

针对这个需求,有许多种实现方式,包括传统的CSS写法和我们今天要讲的使用SASS的编写方法,具体如下:

HTML结构为:

<div id="main-container">
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
    </ul>
</div>

1. 传统CSS实现

最简单的当然是为每一个 <li></li> 都加一个用于区分不同 background-color 的类, 每个类里应用上不同的背景颜色就OK啦,这我们没必要多说了。

2. 使用SASS多值变量: list

list 类型有点像js中的数组。list数据可通过空格,逗号或小括号分隔多个值,可用 nth($var,$index) 取值。关于list数据操作还有很多其他函数如 length($list)join($list1,$list2,[$separator])append($list,$value,[$separator]) 等,具体可参考sass Functions(List Functions)。

本需求实现代码如下:

// 将背景颜色值定义成变量
$red : #FF0000;
$orange : #FFA500;
$yellow : #FFFF00;
$green : #008000;
$bluegreen : #00FFFF;
$blue : #0000FF;
$purple : #800080;

// 定义一个list储存背景颜色
$bgcolorlist: $red $orange $yellow $green $bluegreen $blue $purple;

// 使用SASS for循环语句为每一个li设置background-color
@for $i from 1 to length($bgcolorlist)+1 {
    #main-container ul li:nth-child(#{$i}) {
        background-color: nth($bgcolorlist,$i);
    }
}

这里需要注意的几点是:

  • from后的数值,即循环开始的i值不能为0,这是语法规定的。

  • for循环从 i = 1 开始,但并不是在 i = length($bgcolorlist) 时结束,我们本来是需要循环7次,但如果我们写成 to length($bgcolorlist) 的话,只会循环6次,因此是 to length($bgcolorlist)+1

3. 使用SASS多值变量: map

当然,解决这个需求,我们也可以使用 SASS 中的 map 。map类型有点像js中的对象。map数据以key和value成对出现,其中value又可以是list。格式为: $map: (key1: value1, key2: value2, key3: value3); 。可通过 map-get($map,$key) 取值。关于map数据还有很多其他函数如 map-merge($map1,$map2)map-keys($map)map-values($map) 等,具体可参考sass Functions(Map Functions)。

本需求实现代码如下:

// 将背景颜色值定义成变量
$red : #FF0000;
$orange : #FFA500;
$yellow : #FFFF00;
$green : #008000;
$bluegreen : #00FFFF;
$blue : #0000FF;
$purple : #800080;

//将背景颜色以键值对的形式存在map中
$bgcolorlist : (
    1: $red,
    2: $orange,
    3: $yellow,
    4: $green,
    5: $bluegreen,
    6: $blue,
    7: $purple);

// 使用SASS each语法为每一个li设置background-color
@each $i, $color in $bgcolorlist {
    #main-container ul li:nth-child(#{$i}) {
        background-color: $color;
    }
}

是不是很简单呢~其实这种方法本质上和使用list的方式是一样的。

OK,就这么多吧,当然这个小需求的实现方式远不止这些,选一种自己喜欢的就好啦,都so easy~

查看原文

jyhjn1 收藏了文章 · 2018-12-15

单表60亿记录等大数据场景的MySQL优化和运维之道 | 高可用架构

此文是根据杨尚刚在【QCON高可用架构群】中,针对MySQL在单表海量记录等场景下,业界广泛关注的MySQL问题的经验分享整理而成,转发请注明出处。

杨尚刚,美图公司数据库高级DBA,负责美图后端数据存储平台建设和架构设计。前新浪高级数据库工程师,负责新浪微博核心数据库架构改造优化,以及数据库相关的服务器存储选型设计。

前言


MySQL数据库大家应该都很熟悉,而且随着前几年的阿里的去IOE,MySQL逐渐引起更多人的重视。

MySQL历史

  • 1979年,Monty Widenius写了最初的版本,96年发布1.0

  • 1995-2000年,MySQL AB成立,引入BDB

  • 2000年4月,集成MyISAM和replication

  • 2001年,Heikki Tuuri向MySQL建议集成InnoDB

  • 2003发布5.0,提供了视图、存储过程等功能

  • 2008年,MySQL AB被Sun收购,09年推出5.1

  • 2009年4月,Oracle收购Sun,2010年12月推出5.5

  • 2013年2月推出5.6 GA,5.7开发中

MySQL的优点

  • 使用简单

  • 开源免费

  • 扩展性“好”,在一定阶段扩展性好

  • 社区活跃

  • 性能可以满足互联网存储和性能需求,离不开硬件支持

上面这几个因素也是大多数公司选择考虑MySQL的原因。不过MySQL本身存在的问题和限制也很多,有些问题点也经常被其他数据库吐槽或鄙视

MySQL存在的问题

  • 优化器对复杂SQL支持不好

  • 对SQL标准支持不好

  • 大规模集群方案不成熟,主要指中间件

  • ID生成器,全局自增ID

  • 异步逻辑复制,数据安全性问题

  • Online DDL

  • HA方案不完善

  • 备份和恢复方案还是比较复杂,需要依赖外部组件

  • 展现给用户信息过少,排查问题困难

  • 众多分支,让人难以选择

看到了刚才讲的MySQL的优势和劣势,可以看到MySQL面临的问题还是远大于它的优势的,很多问题也是我们实际需要在运维中优化解决的,这也是MySQL DBA的一方面价值所在。并且MySQL的不断发展也离不开社区支持,比如Google最早提交的半同步patch,后来也合并到官方主线。Facebook Twitter等也都开源了内部使用MySQL分支版本,包含了他们内部使用的patch和特性。

数据库开发规范


数据库开发规范定义:开发规范是针对内部开发的一系列建议或规则, 由DBA制定(如果有DBA的话)。

开发规范本身也包含几部分:基本命名和约束规范,字段设计规范,索引规范,使用规范。

规范存在意义

  • 保证线上数据库schema规范

  • 减少出问题概率

  • 方便自动化管理

  • 规范需要长期坚持,对开发和DBA是一个双赢的事情

想想没有开发规范,有的开发写出各种全表扫描的SQL语句或者各种奇葩SQL语句,我们之前就看过开发写的SQL 可以打印出好几页纸。这种造成业务本身不稳定,也会让DBA天天忙于各种救火。

基本命名和约束规范

  • 表字符集选择UTF8 ,如果需要存储emoj表情,需要使用UTF8mb4(MySQL 5.5.3以后支持)

  • 存储引擎使用InnoDB

  • 变长字符串尽量使用varchar varbinary

  • 不在数据库中存储图片、文件等

  • 单表数据量控制在1亿以下

  • 库名、表名、字段名不使用保留字

  • 库名、表名、字段名、索引名使用小写字母,以下划线分割 ,需要见名知意

  • 库表名不要设计过长,尽可能用最少的字符表达出表的用途

字段规范

  • 所有字段均定义为NOT NULL ,除非你真的想存Null

  • 字段类型在满足需求条件下越小越好,使用UNSIGNED存储非负整数 ,实际使用时候存储负数场景不多

  • 使用TIMESTAMP存储时间

  • 使用varchar存储变长字符串 ,当然要注意varchar(M)里的M指的是字符数不是字节数;使用UNSIGNED INT存储IPv4 地址而不是CHAR(15) ,这种方式只能存储IPv4,存储不了IPv6

  • 使用DECIMAL存储精确浮点数,用float有的时候会有问题

  • 少用blob text

关于为什么定义不使用Null的原因

1、浪费存储空间,因为InnoDB需要有额外一个字节存储

2、表内默认值Null过多会影响优化器选择执行计划

关于使用datatime和timestamp,现在在5.6.4之后又有了变化,使用二者存储在存储空间上大差距越来越小 ,并且本身datatime存储范围就比timestamp大很多,timestamp只能存储到2038年。

单表60亿记录等大数据场景的MySQL优化和运维之道

索引规范

  • 单个索引字段数不超过5,单表索引数量不超过5,索引设计遵循B+ Tree索引最左前缀匹配原则

  • 选择区分度高的列作为索引

  • 建立的索引能覆盖80%主要的查询,不求全,解决问题的主要矛盾

  • DML和order by和group by字段要建立合适的索引

  • 避免索引的隐式转换

  • 避免冗余索引

关于索引规范,一定要记住索引这个东西是一把双刃剑,在加速读的同时也引入了很多额外的写入和锁,降低写入能力,这也是为什么要控制索引数原因。之前看到过不少人给表里每个字段都建了索引,其实对查询可能起不到什么作用。

冗余索引例子

  • idx_abc(a,b,c)

  • idx_a(a) 冗余

  • idx_ab(a,b) 冗余

隐式转换例子

字段:remark varchar(50) NOT Null

MySQL>SELECT id, gift_code FROM gift WHERE deal_id = 640 AND remark=115127; 1 row in set (0.14 sec) 

MySQL>SELECT id, gift_code FROM pool_gift WHEREdeal_id = 640 AND remark=‘115127’; 1 row in set (0.005 sec)

字段定义为varchar,但传入的值是个int,就会导致全表扫描,要求程序端要做好类型检查

SQL类规范

  • 尽量不使用存储过程、触发器、函数等

  • 避免使用大表的JOIN,MySQL优化器对join优化策略过于简单

  • 避免在数据库中进行数学运算和其他大量计算任务

  • SQL合并,主要是指的DML时候多个value合并,减少和数据库交互

  • 合理的分页,尤其大分页

  • UPDATE、DELETE语句不使用LIMIT ,容易造成主从不一致

数据库运维规范


运维规范主要内容

  • SQL审核,DDL审核和操作时间,尤其是OnlineDDL

  • 高危操作检查,Drop前做好数据备份

  • 权限控制和审计

  • 日志分析,主要是指的MySQL慢日志和错误日志

  • 高可用方案

  • 数据备份方案

版本选择

  • MySQL社区版,用户群体最大

  • MySQL企业版,收费

  • Percona Server版,新特性多

  • MariaDB版,国内用户不多

建议选择优先级为:MySQL社区版 > Percona Server > MariaDB > MySQL 企业版,不过现在如果大家使用RDS服务,基本还以社区版为主。

Online DDL问题

原生MySQL执行DDL时需要锁表,且锁表期间业务是无法写入数据的,对服务影响很大,MySQL对这方面的支持是比较差的。大表做DDL对DBA来说是很痛苦的,相信很多人经历过。如何做到Online DDL呢,是不是就无解了呢?当然不是!

单表60亿记录等大数据场景的MySQL优化和运维之道

上面表格里提到的 Facebook OSC和5.6 OSC也是目前两种比较靠谱的方案

MySQL 5.6的OSC方案还是解决不了DDL的时候到从库延时的问题,所以现在建议使用Facebook OSC这种思路更优雅

下图是Facebook OSC的思路

单表60亿记录等大数据场景的MySQL优化和运维之道

后来Percona公司根据Facebook OSC思路,用perl重写了一版,就是我们现在用得很多的pt-online-schema-change,软件本身非常成熟,支持目前主流版本。

使用pt-online-schema-change的优点有:

  • 无阻塞写入

  • 完善的条件检测和延时负载策略控制

值得一提的是,腾讯互娱的DBA在内部分支上也实现了Online DDL,之前测试过确实不错,速度快,原理是通过修改InnoDB存储格式来实现。

使用pt-online-schema-change的限制有:

  • 改表时间会比较长(相比直接alter table改表)

  • 修改的表需要有唯一键或主键

  • 在同一端口上的并发修改不能太多

可用性

关于可用性,我们今天分享一种无缝切主库方案,可以用于日常切换,使用思路也比较简单

在正常条件下如何无缝去做主库切换,核心思路是让新主库和从库停在相同位置,主要依赖slave start until 语句,结合双主结构,考虑自增问题。

单表60亿记录等大数据场景的MySQL优化和运维之道

MySQL集群方案:

  • 集群方案主要是如何组织MySQL实例的方案

  • 主流方案核心依然采用的是MySQL原生的复制方案

  • 原生主从同步肯定存在着性能和安全性问题

MySQL半同步复制:

现在也有一些理论上可用性更高的其它方案

  • Percona XtraDB Cluster(没有足够的把控力度,不建议上)

  • MySQL Cluster(有官方支持,不过实际用的不多)

单表60亿记录等大数据场景的MySQL优化和运维之道

红框内是目前大家使用比较多的部署结构和方案。当然异常层面的HA也有很多第三方工具支持,比如MHA、MMM等,推荐使用MHA。

sharding拆分问题

  • Sharding is very complex, so itʼs best not to shard until itʼs obvious that you will actually need to!

  • sharding是按照一定规则数据重新分布的方式

  • 主要解决单机写入压力过大和容量问题

  • 主要有垂直拆分和水平拆分两种方式

  • 拆分要适度,切勿过度拆分

  • 有中间层控制拆分逻辑最好,否则拆分过细管理成本会很高

曾经管理的单表最大60亿+,单表数据文件大小1TB+,人有时候就要懒一些。

单表60亿记录等大数据场景的MySQL优化和运维之道

上图是水平拆分和垂直拆分的示意图

数据库备份

首先要保证的,最核心的是数据库数据安全性。数据安全都保障不了的情况下谈其他的指标(如性能等),其实意义就不大了。

备份的意义是什么呢?

  • 数据恢复!

  • 数据恢复!

  • 数据恢复!

目前备份方式的几个纬度:

  • 全量备份 VS 增量备份

  • 热备 VS 冷备

  • 物理备份 VS 逻辑备份

  • 延时备份

  • 全量binlog备份

建议方式:

  • 热备+物理备份

  • 核心业务:延时备份+逻辑备份

  • 全量binlog备份

借用一下某大型互联网公司做的备份系统数据:一年7000+次扩容,一年12+次数据恢复,日志量每天3TB,数据总量2PB,每天备份数据量百TB级,全年备份36万次,备份成功了99.9%。

主要做的几点:

  • 备份策略集中式调度管理

  • xtrabackup热备

  • 备份结果统计分析

  • 备份数据一致性校验

  • 采用分布式文件系统存储备份

备份系统采用分布式文件系统原因:

  • 解决存储分配的问题

  • 解决存储NFS备份效率低下问题

  • 存储集中式管理

  • 数据可靠性更好

使用分布式文件系统优化点:

  • Pbzip压缩,降低多副本存储带来的存储成本,降低网络带宽消耗

  • 元数据节点HA,提高备份集群的可用性

  • erasure code方案调研

数据恢复方案

目前的MySQL数据恢复方案主要还是基于备份来恢复,可见备份的重要性。比如我今天下午15点删除了线上一张表,该如何恢复呢?首先确认删除语句,然后用备份扩容实例启动,假设备份时间点是凌晨3点,就还需要把凌晨3点到现在关于这个表的binlog导出来,然后应用到新扩容的实例上,确认好恢复的时间点,然后把删除表的数据导出来应用到线上。

性能优化


复制优化

MySQL复制:

  • 是MySQL应用得最普遍的应用技术,扩展成本低

  • 逻辑复制

  • 单线程问题,从库延时问题

  • 可以做备份或读复制

问题很多,但是能解决基本问题。

单表60亿记录等大数据场景的MySQL优化和运维之道

上图是MySQL复制原理图,红框内就是MySQL一直被人诟病的单线程问题。

单线程问题也是MySQL主从延时的一个重要原因,单线程解决方案

  • 官方5.6+多线程方案

  • Tungsten为代表的第三方并行复制工具

  • sharding

单表60亿记录等大数据场景的MySQL优化和运维之道

上图是MySQL5.6 目前实现的并行复制原理图,是基于库级别的复制,所以如果你只有一个库,使用这个意义不大。

当然MySQL也认识到5.6这种并行的瓶颈所在,所以在5.7引入了另外一种并行复制方式,基于logical timestamp的并行复制,并行复制不再受限于库的个数,效率会大大提升。

单表60亿记录等大数据场景的MySQL优化和运维之道

上图是5.7的logical timestamp的复制原理图

刚才我也提到MySQL原来只支持异步复制,这种数据安全性是非常差的,所以后来引入了半同步复制,从5.5开始支持。

单表60亿记录等大数据场景的MySQL优化和运维之道

上图是原生异步复制和半同步复制的区别。可以看到半同步通过从库返回ACK这种方式确认从库收到数据,数据安全性大大提高。

在5.7之后,半同步也可以配置你指定多个从库参与半同步复制,之前版本都是默认一个从库。

对于半同步复制效率问题有一个小的优化,就是使用5.6+的mysqlbinlog以daemon方式作为从库,同步效率会好很多。

关于更安全的复制,MySQL 5.7也是有方案的,方案名叫Group replication 官方多主方案,基于Corosync实现。

单表60亿记录等大数据场景的MySQL优化和运维之道

主从延时问题

原因:一般都会做读写分离,其实从库压力反而比主库大/从库读写压力大非常容易导致延时。

解决方案:

  • 首先定位延时瓶颈

  • 如果是IO压力,可以通过升级硬件解决,比如替换SSD等

  • 如果IO和CPU都不是瓶颈,非常有可能是SQL单线程问题,解决方案可以考虑刚才提到的并行复制方案

  • 如果还有问题,可以考虑sharding拆分方案

提到延时不得不提到很坑人的Seconds behind master,使用过MySQL的应该很熟悉。

这个值的源码里算法

long time_diff= ((long)(time(0) – mi->rli.last_master_timestamp) – mi->clock_diff_with_master);

Secondsbehindmaster来判断延时不可靠,在网络抖动或者一些特殊参数配置情况下,会造成这个值是0但其实延时很大了。通过heartbeat表插入时间戳这种机制判断延时是更靠谱的

复制注意点:

  • Binlog格式,建议都采用row格式,数据一致性更好

  • Replication filter应用

主从数据一致性问题:

  • row格式下的数据恢复问题

InnoDB优化

成熟开源事务存储引擎,支持ACID,支持事务四个隔离级别,更好的数据安全性,高性能高并发,MVCC,细粒度锁,支持O_DIRECT。

主要优化参数:

  • innodbfileper_table =1

  • innodbbufferpool_size,根据数据量和内存合理设置

  • innodbflushlog_attrxcommit= 0 1 2

  • innodblogfile_size,可以设置大一些

  • innodbpagesize

  • Innodbflushmethod = o_direct

  • innodbundodirectory 放到高速设备(5.6+)

  • innodbbufferpool_dump

  • atshutdown ,bufferpool dump (5.6+)

单表60亿记录等大数据场景的MySQL优化和运维之道

上图是5.5 4G的redo log和5.6 设置大于4G redo log文件性能对比,可以看到稳定性更好了。innodblogfile_size设置还是很有意义的。

InnoDB比较好的特性:

  • Bufferpool预热和动态调整大小,动态调整大小需要5.7支持

  • Page size自定义调整,适应目前硬件

  • InnoDB压缩,大大降低数据容量,一般可以压缩50%,节省存储空间和IO,用CPU换空间

  • Transportable tablespaces,迁移ibd文件,用于快速单表恢复

  • Memcached API,full text,GIS等

InnoDB在SSD上的优化:

  • 在5.5以上,提高innodbwriteiothreads和innodbreadiothreads

  • innodbiocapacity需要调大*

  • 日志文件和redo放到机械硬盘,undo放到SSD,建议这样,但必要性不大

  • atomic write,不需要Double Write Buffer

  • InnoDB压缩

  • 单机多实例

也要搞清楚InnoDB哪些文件是顺序读写,哪些是随机读写。

随机读写:

  • datadir

  • innodbdata file_path

  • innodbundo directory

顺序读写:

  • innodbloggrouphomedir

  • log-bin

InnoDB VS MyISAM:

  • 数据安全性至关重要,InnoDB完胜,曾经遇到过一次90G的MyISAM表repair,花了两天时间,如果在线上几乎不可忍受

  • 并发度高

  • MySQL 5.5默认引擎改为InnoDB,标志着MyISAM时代的落幕

TokuDB:

  • 支持事务 ACID 特性,支持多版本控制(MVCC)

  • 基于Fractal Tree Index,非常适合写入密集场景

  • 高压缩比,原生支持Online DDL

  • 主流分支都支持,收费转开源 。目前可以和InnoDB媲美的存储引擎

目前主流使用TokuDB主要是看中了它的高压缩比,Tokudb有三种压缩方式:quicklz、zlib、lzma,压缩比依次更高。现在很多使用zabbix的后端数据表都采用的TokuDB,写入性能好,压缩比高。

下图是我之前做的测试对比和InnoDB

单表60亿记录等大数据场景的MySQL优化和运维之道

单表60亿记录等大数据场景的MySQL优化和运维之道

上图是sysbench测试的和InnoDB性能对比图,可以看到TokuDB在测试过程中写入稳定性是非常好的。

tokudb存在的问题:

  • 官方分支还没很好的支持

  • 热备方案问题,目前只有企业版才有

  • 还是有bug的,版本更新比较快,不建议在核心业务上用

比如我们之前遇到过一个问题:TokuDB的内部状态显示上一次完成的checkpoint时间是“Jul 17 12:04:11 2014”,距离当时发现现在都快5个月了,结果堆积了大量redo log不能删除,后来只能重启实例,结果重启还花了七八个小时。

MySQL优化相关的case

Query cache,MySQL内置的查询加速缓存,理念是好的,但设计不够合理,有点out。

锁的粒度非常大MySQL 5.6默认已经关闭

When the query cache helps, it can help a lot. When it hurts, it can hurt a lot.明显前半句已经没有太大用处,在高并发下非常容易遇到瓶颈。

关于事务隔离级别 ,InnoDB默认隔离级别是可重复读级别,当然InnoDB虽然是设置的可重复读,但是也是解决了幻读的,建议改成读已提交级别,可以满足大多数场景需求,有利于更高的并发,修改transaction-isolation。

单表60亿记录等大数据场景的MySQL优化和运维之道

单表60亿记录等大数据场景的MySQL优化和运维之道

上图是一个比较经典的死锁case,有兴趣可以测试下。

关于SSD

关于SSD,还是提一下吧。某知名大V说过“最近10年对数据库性能影响最大的是闪存”,稳定性和性能可靠性已经得到大规模验证,多块SATA SSD做Raid5,推荐使用。采用PCIe SSD,主流云平台都提供SSD云硬盘支持。

最后说一下大家关注的单表60亿记录问题,表里数据也是线上比较核心的。

先说下当时情况,表结构比较简单,都是bigint这种整型,索引比较多,应该有2-3个,单表行数60亿+,单表容量1.2TB左右,当然内部肯定是有碎片的。

形成原因:历史遗留问题,按照我们前面讲的开发规范,这个应该早拆分了,当然不拆有几个原因:

  • 性能未遇到瓶颈 ,主要原因

  • DBA比较“懒“

  • 想看看InnoDB的极限,挑战一下。不过风险也是很大的,想想如果在一个1.2TB表上加个字段加个索引,那感觉绝对酸爽。还有就是单表恢复的问题,恢复时间不可控。

我们后续做的优化 ,采用了刚才提到的TokuDB,单表容量在InnoDB下1TB+,使用Tokudb的lzma压缩到80GB,压缩效果非常好。这样也解决了单表过大恢复时间问题,也支持online DDL,基本达到我们预期。

今天讲的主要针对MySQL本身优化和规范性质的东西,还有一些比较好的运维经验,希望大家能有所收获。今天这些内容是为后续数据库做平台化的基础。我今天分享就到这里,谢谢大家。

QA


Q1:use schema;select from table; 和select from schema.table;两种写法有什么不一样吗?会对主从同步有影响吗?
对于主从复制来说执行效率上差别不大,不过在使用replication filter时候这种情况需要小心,应该要使用ReplicateWildIgnoreTable这种参数,如果不使用带wildignore,第一种方式会有问题,过滤不起作用。

Q2:对于用于MySQL的ssd,测试方式和ssd的参数配置方面,有没有好的建议?主要针对ssd的配置哈

关于SATA SSD配置参数,建议使用Raid5,想更保险使用Raid50,更土豪使用Raid 10

单表60亿记录等大数据场景的MySQL优化和运维之道

上图是主要的参数优化,性能提升最大的是第一个修改调度算法的

Q3:数据库规范已制定好,如何保证开发人员必须按照规范来开发?

关于数据库规范实施问题,也是有两个方面吧,第一、定期给开发培训开发规范,让开发能更了解。第二、还是在流程上规范,比如把我们日常通用的建表和字段策略固化到程序,做成自动化审核系统。这两方面结合 效果会比较好。

Q4:如何最大限度提高innodb的命中率?

这个问题前提是你的数据要有热点,读写热点要有交集,否则命中率很难提高。在有热点的前提下,也要求你的你的内存要足够大,能够存更多的热点数据。尽量不要做一些可能污染bufferpool的操作,比如全表扫描这种。

Q5:主从复制的情况下,如果有CAS这样的需求,是不是只能强制连主库?因为有延迟的存在,如果读写不在一起的话,会有脏数据。

如果有CAS需求,确实还是直接读主库好一些,因为异步复制还是会有延迟的。只要SQL优化的比较好,读写都在主库也是没什么问题的。

Q6:关于开发规范,是否有必要买国标?

这个国标是什么东西,不太了解。不过从字面看,国标应该也是偏学术方面的,在具体工程实施时候未必能用好。

Q7:主从集群能不能再细化一点那?不知道这样问合适不?

看具体哪方面吧。主从集群每个小集群一般都是采用一主多从方式,每个小集群对应特定的一组业务。然后监控备份和HA都是在每个小集群实现。

Q8:如何跟踪数据库table某个字段值发生变化?

追踪字段值变化可以通过分析row格式binlog好一些。比如以前同事就是通过自己开发的工具来解析row格式binlog,跟踪数据行变化。

Q9:对超大表水平拆分,在使用MySQL中间件方面有什么建议和经验分享?

对于超大表水平拆分,在中间件上经验不是很多,早期人肉搞过几次。也使用过自己研发的数据库中间件,不过线上应用的规模不大。关于目前众多的开源中间件里,360的atlas是目前还不错的,他们公司内部应用的比较多。

Q10:我们用的MySQL proxy做读负载,但是少量数据压力下并没有负载,请问有这回事吗?

少量数据压力下,并没有负载 ,这个没测试过,不好评价

Q11:对于binlog格式,为什么只推荐row,而不用网上大部分文章推荐的Mix ?

这个主要是考虑数据复制的可靠性,row更好。mixed含义是指如果有一些容易导致主从不一致的SQL ,比如包含UUID函数的这种,转换为row。既然要革命,就搞的彻底一些。这种mix的中间状态最坑人了。

Q12: 读写分离,一般是在程序里做,还是用proxy ,用proxy的话一般用哪个?

这个还是独立写程序好一些,与程序解耦方便后期维护。proxy国内目前开源的比较多,选择也要慎重。

Q13: 我想问一下关于mysql线程池相关的问题,什么情况下适合使用线程池,相关的参数应该如何配置,老师有这方面的最佳实践没有?

线程池这个我也没测试过。从原理上来说,短链接更适合用线程池方式,减少建立连接的消耗。这个方面的最佳配置,我还没测试过,后面测试有进展可以再聊聊。

Q14: 误删数据这种,数据恢复流程是怎么样的(从库也被同步删除的情况)?

看你删除数据的情况,如果只是一张表,单表在几GB或几十GB。如果能有延时备份,对于数据恢复速度是很有好处的。恢复流程可以参考我刚才分享的部分。目前的MySQL数据恢复方案主要还是基于备份来恢复 ,可见备份的重要性。比如我今天下午15点删除了线上一张表,该如何恢复呢。首先确认删除语句,然后用备份扩容实例启动,假设备份时间点是凌晨3点。就还需要把凌晨3点到现在关于这个表的binlog导出来,然后应用到新扩容的实例上。确认好恢复的时间点,然后把删除表的数据导出来应用到线上。

Q15: 关于备份,binlog备份自然不用说了,物理备份有很多方式,有没有推荐的一种,逻辑备份在量大的时候恢复速度比较慢,一般用在什么场景?

物理备份采用xtrabackup热备方案比较好。逻辑备份一般用在单表恢复效果会非常好。比如你删了一个2G表,但你总数据量2T,用物理备份就会要慢了,逻辑备份就非常有用了。

查看原文

jyhjn1 收藏了文章 · 2018-11-19

挑逗Bootstrap4源代码 - Grid篇(下)

本文所引用的版本为Bootstrap 4 Beta版,阅读者请先下载好相关源文件。

时光荏苒,若后续版本代码发生变化,将看心情进行更新补充。如果你觉得本文不错,欢迎评论支持,如需转载请标明作者及出处,谢谢。

在日常使用Bootstrap的时候,我们最常见的做法是给HTML内的元素添加上预设的类名,这种方法直观且易于调试。但是对于一个前端洁癖患者来说,在HTML标签内添加一大堆类名简直和内联style一样让人深恶痛绝。那么在这种时候,学会使用Bootstrap的Scss,利用其内置的函数和@mixin来对你自己命名的类进行样式添加就成了一件很棒很酷的事。

涉及文件

  • 变量:_variables.scss(起始行:171,结束行:205)
  • 函数:_function.scss //其中函数主要用于变量文件中,在此不述
  • 公共类:_flex.scss //在utilities文件夹下,用于flex布局的各种类,只是给属性加了包装,同样不述
  • @mixin:

    • _breakpoints.scss //断点函数区,包括断点区间查找、自动扩展媒体查询等功能
    • _grid.scss //辅助mixin,提供容器、行、列创建
    • _grid-framworks.scss //核心mixin,依据断点,循环创建以flex为基础的12网格
  • 引用:_grid.scss //自动创建包括12列网格在内的布局,本质上是对_grid-frameworks和_grid的引用
  1. _grid-frameworks.scss
@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {
  // Common properties for all breakpoints
  %grid-column {
    position: relative;
    width: 100%;
    min-height: 1px; // Prevent columns from collapsing when empty
    padding-right: ($gutter / 2);
    padding-left:  ($gutter / 2);
  }

  @each $breakpoint in map-keys($breakpoints) {
    $infix: breakpoint-infix($breakpoint, $breakpoints);
    @for $i from 1 through $columns {
      .col#{$infix}-#{$i} {
        @extend %grid-column;
      }
    }
    .col#{$infix},
    .col#{$infix}-auto {
      @extend %grid-column;
    }

    @include media-breakpoint-up($breakpoint, $breakpoints) {
      
      .col#{$infix} {
        flex-basis: 0;
        flex-grow: 1;
        max-width: 100%;
      }
      .col#{$infix}-auto {
        flex: 0 0 auto;
        width: auto;
        max-width: none; // Reset earlier grid tiers
      }

      @for $i from 1 through $columns {
        .col#{$infix}-#{$i} {
          @include make-col($i, $columns);
        }
      }

      @for $i from 1 through $columns {
        .order#{$infix}-#{$i} {
          order: $i;
        }
      }
    }
  }
}

整个文件只有一个@mixin make-grid-columns(),这个@mixin才是真正的搭建12列Grid网格系统的核心,让我们细细来拆解。

首先是参数,引入了列数($columns)、列间距($gutter)、断点列表($breakpoints)。这三者都已经预设好了,不需要操心。

%grid-column {
    position: relative;
    width: 100%;
    min-height: 1px; 
    padding-right: ($gutter / 2);
    padding-left:  ($gutter / 2);
}

下面是占位符%grid-column,这个在之前讲make-col-ready()时已经讲过了,出于减小css体积的考虑,占位符在这里显然优于@mixin。那么这里定义的就是一个列的基本属性,而min-height的设置也是考虑到当列为空时不至于坍缩。

@each $breakpoint in map-keys($breakpoints) {}

接下来是一个大循环,这个循环的根本目的在于为不同的断点加上相匹配的类,它的循环依据就是断点名。

$infix: breakpoint-infix($breakpoint, $breakpoints);

让我们以“md”为例进入这个循环,首先是$infix变量,结合先前的知识,我们了解到,这个$infix的值将是”-md”,让我们记住它,接下来,又是一个小循环。

@for $i from 1 through $columns {
   .col#{$infix}-#{$i} {
      @extend %grid-column;
   }
}

这个@for循环的目的,就是为了创建12列的基本属性,因为所有的列都具备%grid-column定义的基本属性,所以我们将这份共性总结出来,单独设置一个循环进行类名构建,当这个循环结束,你就可以看到css里出现了用逗号相连的col-md-1一直到col-md-12

.col-md-1, .col-md-2, .col-md-3, 
.col-md-4, .col-md-5, .col-md-6, 
.col-md-7, .col-md-8, .col-md-9, 
.col-md-10, .col-md-11, .col-md-12, 
 { position: relative; width: 100%; min-height: 1px; padding-right: 15px; padding-left: 15px; }

它们都有着同样的属性,这也是@extend的好处。但是请注意,我们并没有设置这些列的宽度,所以它们现在还是不可用的状态。

.col#{$infix},
.col#{$infix}-auto {
    @extend %grid-column;
}

接下来有一个单独的小家伙,通过它,你可以发现.col-md这个类也出现了,也是同样的属性,这个我们就先不去管它了。

@include media-breakpoint-up($breakpoint, $breakpoints) {}

接下拉就是大头了,我们引入了media-brakpoint-up()函数,拿到了media查询的外包装

@media (min-width:768px){}

花括号里就是我们要往这个外包装里塞的内容了。

.col#{$infix} {
    flex-basis: 0;
    flex-grow: 1;
    max-width: 100%;
 }

首先是一个小家伙,又是这货,它单独给col-md定义了一些flex属性,它的意思表明,当“.col-md”出现的时候,它将撑满整行剩余的空间。

.col#{$infix}-auto {
     flex: 0 0 auto;
     width: auto;
     max-width: none; 
 }

接下来定义了“.col-md-auto”类,这个类挺奇葩,宽度随着内容走,跟列宽毛关系都没有。

@for $i from 1 through $columns {
   .col#{$infix}-#{$i} {
      @include make-col($i, $columns);
   }
}

接下来的@for循环就开始定义我们的列宽了,利用make-col(),我们得到了从col-md-1col-md-12各种不同的列宽。

@for $i from 1 through $columns {
   .order#{$infix}-#{$i} {
      order: $i;
   }
}

最后的这个@for循环也是从1到12遍历一次,熟悉flex的同学会知道,添加的这个order属性就相当于名次,数值越小越靠前,而在flex布局下的12列Grid也正是依靠这一点来对不同的列进行排序的。

至此,“md”下的12列Grid框架构建完成。依次类推,其它的断点列也都是如此生成的。比较特殊的还是断点”xs”,由于先前提到的原因,最小的那一部分是没有前缀的,所以你在css里看到的.col-[1-12]就可以视作.col-xs-[1-12],这需要适应一下。

_grid.scss (根目录)

自动化构建其它诸如”.container”之类的元素,那个$enable-grid-classes布尔值,在变量里的第126行,默认为ture

$enable-grid-classes: true !default;

换句话说,如果你哪天心情不好,不想用bootstrap的网格系统了,直接把这里改成false就行。

@if $enable-grid-classes {
  .row {
    @include make-row();
  }

  .no-gutters {
    margin-right: 0;
    margin-left: 0;

    > .col,
    > [class*="col-"] {
      padding-right: 0;
      padding-left: 0;
    }
  }
}

在这里面有一个新定义的类“.no-gutter”,它这个嵌入式展开后是“.no-gutter>col,.no-gutter>[class*=”col-”]”,从结构可以看出来,它就是加在row元素上的,可以取消列的默认间距。

使用建议

说回我们的Grid,我们知道,如果不加以控制,那么Bootstrap在编译Scss的时候会自动生成所有断点下的列,如果你不打算给每个等级都用上一种布局,那么自动编译的Scss将会产生大量冗余的css代码。

.col-1, .col-2, .col-3,
.col-4, .col-5, .col-6, 
.col-7, .col-8, .col-9, 
.col-10, .col-11, .col-12, 
.col, .col-auto, 
.col-sm-1, .col-sm-2, .col-sm-3, 
.col-sm-4, .col-sm-5, .col-sm-6, 
.col-sm-7, .col-sm-8, .col-sm-9, 
.col-sm-10, .col-sm-11, .col-sm-12, .col-sm, .col-sm-auto, 
.col-md-1, .col-md-2, .col-md-3, 
.col-md-4, .col-md-5, .col-md-6, 
.col-md-7, .col-md-8, .col-md-9, 
.col-md-10, .col-md-11, .col-md-12, 
.col-md, .col-md-auto, 
.col-lg-1, .col-lg-2, .col-lg-3, 
.col-lg-4, .col-lg-5, .col-lg-6, 
.col-lg-7, .col-lg-8, .col-lg-9, 
.col-lg-10, .col-lg-11, .col-lg-12, 
.col-lg, .col-lg-auto, 
.col-xl-1, .col-xl-2, .col-xl-3, 
.col-xl-4, .col-xl-5, .col-xl-6, 
.col-xl-7, .col-xl-8, .col-xl-9, 
.col-xl-10, .col-xl-11, .col-xl-12, 
.col-xl, .col-xl-auto

比如我只想做桌面端和手机端两种适配,那么我可能只需要lgsm的列,css中其它的列代码对我是没用的。所以面对这种情况,我们就需要对Bootstrap进行修改。这里提供两种化用方式,如果你有其它的主意,也欢迎在评论区留言。

  1. 在变量文件中注释掉不需要的列,与之对应的容器等级也不要忘记
$grid-breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  //lg: 992px,
  //xl: 1200px
) !default;
$container-max-widths: (
  sm: 540px,
  md: 720px,
  //lg: 960px,
  //xl: 1140px
) !default;

这是一种很简单的方式,也很直观,我需要什么列就用什么列,不需要的我就给扔掉,但是注意,别扔掉默认值为0的xs。如果你不是前端洁癖患者,只是想缩减css体积,那么推荐用这种方式。

  1. 自己新建一个@mixin,替换掉默认的循环创建行为

第一种方式有一个问题,即虽然你注释掉了不需要的列,但仍需要在HTML中写入预设的类名。如果你不希望在HTML中写入一堆以”col-”开头的类名,那么就尝试自己定义一个@mixin,来创建自己的列吧。
创建之前注意,在bootstrap-grid.scss中将@import “grid”注释掉,咱们不需要自动创建。

其次,新建一个scss文件,引入bootstrap-grid

@import "bootstrap-grid"
%grid-column{
  position: relative;
  width: 100%;
  min-height: 1px;
  padding-left: ($grid-gutter-width/2);
  padding-right: ($grid-gutter-width/2);
}
@mixin make-my-col($breakpoint:null,$size:null,$breakpoints: $grid-breakpoints){
  @extend %grid-column;
  @include media-breakpoint-up($breakpoint,$breakpoints){
    @include make-col($size,$grid-columns);
  }
}

在这里我提供一个自定义的@mixin,名字也很简单make-my-col。包含两个参数,一个是$breakpoint(断点名),一个是$size(列宽)。这个@mixin其实是make-grid-columns()的简化版。

具体原理不用多说了,因为是自用,所以我就没去考虑参数验证的问题。如果你有这方面的需求,要应用到项目中,可以考虑加上参数验证。

调用也很简单,在你需要的类中直接调用即可,传入断点名和列宽,就能创建在对应视宽下的列了。

@import "bootstrap-grid";
.side{
  @include make-my-col(sm,2);
  @include make-my-col(md,6);
}
.content{
  @include make-my-col(sm,10);
  @include make-my-col(md,6);
}

P.S.写的时候注意顺序,要按照升序排列,小的放在上面,即sm在md上面,写反了md将失效。

这种方式同样有一个问题,在小型项目中,这样编译出的css能显著缩减css的体积。但在大型项目中,各种类名交错混杂,利用这种创建单个列的方式,最后生成的css代码不见得比bootstrap预定义的类名更好,所以请规范命名,一些容器元素最好保持固定宽度和固定变化。

Scss显然是利用Bootstrap更高效的方式,根据需求,以上两种方式可任选其一。当然,如果你有其它的利用方式,也可以随心所欲地蹂♂躏Bootstrap~

总结

“好的代码像一首诗”

以前对这句话只觉得莫名高大上,却没有多少感触。而在阅读了Bootstrap用Scss写的源码之后,却是真切地感受到了这一点。打开变量(_variables.scss)文件的时候,带给我的震惊是不可言表的。严谨而有序,体量庞大而层次井然。这些模块如果一个个看下来,相信会获益良多。所以如果你和我一样,是Scss的初学者,那么浏览一下Bootstrap的源码,绝对会爽翻。

Grid应该是Bootstrap的核心区块了,从这里入手虽然比较难,但是方便从根本上了解Bootstrap的运行方式。

总的来说,Beta版本的Bootstrap4相比于Alpha版本已经往前迈了一大步,告别了传统盒模型的布局方式,转身拥抱flexbox,同时删去了很多以前的残余代码,在初期,习惯使用b3的同学可能会觉得不大适应,具体表现是

“哎?我写了这个类咋没反应啊?”

眼下这个时候,官方说明文档都不见得会同步更新,看源代码才是最直接的阅读学习方式。

Grid篇到此结束,谢谢阅读,欢迎指出本文的错漏之处,前端新手上路,请多指教。

查看原文

认证与成就

  • 获得 0 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2015-10-13
个人主页被 362 人浏览