企业数据越存越多,存储容量与查询性能、以及存储成本之间的矛盾对于技术团队来说是个普遍难题。这个难题在 Elasticsearch 与 ClickHouse 这两个场景中尤为突出,为了应对不同热度数据对查询性能的要求,这两个组件在架构设计上就有一些将数据进行分层的策略。
同时,在存储介质方面,随着云计算的发展,对象存储以低廉的价格和弹性伸缩的空间获得了企业的青睐。越来越多的企业将温、冷数据迁移至对象存储。但如果将索引、分析组件直接对接至对象存储时会发生查询性能、兼容性等问题。
这篇文章将为大家介绍这两个场景中冷热数据分层的基本原理,以及如何通过使用 JuiceFS 来应对在对象存储上存在的问题。
01- Elasticsearch 数据分层结构详解
在介绍 ES 如何实现冷热数据分层策略之前先来了解三个相关的概念:Data Stream,Index Lifecycle Management 和 Node Role。
Data Stream
Data Stream(数据流)是 ES 中一个重要概念,它有如下特征:
流式写入:它是一个流式写入的数据集,而不是一个固定大小的集合;
仅追加写:它是用追加写的方式将数据更新进去,且不需要修改历史数据;
时间戳:每一条新增的数据都会有一个时间戳记录是什么时候产生的;
多个索引:在 ES 里有一个索引的概念,每一条数据最终会落到它对应的一个索引中,但是数据流是一个更上层、更大的概念,一个数据流背后可能会有很多索引,这些索引是根据不同的规则来生成的。一个数据流虽然由很多的索引来构成,但是只有最新的索引才是可写的,历史索引是只读的,一旦固化好之后就不能再修改。
日志数据就是符合数据流特征的一类数据,它是只追加写,同时也得有时间戳,用户会根据不同的维度,比如按天或者按其他的维度来生成新的索引。
下图是一个数据流建立索引的简单示例,在用数据流的过程中,ES 会直接写到最新的索引,而不是历史索引,历史索引不会被修改。随着后续更多新的数据生成,这个索引也会沉淀成为一个老的索引。
下图,当用户往 ES 里面去写数据时,大致分为两个阶段:
阶段 1:数据会先写到内存的 In-memory buffer 缓冲区;
阶段 2:缓冲区根据一定的规则和时间,再落到本地磁盘上,就是下图绿色的持久化的数据,在 ES 中叫做 Segment。
这个过程中可能会有一些时间差,在持久化的过程中,如果去触发查询, 新创建的Segment 不能被搜索到。一旦这个 Segment 持久化完成之后,就可以立即被上层的查询引擎搜索。
Index Lifecycle Management
Index Lifecycle Management,简称 ILM,就是索引的生命周期管理。ILM 将索引的生命周期定义为 5 个阶段:
热数据(Hot):需要频繁更新或者查询的数据;
温数据(Warm):不再更新,但仍会被频繁查询的数据;
冷数据(Cold):不再更新,且查询频率较低的数据;
极冷数据(Frozen):不再更新,且几乎不会被查询的数据。可以比较放心地把这类数据放在一个相对最低速最便宜的存储介质中;
删除数据(Delete) : 不再需要用到,可以放心删除的数据。
一个索引里的数据,不管是 index 还是 segment,都会经历这些阶段,这个分类的规则很好地帮助用户去管理 ES 里的数据,用户可以自己定义不同阶段的规则。
Node Role
在 ES 中,每一个部署节点都会有一个 Node Role,也就是节点角色。每一个 ES 节点会分配不同的角色,比如 master、data、ingest 等。用户可以结合节点角色,以及上文提到的不同生命周期的阶段来组合进行数据管理。
数据节点会有不同的阶段,可能是一个存储热数据的节点,也可能是一个存储温数据、冷数据,甚至极冷数据的节点。需要根据节点的功能去给他分配不同的角色,同时会给不同的角色的节点配置不同的硬件。
比如,对于热数据节点需要配置高性能的 CPU 或者磁盘,对于温冷数据的节点,基本上认为这些数据被查询的频率较低,这个时候其实对于某些计算资源的硬件要求就没有那么高了。
节点角色是根据生命周期的不同阶段来定义的,需要注意的一点是,每一个 ES 节点,可以有多种角色,这些角色并不是一一对应的关系。下面有个示例,在 ES 的 YAML 文件里面配置的时候,node.roles 就是节点角色的配置,可以针对这个节点应该有的角色给它配置多种角色。
node.roles: ["data_hot", "data_content"]
生命周期策略
在了解完 Data Stream 、Index Lifecycle Management、Node Role 这些概念以后,就可以为数据创建一些不同的生命周期策略(Lifecycle Policy)。
根据生命周期策略中定义的不同维度的索引特征,如索引的大小、索引里的文档的数量、索引创建的时间,ES 可以自动地帮用户把某个生命周期阶段的数据滚动到另一个阶段,在 ES 中的术语是 rollover。
比如,用户可以制定基于索引大小维度的特征,把热数据滚动到温数据,或者根据一些其它规则,再把温数据滚动到冷数据。这样,索引在不同生命周期的阶段之间去滚动的时候,相应的它索引的数据也会去做迁移和滚动。ES 可以自动完成这些工作,但是生命周期策略则需要用户自己来定义。
下面的截图,是 Kibana 的管理界面,用户可以通过图形化的方式去配置生命周期策略。可以看到有三个阶段,从上到下分别是热数据、温数据以及冷数据。
展开其中热数据阶段的高级设置,可以看到更详细,上文提到的基于不同维度特征的策略配置,如在下图右边看到的这三个选项。
索引的大小,示意图上的例子是 50GB,当索引的大小超过 50GB 的时候,就会把它从热数据阶段滚动到温数据阶段。
最大的文档数,ES 里索引的单元是文档,用户数据是以文档的形式写入 ES 中的,所以文档数也是一个可以衡量的指标。
最大索引创建时间,这里的示例是 30 天,假设某个索引已经创建了 30 天了,这个时候就会触发刚刚提到的从热数据阶段到温数据的滚动。
02- ClickHouse 数据分层架构详解
下图是一组从大到小的俄罗斯套娃,它非常形象地展现了 ClickHouse 的数据管理模式, MergeTree 引擎。
Table: 在图片的最右边是一个最大的概念,用户最开始要创建或者能够直接接触到的就是 Table;
Partition:是一个更小的维度或者更小的粒度。在 ClickHouse 里,数据分成 Partition 来存储,每个 Partition 会有一个标识;
Part:在每个 Partition 中,又会再进一步地细分为多个 Part。如果查看 ClickHouse 磁盘上存储的数据格式,可以认为每一个子目录就是一个 Part;
Column:在 Part 里会看到一些更小粒度的数据,即 Column。ClickHouse 的引擎使用的是列式存储,所有的数据都是按照列存的方式来组织。在 Part 目录里会看到很多列,比如 Table 可能有100 列,就会有 100 个 Column 文件;
Block:每个 Column 文件里是按照 Block 的粒度来组织。
下面这个示例中,在 table 目录下可以看到有 4 个子目录,每个子目录就是上文提到的 Part。
$ ls -l /var/lib/clickhouse/data//
drwxr-xr-x 2 test test 64B Aug 8 13:46 202208_1_3_0
drwxr-xr-x 2 test test 64B Aug 8 13:46 202208_4_6_1
drwxr-xr-x 2 test test 64B Sep 8 13:46 202209_1_1_0
drwxr-xr-x 2 test test 64B Sep 8 13:46 202209_4_4_
图示的最右边这一列,每个子目录的名字前面可能是一个时间,比如 202208 类似这样的前缀,202208 其实就是 Partition 名。Partition 名字是用户自己来定义的,但是按照约定俗成或者一些实践习惯,通常会使用时间来命名。
比如, 202208 这个 Partition,它会有两个子目录,子目录就是 Part,一个 Partition 通常会由多个 Part 来构成。用户在往 ClickHoue 写入数据时,会先写到内存里,再根据内存里的数据结构,持久化到磁盘上。同一个Partition 里面的数据如果比较大的话,在磁盘上就会变成很多 part。ClickHouse 官方建议不要在一个 Table 下创建太多 Part,它会定期或者不定期地对 Part 进行合并,减少总的 Part 数量。Merge 的概念就是合并 Part,这也是 MergeTree 这个引擎的名字来源之一。
再通过一个例子来了解 Part。Part 里会有很多小文件,有一些是元信息,比如索引信息,帮助用户快速查找数据。
$ ls -l /var/lib/clickhouse/data///202208_1_3_0
-rw-r--r-- 1 test test ?? Aug 8 14:06 ColumnA.bin
-rw-r--r-- 1 test test ?? Aug 8 14:06 ColumnA.mrk
-rw-r--r-- 1 test test ?? Aug 8 14:06 ColumnB.bin
-rw-r--r-- 1 test test ?? Aug 8 14:06 ColumnB.mrk
-rw-r--r-- 1 test test ?? Aug 8 14:06 checksums.txt
-rw-r--r-- 1 test test ?? Aug 8 14:06 columns.txt
-rw-r--r-- 1 test test ?? Aug 8 14:06 count.txt
-rw-r--r-- 1 test test ?? Aug 8 14:06 minmax_ColumnC.idx
-rw-r--r-- 1 test test ?? Aug 8 14:06 partition.dat
-rw-r--r-- 1 test test ?? Aug 8 14:06 primary.id
在示例的右侧,以 Column 作为前缀的这些文件是实际的数据文件,相比元信息通常会比较大。这个示例中只有 A、B 两列,实际的表里可能有很多列。所有这些文件,包括元信息、索引信息,都会共同帮助用户快速地在不同文件之间去做跳转或者查找。
ClickHouse 存储策略
如果要在 ClickHouse 里做冷热数据分层,会用到类似于 ES 中提到的生命周期策略,在 ClickHouse 里称为存储策略(Storage Policy)。
与 ES 稍有不同,ClickHouse 官方并没有将数据划分不同的阶段,比如热数据、温数据、冷数据这些不同的阶段,ClickHouse 提供了一些规则和配置方法,需要用户自己来制定分层策略。
每个 ClickHouse 节点支持同时配置多块磁盘,存储介质可以是多种多样的。比如,一般用户为了性能会给 ClickHouse 节点配置 SSD 盘;对于一些温冷数据,用户可以把数据存储在成本更低的介质,如机械盘。ClickHouse 的用户对底层存储介质是无感知的。
与 ES 相似,ClickHouse 用户需要根据数据不同的维度特征去制定存储策略,比如每个 part 子目录的大小、整个磁盘的剩余空间比例等,当满足某个维度特征设定的条件时就会触发存储策略的执行。这个策略会将某一个 part 从一块盘迁移到另外一块盘。在 ClickHouse 中,一个节点配置的多块盘是有优先级的,默认情况下数据会优先落在最高优先级的盘上。这样实现了 Part 从一个存储介质转移到另外一个存储介质上。
通过 ClickHouse 的一些 SQL 命令,如 MOVE PARTITION/PART 命令可以手动触发数据迁移,用户也可以通过这些命令做一些功能性的验证。其次有某些情况下,可能也希望能够通过手动的方式,而不是自动转移的方式来显式把 part 从当前的存储介质上转移到另外一个存储介质上。
ClickHouse 还支持基于时间的迁移策略,这是一个独立于存储策略的概念。数据写入后,ClickHouse 会按照每个表的 TTL 属性设置的时间来触发磁盘上数据的迁移。比如设置 TTL 为 7 天,ClickHouse 就会把表中超过 7 天的数据从当前的磁盘(如默认的 SSD)再写到另外一个更低优先级的磁盘上(如 JuiceFS)。
03- 温冷数据存储:为什么使用对象存储+ JuiceFS ?
企业把温、冷数据存放到云上后,存储成本相较于传统的 SSD 架构大为下降。企业还享受到了云上的弹性伸缩空间;不用为数据存储去做任何运维操作,比如扩缩容,或者一些数据清理类的工作 。温冷数据所需的存储容量比热数据大很多,尤其是随着时间推移,会产生大量需要长期保存的数据,如果这些数据都存储在本地,相应的运维工作将不堪重负。
但如果在对象存储上使用 Elasticsearch、ClickHouse 这类数据应用组件,会存在写入性能差、兼容性等问题。希望兼顾查询性能的企业,开始在云上寻找解决方案。在这样的背景之下,JuiceFS 被越来越多地应用于数据分层的架构之中 。
通过下面 ClickHouse 写入性能测试可以直观了解到写入SSD、JuiceFS 以及对象存储的性能差异。
JuiceFS 的写入吞吐量远大于直接对接对象存储,接近 SSD 。当用户把热数据转移到温暖数据这一层时,对于写入性能也有一定要求。在迁移的过程中,如果底层存储介质的写入性能差,整个迁移的流程也会拖得很长,对于整个 pipeline 或数据管理也会带来一些挑战。
下图的 ClickHouse 查询性能测试使用真实业务中的数据,并选取几个典型的查询场景进行测试。其中 q1-q4 是扫描全表的查询,q5-q7 是命中主键索引的查询。测试结果如下图:
JuiceFS 与 SSD 盘的查询性能基本相当,平均差异在 6% 左右,但是对象存储相比 SSD 盘有 1.4 至 30 倍的性能下降 。得益于 JuiceFS 高性能的元数据操作以及本地缓存特性,可以自动将查询请求需要的热数据缓存在 ClickHouse 节点本地,大幅提升了 ClickHouse 的查询性能。需要注意的是以上测试中对象存储是通过 ClickHouse 的 S3 磁盘类型进行访问,这种方式只有数据是存储在对象存储上,元数据还是在本地磁盘。如果通过类似 S3FS 的方式把对象存储挂载到本地,性能会有进一步的下降。
另外值得一提的是 JuiceFS 是一个完全兼容 POSIX 的文件系统,它能够与上层应用(如 Elasticsearch、ClickHouse)有很好的兼容。用户对底层存储是分布式文件系统或者是本地磁盘是没有感知的。如果直接使用对象存储,不能很好地实现与上层应用的兼容。
04- 实操:ES + JuiceFS
Step 1:准备多种类型节点,分配不同角色 。每一个 ES 节点可以分配不同的角色,比如存热数据、温数据、冷数据等,用户需要准备不同机型的节点来匹配不同角色的需求。
Step 2:挂载 JuiceFS 文件系统 。一般用户将 JuiceFS 用于温、冷数据的存储,用户需要在 ES 温数据节点或冷数据的节点上把 JuiceFS 文件系统挂载到本地。用户可以通过符号链接或其它方式把挂载点配置到 ES 中去,让 ES 认为它的数据存储在本地目录里,但这个目录背后其实是一个 JuiceFS 文件系统。
Step 3:创建生命周期策略 。这个需要每个用户自己去定制,用户既可以通过 ES API 去创建,也可以通过 Kibana 去创建,Kibana 提供了一些相对便捷的方式去创建和管理生命周期策略。
Step 4:为索引设置生命周期策略 。创建完生命周期策略之后,用户需要把这个策略应用到索引上,也就是要为索引去设置刚刚创建好的策略。用户可以通过索引模板的方式,可以在 Kibana 里创建索引模板,也可以通过 index.lifycycle.name,显式通过 API 配置。
这里有几个小提示:
Tip 1:Warm 或 Cold 节点的副本数(replica)可以设置为 1 。所有数据本质上都是放在 JuiceFS 上,它的底层是对象存储,因而数据的可靠性已经足够高了,所以在 ES 这边可以适当降低副本数,节省存储空间。
Tip 2:开启 Force merge 可能会导致节点 CPU 持续占用,酌情关闭 。从热数据转移到温数据这个阶段时,ES 会将所有热数据索引对应的底层 segment 做合并。如果开启 Force merge 这个功能,ES 会先合并完这些 segment 以后,再把它存储到温数据的底层系统。然而合并 segment 是一个非常消耗 CPU 的过程,如果温数据的数据节点同时也需要承载一些查询请求,可以酌情关闭这个功能能,也就是原封不动地把数据保留下来,直接写到底层存储中。
Tip 3:Warm 或 Cold 阶段的索引可以设置为只读 。在给温数据和冷数据阶段建立索引时,我们基本上可以认为这些数据是只读的,这些阶段的索引不会被修改。设置为只读可以适当降低温冷数据节点上的资源,比如内存可以释放一些,从而节省一些在温节点或者冷节点上的硬件资源。
05- 实操:ClickHouse + JuiceFS
Step 1:在所有 ClickHouse 节点上挂载 JuiceFS 文件系统。 这个路径可以是任意路径,因为 ClickHouse 会有一个配置文件去指向挂载点。
Step 2:修改 ClickHouse 配置,新增 JuiceFS 盘。 在 ClickHouse 中把刚刚挂载好的 JuiceFS 文件系统挂载点添加进来,让 ClickHouse 可以识别这个新磁盘。
Step 3:新增存储策略,设定下沉数据规则。 这个存储策略会根据用户的规则去不定期的、自动地将数据从默认磁盘上下沉到指定的,比如 JuiceFS 中。
Step 4:为特定表设置存储策略及 TTL。 存储策略制定好之后,需要把这个策略应用到某一个表上。前期测试阶段和验证阶段,可以把用相对大一点的表去做测试和验证,如果用户希望基于时间维度来实现数据下沉,就同时也需要在表上设置 TTL。整个下沉过程是一个自动的机制,可以通过 ClickHouse 的 system 表查看当前正在进行数据迁移的 part 以及迁移进度。
Step 5:手动移动 part 进行验证。 可以通过手动执行 MOVE PARTITION
命令的方式去验证当前的配置或存储策略是否生效。
下图是一个具体示例,在 ClickHouse 中有一个叫做 storage_configuration
的配置项,其中包含 disks 配置,这里会把 JuiceFS 作为一个盘加进来,我们将它命名为“jfs”,但其实可以用任意名字,挂载点是 /jfs
目录。
/jfs default 1073741824 jfs 0.1
再往下是 policies 配置项,这里定义了一个叫做 hot_and_cold
的存储策略,用户需要定义一些具体的规则,如 volumes 中按照先热后冷的优先级排列,数据首先会落到 volumes 里的第一个 hot 盘上,及默认的 ClickHouse 磁盘,一般是本地的 SSD。
volumes 中的 max_data_part_size_bytes
配置表示当某一个 part 的大小超过设定的大小之后,就会触发存储策略的执行,对应的 part 会下沉到下一个 volume,也就是 cold volume。在上面的示例中,cold volume 就是 JuiceFS。
最下面的 move_factor
配置代表 ClickHouse 会根据当前磁盘的剩余空间比例来触发存储策略的执行。
CREATE TABLE test ( d DateTime, ...) ENGINE = MergeTree...TTL d + INTERVAL 1 DAY TO DISK 'jfs'SETTINGS storage_policy = 'hot_and_cold';
如上面的代码所示,有了存储策略之后,在创建表或者修改这个表的 schema 时,可以在 SETTINGS 中设置 storage_policy 为前面定义的 hot_and_cold 存储策略。上述代码中倒数第二行的 TTL 即为上文提过的基于时间的分层规则。在这个示例中,我们指定的表中某一个叫做 d 的列,它的类型是 DateTime,结合 INTERVAL 1 DAY 就表示当新的数据写进来超过一天之后,这些数据就会转移到 JuiceFS 上。
06- 展望
第一,副本共享。 无论是 ES 还是 ClickHouse,他们都是由多副本来保证数据的可用性和可靠性。JuiceFS 本质上是一个共享文件系统,任何一份数据写入到 JuiceFS 之后,不再需要维护多个副本。比如,用户有两个 ClickHouse 节点,都有某一个表的或者某一个 part 的副本,这两个节点都下沉到了 JuiceFS,它可能会写两次一样的数据。未来,我们是否可以做到让上层引擎能够感知到下层使用的是一个共享存储,当数据下沉的时候去降低副本数,这样在不同节点之间是可以做副本共享的。 从应用层来说,用户查看这个表, part 数还是多副本,但实际在底层的存储上只保了一个副本,因为本质上数据是可以共享的。
第二点,故障恢复。 当数据已经下沉到一个远端的共享存储之后,如果 ES 或 ClickHousle 节点宕机故障之后,怎么快速地做故障恢复?除了热数据以外的大部分数据其实都已经转移到了一个远端的共享存储上,这个时候如果要去恢复或创建一个新的节点时,成本会比传统的基于本地盘的故障恢复方式轻量很多,这在 ES 或者 ClickHouse 场景上是值得探索的。
第三点,存算分离。 不管 ES 也好,还是 ClickHouse,整个社区也都在尝试或者探索在云原生的大环境下,怎么去让传统的这些基于本地盘的存储系统变成一个真正的存算分离系统。但存算分离不是仅仅简单地把数据和计算分离就好了,同时要满足上层各种复杂的需求,比如对于查询性能的需求、对于写入性能的需求、对各种维度调优的需求,在存量分离整个大的方向上还是有许多值得探索的技术难点。
第四点,其他上层应用组件数据分层探索。 除了ES 和 ClickHouse 这两个场景,我们最近也有在做一些尝试,把 Apache Pulsar 中的温冷数据下沉到 JuiceFS 中,用到的一些策略和方案与本文中提到的是类似的,只不过在 Apache Pulsar 中,它需要下沉的数据类型或者数据格式不太一样。有了进一步成功实践后,会分享出来。
相关阅读:
你可能感兴趣的:(JuiceFS 在 Elasticsearch/ClickHouse 温冷数据存储中的实践)
html animation 属性,css3学习--css3动画详解一(animation属性)
蜗牛老湿
html animation 属性
***介绍的属性并不完全,写的都是我认为容易混淆的难点属性,所以属性会在最后综合案例展示~Keyframes介绍:Keyframes被称为关键帧,其类似于Flash中的关键帧。在CSS3中其主要以“@keyframes”开头,后面紧跟着是动画名称加上一对花括号“{…}”,括号中就是一些不同时间段样式规则。@keyframeschangecolor{0%{background:red;}100%{b
推罗
葡萄园里的羚羊
推罗就是今天黎巴嫩的泰尔,位于以色列城市阿卡以北37公里,西顿以南32公里,面积大概为53公顷。它是古代腓尼基地区重要的港口城市,一直被当成是腓尼基城市的中心。古代亚述文献将推罗记载为Sur-ri,其最初的名字是sor或者Sur,在古代腓尼基语中就是“岩石”的意思。早期推罗是一座岛屿,亚历山大在公元前332年进攻推罗时将其与陆地链接起来成为了后来的半岛。推罗不仅是从非洲通向亚洲地区的交通要塞,也是
区块链:数据库技术
时汝佳
图片发自App0引言对于区块链的解读,有一种观点是数据库。正好手头有本书《计算机是怎样跑起来的》(日文版重印18次,中文版也多达10次印刷),里面有一章介绍数据库,浅显易懂。1基础概念数据库是数据的基地。适合大规模数据存储的是关系型数据库(RelationalDatabase)。现在大多在用的是关系型数据库,数据被拆分到多张表(Table)中,表与表之间的关系可以被记录下来,为了在表之间建立关系,
老公学跳舞
眉开眼笑_2871
自从参加鬼步舞学习后,陪老公散步时间缩短了,他也没散步了,从刚开始旁边看我们跳舞,到慢慢跟在我们后边跳,期间断断续续地跟着我们蹦,大概也坚持了两个多星期吧,今天队伍里的新音响到了之后,他居然从头跳到尾,以前八点准时结束,今天跳到八点半,身上衣服汗透了,简直可以拧出水来,结束后我们九点到家,回家还没歇一口气,我阳台上收的衣服没整理好,又拉我在镜子面前切磋训练,还扬言要比我跳的好,这是上瘾了的节奏么?
无知的选择
白眼书生
今天是2018年10月28号,距离高考结束已经过去一年多,韩光也不再是当时那个无忧无虑的少年。韩光从来都是一个特别普通的人,高中的时候平平无奇,还被班主任点名说:就你这样的学生,我见的多了,你就混吧,到最后高中毕业,你就哭去吧!按照高能励志的剧情来讲,应该是韩光小朋友奋发图强,改过自新,走向人生巅峰。事实有些差距,他并没有从那时起努力,而是在离高考一个月的时候失了智一样埋头学习。奈何他不是个天才啊
大前端面试题集锦——HTML篇
Midshar.top
前端 html
HTML篇1.什么是DOCTYPE,有何作用?DOCTYPE是用来声明HTML文档应当使用哪种版本来进行解析如果不使用DOCTYPE告知浏览器应当使用哪种版本解析文档,那么浏览器将会开启最大兼容模式来解析文档,一般称为怪异模式.在怪异模式下,可能会产生一些预想不到的bug2.说说对HTML语义化的理解?HTML的语义化指的是在合适的地方使用合适的标签,让人能够根据标签进而大致知晓标签内容语义化使得
极简生活抄书第101天:断舍“离”的能力
兮若耶
极简生活抄书第101天:断舍“离”的能力断舍离从知识理论到实践,经过了不少的纠结与坚定,从刚开始的盲从,认为扔就完事了,到后来需要对物品进行分类和校对作用,最后把可以用但不用的物品做了舍弃,这中间经历的时间就不短。断舍离,从字面上来理解就是要从已经拥有的物品中,把没用的破烂收拾出来,将它们送到更加有用的地方,于此同时了解自己的喜好范围,确定哪些是自己需要的,哪些合适自己的,和哪些物品相处起来舒服的
人老了以后
静字凡
中午带着女儿儿子去公公家做饭,自婆婆去世后,大姑姐照顾公公的时候多,包括公公住院,前几天公公因血压高且手麻住院治疗,好在几天后血压降下来了且手麻也好了,以后他的饮食就要更注意。年龄大了牙口不好了,要吃軟的食物,况且血压高要吃清淡一些的,公公爱吃鱼,于是做了酸菜鱼。饭后和公公聊天,也不能说是聊天,公公耳背,带着助听器听力也有些困难,全程我都是在喊话。公公每天还是如常去公园看他们打牌下棋,有时步行有时
今日份还是有好多快乐呀!
艺馨_
疫情使人日渐发癫,这段时间大家都是安分守己的乖乖仔,已经差不多一个月不出门的自己都差点忘记外面的世界长什么样了哈哈。很久没有见面的朋友,大家这段时间也是各顾各的,各自都在忙着收拾和整理自己的心情和心态,每个人的最重要的任务好像就是尽力保护好自己,不给社会添麻烦,就是在创造最大的贡献。今天下班后早早完成了学习打卡,今天的效率很高,还挺好的,我好像在做着自己本来就比较擅长的事情的时候就会格外自信和开心
vscode 使用git bash,路径分隔符缺少问题
huoxingwen
vscode vscode
window使用bash--login-i使用bash时候,在系统自带的terminal里面进入,测试conda可以正常输出,但是在vscode里面输入conda发现有问题bash:C:\Users\marswennaconda3\Scripts:Nosuchfileordirectory实际路径应该要为C:\Users\marswen\anaconda3\Scripts少了\a,很明显\a在vs
python numpy安装步骤-NumPy 安装
编程大乐趣
NumPy安装Python官网上的发行版是不包含NumPy模块的。我们可以使用以下几种方法来安装。1、使用已有的发行版本对于许多用户,尤其是在Windows上,最简单的方法是下载以下的Python发行版,它们包含了所有的关键包(包括NumPy,SciPy,matplotlib,IPython,SymPy以及Python核心自带的其它包):Anaconda:免费Python发行版,用于进行大规模数据
你在“他乡”还好吗?
文荣_f978
今天是公元2018年的4月5日,清明节。淅淅沥沥的小雨时断时续的下了整一天,傍晚时分,终是停了下来。在这个特殊的日子里,算起来,你己经走了整整三年零4天了。在这三年里,在这一千多个日日夜夜里,在每个风起的时候;雨落的时候;飘雪的时候,我们无时无刻不在想着你,念着你,你知道吗?只是从不敢提及你,大人小孩都小心冀翼护着那个伤疤,不敢揭开它,露出血红的伤口。互相慰藉,互相欺骗。甚至小儿小小年纪反过来安慰
python中列表的常见操作
945048125
python python
列表的相关操作列表中存放的数据是可以进行修改的,比如"增"、"删"、"改""添加元素("增"append,extend,insert)1.append:在列表的末尾进行添加>>>a=[123,345]>>>a.append(678)>>>a[123,345,678]2.extend可以将另一个集合中的元素逐一添加到列表中a=['a','b','c']b=['d','e','f']>>>a.exte
胃不好,要远离4种食物,饭后出现4种迹象,及时检查,切莫拖延
一杯故事和酒
中医讲,内伤脾胃,百病由生!然而,生活中,“十人九胃”,越来越多的人,在承受着胃病的折磨。4种食物,会“加速”胃部癌变,再喜欢也要少吃食物1、咸菜等腌制品高盐饮食和长期吃腌制品,是导致胃癌的2大危险因素。有研究表明,经常吃腌制品的人,其患胃癌的风险会增加4-5倍。高盐,可直接损伤胃黏膜;腌制品,会在胃内产生强致癌物亚硝胺;强致癌物亚硝胺+损伤的胃黏膜=增加胃癌几率。食物2、麻辣烫麻辣烫,过于辛辣刺
CSS接入的四种方式
本来无一物_f1f2
1.内联内联式是将CSS样式集中写在网页的标签对的标签对中。格式如下:...此处写CSS样式缺点是对于一个包含很多网页的网站,在每个网页中使用内联式,进行修改样式时非常麻烦。单一网页可以考虑使用内联式。2.内嵌具体表现形式3.外联也是将一个.css文件引入到HTML文件中,在网页的标签对中使用标记来引入外部样式表文件,使用语法如下:4.链接式在css文件中@importurl(./b.css);总
canvas插件 fabric.js 使用
lxw_powerfulCat
前端 javascript fabric html
fabric.js使用fabric.js是常用的canvas插件1,在项目中使用2,特殊用法①,基本设置②,画板数据的导入导出③,遮罩Pattern(引用官网案例)④,多个对象合并,并设置为fabric背景(适用于变色和更多场景)⑤,把canvas对象或者fabric对象导出为图片⑥,位置的获取思路一:(计算法,没算出来)思路二(记录位置)补充:鼠标位移留痕不规则线条在如下代码中注意事项fabri
2019-04-16
我的响当当
姓名:张静公司:山东科普电源系统有限公司【日精进打卡第281天】【知~学习】《六项精进》大纲背诵1遍共295遍《大学》开篇背诵1遍共378遍【行~实践】一、修身:(对自己个人)走了9000多步二、齐家:(对家庭和家人)给伯伯打电话三、建功:(对工作)作废青州公共资源项目外管证开发票准备济南铁路信息项目外管证审核业务员报销单据{积善}:发愿从2018年7月9日起1年行365件善事。今日1善,累计23
君埋泉下泥销骨,我寄人间雪满头。
南歌吟
君埋泉下泥销骨,我寄人间雪满头。(想你逝去九泉尸骨已经化成泥沙,我还暂时寄住人间白发满头。)出自唐代白居易的《梦微之》夜来携手梦同游,晨起盈巾泪(泪水沾满头巾)莫收。漳浦老身三度病,咸阳宿草(墓地上隔年的草)八回秋。君埋泉下泥销骨(尸骨化为泥沙),我寄人间雪满头。阿卫韩郎相次去,夜台(坟墓)茫昧(模糊)得知不?词意理解:夜里做梦与你携手共同游玩,早晨醒来泪水流满巾也不擦拭。在漳浦我三次生病,长安城
D咖智能咖啡机:营业利器,品质与效率的完美结合
lily363926046a
咖啡机招商 咖啡机加盟 无人售货机 人工智能 机器人 科技
D咖作为中国知名国产商用全自动咖啡机品牌,持续引领商用全自动智能咖啡机赛道技术、产品、创新的行业新标准,目前为全国几十个地区提供全场景自助咖啡机解决方案,并获得了广泛的认可和口碑。一、便捷操作:一键即可享受美味咖啡在快节奏的现代商业环境中,咖啡机作为重要的饮品供应设备,其品质与效率直接影响着顾客的消费体验和企业运营的顺畅度,D咖自助咖啡机采用先进的咖啡冲泡技术,确保每一杯咖啡都能达到高品质的标准。
2022年5月25日《孩子不合群怎么办》培训总结
高沙沙18032957006
——高沙沙+春蕾六幼科技在进步,社会在进步,孩子在进步,这也必然需要我们教育跟上时代的步伐。感受1.随着信息技术的爆发,孩子也深深的被这些信息所吸引,玩手机、玩电子游戏成了孩子的最爱。这些信息有利也有害,我们要学会用正确的方式引导孩子向好的方面发展。2.在拒绝孩子长时间玩手机或电子游戏时,我们家长的态度一定要坚定。收获1.孩子在成长的道路上免不了磕磕碰碰,这时候我们最需要的是理解爷爷奶奶在照看孩子
动画框架:Animation
lengbingteng
OGRE_API
在说API之前先了解一下动画的原理:以一个人行走为例,要想使人能够行走,必须在这个人模型上设置一些特真点,然后让这些点在每一帧按照一定的规则进行动,从而改变整个模型的状态。在ogre里面,Keyframe类描述的是一个特征点(一个结点、一根骨头、一个顶点)的某一个关键帧的状态(位置、缩放、朝向等)和对应的时间点。一个可驱动点的所有keyframe组合成一个track,每个可驱动点都有一个他的tra
【解决方案】多机共用一套键鼠
靖待
解决方法 多机协作
由现实问题引发的思考(可不看)日常在实验室学习,目前在实验室常用四台设备:台式电脑、笔记本、ipad、手机。受限:1.台式电脑鼠标坏了。2.笔记本键盘舒适度不如机械键盘。3.实验室桌子小,除了设备还有一堆书籍纸张,已经放置不下更多的键盘鼠标。4.校园网只能同时在线三个设备。5.懒惰(滑稽),不愿意在两个设备间用U盘拷来拷去。之前的痛:一个鼠标发射器每日从台式主机插拔换到笔记本上,伸手去够笔记本键盘
Vue2 中组件的本质、组件的实例化、组件实例的挂载
纷飞丿
vue杂谈 vue.js 前端 javascript
这篇博客的内容是讲讲在Vue2中,组件在底层的本质。在这里,直接抛出结论:组件的本质就是一个个的构造函数,这些函数以组件的定义options对象为参数,在Vue2中,最顶级的组件就是我们从vue.js中导入的Vue函数,而子组件是Vue底层通过extend函数创建出来的VueComponent函数。通过new这些组件的构造函数,我们可以创建出组件实例。1,顶级组件(Vue构造函数)有哪些重要的属性
当天事情当天毕
吴庞炜
曾几何时,有过很多计划,可是总是夜里想法千万种,白天又走回原路。除了没有胆量去付诸实践,拖拉也是一种很不好的习惯,所以从今天起要列清单并分类,逐个目标完成,否则要狠狠地罚自己一下。正所谓“一屋不扫,何以扫天下。”
如何在京东购物中获得高额返利?尝试高省APP创始邀请码
高省_飞智666600
如果你经常在京东购物,你可能会想知道如何拿到返利。另外,京东返利APP的众多选择中,哪一个的佣金最高呢?今天我们来为大家介绍一款非常有潜力的返利神器——高省APP,并提供创始邀请码000018。在开始之前,我们先来了解一下京东返利的工作原理。京东返利是指在购买京东商品时,通过专门的返利平台获取一定比例的返利。这种返利是由返利平台和京东之间的合作来实现的,平台会从自己获得的后台佣金中返还给用户一部分
是我向往的《野生厨房》
阿派iPad
《野生厨房》第一季在前天落幕。原本昨天就想发表这一季的观后感的,写“儿时的年味”去了。不过喜欢《野生厨房》,也跟我的儿时有关。“过家家”这种游戏,相信我们上个世纪末的孩子都玩过。在那个BB机的年代,在我们村里,小孩动画片都看的少,都是玩泥巴、玩树叶、玩沙子、玩石头……不过偶尔也会不一样。有时候我们会和比我们大的小孩玩,同样是玩过家家,但所谓的“饭”和“菜”,再也不是泥巴和树叶,都是货真价实的饭和菜
中秋的诗行
何远智_73d3
中秋的诗行何远智在颗粒归仓的背景中金秋的诗行随地气上扬心境自然,鸟儿都让出枝头一道一道的弧线拉开延伸田园山林村寨和城市的街区楼堂灼金的银杏,燃烧的枫叶有序无序地置换诗人们仰望树梢粘贴雁南飞,韵律悠远美酒开瓶,月饼分享举杯天上宫阙嫦娥错乱了舞步暗香盈袖于民间玉兔衔诗,荡气回肠2018年9月13日于成都一品天下
Python的字符串操作
心繁
python 开发语言
1、获取元素1.1索引索引是最简单的获取元素的方式。根据元素在字符串的下标位置直接输出想要获取的一个元素。例:print(str[1])str="Imissyousomuch,let'smeet."print(str[2])#输出结果为:“m”1.2切片切片可以根据元素的下标位置获取想要的多个元素。例:print(str[2:10:1]),即:print(字符串名称[起始元素下标:结束元素下标+1
前端架构师的职责
acigt0531
前端 git ViewUI
前端架构师的职责没有文档的代码=放弃治疗作为前端架构师,首先要解决的问题就是让日益膨胀的代码可控,因此你需要梳理代码,建立架构,组织文档,管理架构的更新和维护,评审技术方案对架构的影响,核心模块的方案设计,重点项目的方案设计,CodeReview等.架构师和资深开发在工作职责上有着明确的界限,在一个没有架构师的团队,每一个资深开发或多或少都承担了一部分架构的工作,但都是破碎的,不成体系而且不统一,
一条狗的前半生
王晓侠
记得小时候看过一部非常感人的电影,名字忘了,欧美电影,内容大致讲的是一个男人养了一条狗,几年相依为命,因为某种原因走散,经过几年的寻找仍没找到。后来在小镇上偶遇新的主人,主人看它孤苦无依惹起了他的怜悯,决定收养它让它成为家庭一员。在狗狗怀念它的原主人的几年里和新主人相处的也非常和谐,彼此成了生命中最好的朋友相依为命。狗狗的日子就这样一天天的过去同时因为经历与原主人的分别而坚强成长,变得更加成熟懂事
如何用ruby来写hadoop的mapreduce并生成jar包
wudixiaotie
mapreduce
ruby来写hadoop的mapreduce,我用的方法是rubydoop。怎么配置环境呢:
1.安装rvm:
不说了 网上有
2.安装ruby:
由于我以前是做ruby的,所以习惯性的先安装了ruby,起码调试起来比jruby快多了。
3.安装jruby:
rvm install jruby然后等待安
java编程思想 -- 访问控制权限
百合不是茶
java 访问控制权限 单例模式
访问权限是java中一个比较中要的知识点,它规定者什么方法可以访问,什么不可以访问
一:包访问权限;
自定义包:
package com.wj.control;
//包
public class Demo {
//定义一个无参的方法
public void DemoPackage(){
System.out.println("调用
[生物与医学]请审慎食用小龙虾
comsci
生物
现在的餐馆里面出售的小龙虾,有一些是在野外捕捉的,这些小龙虾身体里面可能带有某些病毒和细菌,人食用以后可能会导致一些疾病,严重的甚至会死亡.....
所以,参加聚餐的时候,最好不要点小龙虾...就吃养殖的猪肉,牛肉,羊肉和鱼,等动物蛋白质
org.apache.jasper.JasperException: Unable to compile class for JSP:
商人shang
maven 2.2 jdk1.8
环境: jdk1.8 maven tomcat7-maven-plugin 2.0
原因: tomcat7-maven-plugin 2.0 不知吃 jdk 1.8,换成 tomcat7-maven-plugin 2.2就行,即
<plugin>
你的垃圾你处理掉了吗?GC
oloz
GC
前序:本人菜鸟,此文研究学习来自网络,各位牛牛多指教
1.垃圾收集算法的核心思想
Java语言建立了垃圾收集机制,用以跟踪正在使用的对象和发现并回收不再使用(引用)的对象。该机制可以有效防范动态内存分配中可能发生的两个危险:因内存垃圾过多而引发的内存耗尽,以及不恰当的内存释放所造成的内存非法引用。
垃圾收集算法的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别
shiro 和 SESSSION
杨白白
shiro
shiro 在web项目里默认使用的是web容器提供的session,也就是说shiro使用的session是web容器产生的,并不是自己产生的,在用于非web环境时可用其他来源代替。在web工程启动的时候它就和容器绑定在了一起,这是通过web.xml里面的shiroFilter实现的。通过session.getSession()方法会在浏览器cokkice产生JESSIONID,当关闭浏览器,此
移动互联网终端 淘宝客如何实现盈利
小桔子
移動客戶端 淘客 淘寶App
2012年淘宝联盟平台为站长和淘宝客带来的分成收入突破30亿元,同比增长100%。而来自移动端的分成达1亿元,其中美丽说、蘑菇街、果库、口袋购物等App运营商分成近5000万元。 可以看出,虽然目前阶段PC端对于淘客而言仍旧是盈利的大头,但移动端已经呈现出爆发之势。而且这个势头将随着智能终端(手机,平板)的加速普及而更加迅猛
wordpress小工具制作
aichenglong
wordpress 小工具
wordpress 使用侧边栏的小工具,很方便调整页面结构
小工具的制作过程
1 在自己的主题文件中新建一个文件夹(如widget),在文件夹中创建一个php(AWP_posts-category.php)
小工具是一个类,想侧边栏一样,还得使用代码注册,他才可以再后台使用,基本的代码一层不变
<?php
class AWP_Post_Category extends WP_Wi
JS微信分享
AILIKES
js
// 所有功能必须包含在 WeixinApi.ready 中进行
WeixinApi.ready(function(Api) {
// 微信分享的数据
var wxData = {
&nb
封装探讨
百合不是茶
JAVA面向对象 封装
//封装 属性 方法 将某些东西包装在一起,通过创建对象或使用静态的方法来调用,称为封装;封装其实就是有选择性地公开或隐藏某些信息,它解决了数据的安全性问题,增加代码的可读性和可维护性
在 Aname类中申明三个属性,将其封装在一个类中:通过对象来调用
例如 1:
//属性 将其设为私有
姓名 name 可以公开
jquery radio/checkbox change事件不能触发的问题
bijian1013
JavaScript jquery
我想让radio来控制当前我选择的是机动车还是特种车,如下所示:
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"><
AngularJS中安全性措施
bijian1013
JavaScript AngularJS 安全性 XSRF JSON漏洞
在使用web应用中,安全性是应该首要考虑的一个问题。AngularJS提供了一些辅助机制,用来防护来自两个常见攻击方向的网络攻击。
一.JSON漏洞
当使用一个GET请求获取JSON数组信息的时候(尤其是当这一信息非常敏感,
[Maven学习笔记九]Maven发布web项目
bit1129
maven
基于Maven的web项目的标准项目结构
user-project
user-core
user-service
user-web
src
【Hive七】Hive用户自定义聚合函数(UDAF)
bit1129
hive
用户自定义聚合函数,用户提供的多个入参通过聚合计算(求和、求最大值、求最小值)得到一个聚合计算结果的函数。
问题:UDF也可以提供输入多个参数然后输出一个结果的运算,比如加法运算add(3,5),add这个UDF需要实现UDF的evaluate方法,那么UDF和UDAF的实质分别究竟是什么?
Double evaluate(Double a, Double b)
通过 nginx-lua 给 Nginx 增加 OAuth 支持
ronin47
前言:我们使用Nginx的Lua中间件建立了OAuth2认证和授权层。如果你也有此打算,阅读下面的文档,实现自动化并获得收益。SeatGeek 在过去几年中取得了发展,我们已经积累了不少针对各种任务的不同管理接口。我们通常为新的展示需求创建新模块,比如我们自己的博客、图表等。我们还定期开发内部工具来处理诸如部署、可视化操作及事件处理等事务。在处理这些事务中,我们使用了几个不同的接口来认证:
&n
利用tomcat-redis-session-manager做session同步时自定义类对象属性保存不上的解决方法
bsr1983
session
在利用tomcat-redis-session-manager做session同步时,遇到了在session保存一个自定义对象时,修改该对象中的某个属性,session未进行序列化,属性没有被存储到redis中。 在 tomcat-redis-session-manager的github上有如下说明: Session Change Tracking
As noted in the &qu
《代码大全》表驱动法-Table Driven Approach-1
bylijinnan
java 算法
关于Table Driven Approach的一篇非常好的文章:
http://www.codeproject.com/Articles/42732/Table-driven-Approach
package com.ljn.base;
import java.util.Random;
public class TableDriven {
public
Sybase封锁原理
chicony
Sybase
昨天在操作Sybase IQ12.7时意外操作造成了数据库表锁定,不能删除被锁定表数据也不能往其中写入数据。由于着急往该表抽入数据,因此立马着手解决该表的解锁问题。 无奈此前没有接触过Sybase IQ12.7这套数据库产品,加之当时已属于下班时间无法求助于支持人员支持,因此只有借助搜索引擎强大的
java异常处理机制
CrazyMizzz
java
java异常关键字有以下几个,分别为 try catch final throw throws
他们的定义分别为
try: Opening exception-handling statement.
catch: Captures the exception.
finally: Runs its code before terminating
hive 数据插入DML语法汇总
daizj
hive DML 数据插入
Hive的数据插入DML语法汇总1、Loading files into tables语法:1) LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename [PARTITION (partcol1=val1, partcol2=val2 ...)]解释:1)、上面命令执行环境为hive客户端环境下: hive>l
工厂设计模式
dcj3sjt126com
设计模式
使用设计模式是促进最佳实践和良好设计的好办法。设计模式可以提供针对常见的编程问题的灵活的解决方案。 工厂模式
工厂模式(Factory)允许你在代码执行时实例化对象。它之所以被称为工厂模式是因为它负责“生产”对象。工厂方法的参数是你要生成的对象对应的类名称。
Example #1 调用工厂方法(带参数)
<?phpclass Example{
mysql字符串查找函数
dcj3sjt126com
mysql
FIND_IN_SET(str,strlist)
假如字符串str 在由N 子链组成的字符串列表strlist 中,则返回值的范围在1到 N 之间。一个字符串列表就是一个由一些被‘,’符号分开的自链组成的字符串。如果第一个参数是一个常数字符串,而第二个是type SET列,则 FIND_IN_SET() 函数被优化,使用比特计算。如果str不在strlist 或st
jvm内存管理
easterfly
jvm
一、JVM堆内存的划分
分为年轻代和年老代。年轻代又分为三部分:一个eden,两个survivor。
工作过程是这样的:e区空间满了后,执行minor gc,存活下来的对象放入s0, 对s0仍会进行minor gc,存活下来的的对象放入s1中,对s1同样执行minor gc,依旧存活的对象就放入年老代中;
年老代满了之后会执行major gc,这个是stop the word模式,执行
CentOS-6.3安装配置JDK-8
gengzg
centos
JAVA_HOME=/usr/java/jdk1.8.0_45
JRE_HOME=/usr/java/jdk1.8.0_45/jre
PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
export JAVA_HOME
【转】关于web路径的获取方法
huangyc1210
Web 路径
假定你的web application 名称为news,你在浏览器中输入请求路径: http://localhost:8080/news/main/list.jsp 则执行下面向行代码后打印出如下结果: 1、 System.out.println(request.getContextPath()); //可返回站点的根路径。也就是项
php里获取第一个中文首字母并排序
远去的渡口
数据结构 PHP
很久没来更新博客了,还是觉得工作需要多总结的好。今天来更新一个自己认为比较有成就的问题吧。 最近在做储值结算,需求里结算首页需要按门店的首字母A-Z排序。我的数据结构原本是这样的:
Array
(
[0] => Array
(
[sid] => 2885842
[recetcstoredpay] =&g
java内部类
hm4123660
java 内部类 匿名内部类 成员内部类 方法内部类
在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号。内部类可以间接解决多继承问题,可以使用内部类继承一个类,外部类继承一个类,实现多继承。
&nb
Caused by: java.lang.IncompatibleClassChangeError: class org.hibernate.cfg.Exten
zhb8015
maven pom.xml关于hibernate的配置和异常信息如下,查了好多资料,问题还是没有解决。只知道是包冲突,就是不知道是哪个包....遇到这个问题的分享下是怎么解决的。。
maven pom:
<dependency>
<groupId>org.hibernate</groupId>
<ar
Spark 性能相关参数配置详解-任务调度篇
Stark_Summer
spark cache cpu 任务调度 yarn
随着Spark的逐渐成熟完善, 越来越多的可配置参数被添加到Spark中来, 本文试图通过阐述这其中部分参数的工作原理和配置思路, 和大家一起探讨一下如何根据实际场合对Spark进行配置优化。
由于篇幅较长,所以在这里分篇组织,如果要看最新完整的网页版内容,可以戳这里:http://spark-config.readthedocs.org/,主要是便
css3滤镜
wangkeheng
html css
经常看到一些网站的底部有一些灰色的图标,鼠标移入的时候会变亮,开始以为是js操作src或者bg呢,搜索了一下,发现了一个更好的方法:通过css3的滤镜方法。
html代码:
<a href='' class='icon'><img src='utv.jpg' /></a>
css代码:
.icon{-webkit-filter: graysc