Activiti数据库说明

概要说明

ACT_RE_*: ‘RE’表示repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
ACT_RU_*: ‘RU’表示runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
ACT_ID_*: ‘ID’表示identity。 这些表包含身份信息,比如用户,组等等。
ACT_HI_*: ‘HI’表示history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
ACT_GE_*: 通用数据, 用于不同场景下。
ACT_EVT_*: EVT表示EVENT,目前只有一张表ACT_EVT_LOG,存储事件处理日志,方便管理员跟踪处理。

下面是各个表的大致意义:

意义 备注
ACT_EVT_LOG 事件处理日志
ACT_GE_BYTEARRAY 二进制数据表 保存流程定义图片和xml
ACT_GE_PROPERTY 属性数据表存储整个流程引擎级别的数据
ACT_HI_ACTINST 历史节点表 只记录usertask内容
ACT_HI_ATTACHMENT 历史附件表
ACT_HI_COMMENT 历史意见表
ACT_HI_DETAIL 历史详情表,提供历史变量的查询 流程中产生的变量详细,包括控制流程流转的变量等
ACT_HI_IDENTITYLINK 历史流程人员表
ACT_HI_PROCINST 历史流程实例表
ACT_HI_TASKINST 历史任务实例表
ACT_HI_VARINST 历史变量表
ACT_ID_GROUP 用户组信息表
ACT_ID_INFO 用户扩展信息表
ACT_ID_MEMBERSHIP 用户与用户组对应信息表
ACT_ID_USER 用户信息表
ACT_PROCDEF_INFO  流程定义数据表
ACT_RE_DEPLOYMENT 部署信息表
ACT_RE_MODEL 流程设计模型部署表 流程设计器设计流程后,保存数据到该表
ACT_RE_PROCDEF 流程定义数据表
ACT_RU_EVENT_SUBSCR throwEvent,catchEvent时间监听信息表
ACT_RU_EXECUTION 运行时流程执行实例表
ACT_RU_IDENTITYLINK 运行时流程人员表,主要存储任务节点与参与者的相关信息
ACT_RU_JOB 运行时定时任务数据表
ACT_RU_TASK 运行时任务节点表
ACT_RU_VARIABLE 运行时流程变量数据表

Mysql数据库复制及备份


一、mysql数据库复制概述

复制解决的基本问题是让一台服务器的数据和另外的服务器保持同步。一台主服务器可以连接多台从服务器,并且从服务器也可以反过来作为主服务器。主服务器和从服务器可以位于不同的网络拓扑中,还能对整台服务器、特定的数据库,甚至特定的表进行复制。

MySQL复制大部分都是向后兼容的。这意味着版本较新的服务器可以是版本较老的服务器的从服务器。但老版本的服务器通常不能是新版本号的服务器的从服务器。复制通常不会大幅增加主服务器的开销。它需要主服务器启用二进制日志,这或许会有较大的开销,但是出于备份的目的,无论如何这种开销都是需要的。

复制通常的用途有:数据分布、负载平衡、备份、高可用性和故障转移、测试mysql升级。


二、复制的工作原理

总体上来说,复制有以下三个步骤:

(a)主服务器把数据更改记录到二进制日志中。(这叫做二进制日志事件(binary
log events
)。)

(b)从服务器把主服务器的二进制日志事件拷贝到自己的中继日志(relay
log
)中。

(c)从服务器重放中继日志中的事件,把更改用到自己的数据上。

这三个步骤如下图所示:

第一步:在主服务器上记录二进制日志。在每个更新数据的事务完成之前,主服务器都会把数据更改记录到二进制日志中。即使事务在执行期间是交错的,mysql也会串行地把事务写入到二进制日志中。在把事件写入到二进制日志之后,主服务器告诉存储引擎提交事务。

第二步:从服务器把主服务器的二进制日志拷贝到自己硬盘上,进入所谓的“中继日志(relay log)中。

它首先启动一个工作线程,叫I/O线程。这个I/O线程开启一个普通的客户端连接,然后启动一个特殊的二进制日志转储进程。这个转储进程从主服务器的二进制日志中读取事件。它不会对事件进行轮询。如果它跟上了主服务器,就会进入休眠状态,并等待有新事件发生时主服务器发出的信号。I/O线程把事件写入从服务器的中继日志中。

第三步:SQL从线程处理了该过程的最后一部分。该线程读取了中继日志,并且重放其中的事件,然后更新从服务器的数据。由于这个线程能跟上I/O线程,所以,中继日志一般都是在操作系统的缓存中,因此中继日志的开销很低。同时SQL线程执行的事件也可以被写入从服务器自己的二进制日志中。


三、创建复制以及配置主、从服务器

创建复制主要分为下面几个步骤:

1、  在每一台服务器上建立复制账号。

2、  配置主、从服务器。

3、  指导从服务器进行连接与复制。

下面以实际操作的例子来进一步说明复制的创建。

1、  平台说明:

系统环境:ubuntu10.10

软件版本:mysql-5.5.25

2、  Mysql数据库说明:

在两台不同的计算机里面安装好mysql服务器。

主服务器IP192.168.1.100

从服务器IP192.168.1.104

下面将按照创建复制的步骤进行说明

第一步:在每一台服务器上建立复制账号

Mysql有一些特殊的权限允许复制进程运行。从I/O线程运行在从服务器上,它创建了到主服务器的连接。基于此,在主服务器上就必须创建一个用户账户并且给它合适的权限,这样I/O线程就可以以这个用户的身份连接到主服务器,并且读取它的二进制日志。

上述命令在主服务器上创建一个用户名为repl的账号。这里把用户限制到了本地网络。

第二步:配置主、从服务器

修改主服务器的配置文件my.cnf:加入如下设置

sync_binlog = 1

log-bin = mysql-bin

server-id = 10

innodb_support_xa =1

在主服务器上最重要的二进制日志设置是sync_binlog,这使得mysql在每次提交事务的时候把二进制日志的内容同步到磁盘上,即使服务器崩溃也会把事件写入日志中。

如果不想在服务器崩溃后表被破坏,所以我们使用mysql默认的存储引擎InnoDB

而且必须显式地定义唯一的服务器ID。这里为了避免产生混淆,用10代替1。因为1通常是服务器的默认值。所以使用1容易引起混淆,并导致和没有服务器ID的服务器冲突。

如果log_bin选项在主服务器的配置文件中没有定义,那就重启mysql

验证二进制日志文件已经被创建出来,可以运行如下指令:

从服务器也需要和主服务器类似的配置,并且也需要在从服务器上重启mysql

log-bin=mysql-bin

server-id  = 2

relay_log  = mysql-relay-bin

log_slave_updates = 1

read_only  = 1

skip_slave_start

以上配置单从技术上来说,有些选项是不需要的。实际上,从服务器只有server_id参数是必须的,这里我们也开启了log_bin,并且显式地给了二进制文件一个名字。同时,把主、从服务器的文件名设置成一样的,这样做的目的是便于把从服务器变为主服务器。

此外,还添加了两个其他的可选配置参数:relay_log(定义中继日志的位置和文件名)和log_slave_update(使从服务器把复制的事件记录到自己的二进制日志中)。

Skip_slave_start选项会阻止从服务器在崩溃后自动启动,可以让管理员有机会去修复服务器。

第三步:指导从服务器进行连接与复制

这一个步骤是告诉从服务器如何连接到主服务器并中继其中二进制日志。这一个步骤中,不要直接在配置文件my.cnf里配置,而是应该使用CHANGE MASTER TO命令,在从服务器上输入如下所示命令:

参数说明:

MASTER_HOST:指主服务器IP

MASTER_USER:指复制账号用户名。

MASTER_PASSWORD:指复制用户密码。

MASTER_LOG_FILE:指复制的二进制日志名。

MASTER_LOG_POS:指二进制日志的偏移量。

输入上述指令没有出错的话,将会有成功的提示。

检测输出:

输入上面这条命令,将会出现如下所示内容:

由上图可以看到,此时,从服务器I/O线程和SQL线程正在运行,并且Seconds_Behind_Master不再是NULL(如果想看到是NULL的话,就先STOP
SLAVE
;然后再SHOW SLAVE STATUS\G)。I/O线程正在等待主服务器的事件,这意味着它已经提取了主服务器的所有二进制日志。日志位置已经增加了,表示一些事件已经提取并执行过了。这时,如果在主服务器上做一些数据更改,那么将会看见从服务器上的文件和位置参数都增加了。些时就可以在从服务器上看到数据库改变了。

查看主、从服务器工作线程:

    当然啦,这里显示的例子,是来自运行了一些日子的服务器,所以I/O线程Time列值比较大。从服务器的SQL线程已经空闲了11秒,这表示已经有11秒没有发生事件重放了。Info显示正在执行的查询,目前正在执行的查询为NULL

前面所述都是假设主、从服务器刚刚安装好,使用的数据都是默认的初始化数据,所以两台服务器默认的有相同数据,并且也知道了主服务器的二进制日志。但如果是主服务器已经运行了一段时间了,然后用一台新安装的从服务器进行同步,那么此时,从服务器还没有主服务器的数据。

所以,在复制数据之前,必须用主服务器初始化一台从服务器。为了让从服务器和主服务器同步数据,需要下面三个条件:

1、  某个时间点上主服务器数据的快照。

2、  主服务器当前的日志,以及从快照时间点以来精确的日志字节偏移量。这两个值叫做日志文件坐标,它们共同确定了二进制日志的位置。可以使用SHOW
MASTER STATUS
查找主服务器的日志文件坐标。

3、  从快照时间到现在的主服务器二进制日志文件。

这里,由于我们只用了mysql默认的存储引擎InnoDB,所以,可以使用下面的命令来转储主服务器的所有内容,把它装入从服务器中,并且把从服务器的坐标改成主服务器二进制日志的相应位置:

$mysqldump –h 192.168.1.100 –u repl –p123456 –single-transaction –all-databases –master-data=0 | mysql –p

这样,就可以把主服务器上的数据同步到从服务器上了。

至此,当主服务器上有数据更改,从服务器上的数据也会发生更改。我们管这种数据复制模式叫做主—从模式。

但现实中,通常需要的是两台服务器,当其中一台服务器的数据发生变化,将会把数据同步到另一台服务器中。这种模式叫主—主模式。

配置这种模式,只要将前面所述的主、从服务器配置反过来设置一次就可以了。主服务器和从服务器的设置是对称的。

当设置好后,就可以实现主—主同步复制了。


四、其他复制模式简介

1、一主多从模式

刚才配置从服务器的时候加入了log_slave_updates选项,这样就可以把一台从服务器变成主服务器了,它会指导mysql把自己执行的事件写到二进制日志中,然后自己的从服务器就可以取得这些事件并执行它。如下图所示:

在这种情形下,主服务器的改变导致事件被写入到二进是中。第1个从服务器提取并执行这个事件。

一般来说,这个时候事件的生命周期就结束了,但是因为log_slave_updates 打开了,所以,从服务器就会把它写入到自己的二进制日志中。那么第2个从服务器就可以再次取得这个事件并执行它了。

2、复制过滤器

在主、从服务器的数据复制同步过程中,我们可以有选择性的复制同步一部份数据,这时就要用到复制过滤器。复制过滤器有两种:

(a)主服务器上把事件从二进制日志中过滤掉的过滤器。

(b)从服务器上把事件从中继日志中过滤掉的过滤器。

如下图所示:

控制二进制日志过滤的选项是binlog_do_dbbinlog_ignore_db。但通常不要开启它们。

在从服务器上,replicate_*选项在从服务器SQL线程从中继日志中读取事件的时候进行过滤。可以复制,或者忽略一个或多个数据库。

3、复制拓扑

Mysql数据库的复制拓扑中,必须遵守下面的基本原则:

(a)一个mysql从服务器只能有一个主服务器。

(b)每个从服务器有唯一的服务器ID

(c)一个主服务器可以有很多从服务器。

(d)如果打开了log_slave_update,一个从服务器就能把主服务器的改动传播下去,并且能成为其他从服务器的主服务器。

下面列出一部份复制拓扑的结构示意图:

一台主服务器和多个从服务器

  

拥有从服务器的环形结构

 

    除上述所讲到的复制拓扑外,不家其他一些拓扑,这里不一一叙述。具体在现实工作环境中,要使用哪一个复制模式,将要按照需求进行选择配置。


五、备份与还原

备份数据是数据库管理中最常用的操作。为保证数据库中数据的安全,数据库管理员需要定期的进行数据库备份。当数据库遭到破坏时,通过备份文件来还原数据库。

1、使用mysqldump命令备份

Mysqldump命令可以将数据库中的数据备份成一个文本文件。表的结构和表中的数据将存储在生成的文本文件中。

Mysqldump命令的工作原理:它先查出需要备份的表的结构,再在文本文件中生成一个CREATE语句。然后,将表中的所有记录转换成一个INSERT语句。这些CREATE语句和INSERT语句都是还原时使用的。还原数据时就可以使用其中的CREATE语句来创建表,使用其中的INSERT语句来还原数据。

使用mysqldump命令备份一个数据库的基本语法如下:

Mysqldump –u username –p dbname table1 table2……>BackupName.sql

参数说明:

Dbname:参数表示数据库的名称;

Table1table2:表示表的名称,没有该参数时将备份整个数据库;

BackupName.sql:表示备份文件的名称,文件名前面可以加上一个绝对路径。一般都会将数据库备份成一个后缀名为sql的文件。

但是mysqldump命令备份的文件并非一定要求后缀名为.sql,备份成其他格式的文件也是可以的,但是通常情况下是备份成后缀名为.sql的文件,因为,后缀名为.sql的文件给人的第一感觉就是与数据库有关的文件。

使用mysqldump备份多个数据库:

Mysqldump –u username –p –databases dbname1 dbname2…… > BackupName.sql

可以看到,备份多个数据库和备份一个数据库不同,备份多个数据库要加上“–databases”这个选项,然后后面跟多个数据库的名称。

使用mysqldump备份所有的数据库:

Mysqldump –u username –p –all-databases > BackupName.sql

2、远程备份

远程备份可以分为两种情况:

第一:两台服务器没有进行数据同步设置、从服务器数据跟不上主服务器数据更新或者设置数据同步的时候不是完全数据同步而只份数据同步。

第二:两台服务器进行了数据同步设置、服务器数据几本上不落后主服务器数据并且设置数据同步的时候是完全数据同步。

下面先讨论第一种情况。假设,需要备份的服务器主机IP为:192.168.1.100,首先在该主机上为mysql系统添加root远程登录权限:

mysql> grant all privileges on *.* to  root@’%’ identified by ‘123456;

此时在远端的服务器上就可以登录主机服务器了:

Mysql –h 192.168.1.100 –u root –p

然后输入密码后就可以登录进主服务器的root用户了。

远程备份全部数据库:

 Mysqldump –h 192.168.1.100 –u root –p123456 –all-databases >/home/Backup.sql

这样就可以把远程主机上的数据库数据备份到本地设备上了。

第二种情况比较简单,因为两台服务器数据是完全同步的,也就是说两台服务器上的数据库数据是一致的,所以,对于这种情况,我们只需要用mysqldump命令按本地备份模式进行备份就可以了。

3、数据还原

当管理员的非法操作和计算机的故障等都会破坏数据库文件。当数据库遭到这些意外时,可以通过备份文件将数据库还原到备份时的状态。这样可以将损失降低到最小。

前面说了,我们是用mysqldump命令将数据库中的数据备份成一个文本文件。并且通常来说这个文件会以.sql后缀名结尾。需要还原的时候,可以使用mysql命令来还原备份的数据。

备份文件中通常包含CREATE语句和INSERT语句。Mysql命令可以执行备份文件中的CREATE语句和INSERT语句。通过CREATE语句来创建数据库和表。通过INSERT语句来插入备份的数据。从而达到还原的目的。

Mysql命令的基本语法如下:

Mysql –u root –p [dbname] < backup.sql

参数说明:

Dbname参数表示数据库名称。该参数可选,可以指定数据库名,也可以不指定。指定数据库名时,表示还原该数据库下的表。不指定数据库名时,表示还原特定的一个数据库。

 

mysql show database size

SELECT table_schema                                        "DB Name", 
   Round(Sum(data_length + index_length) / 1024 / 1024, 1) "DB Size in MB" 
FROM   information_schema.tables 
GROUP  BY table_schema; 

arguments

function show(foo, bar) {
  window.alert(foo+' '+bar);
}
function run(f) {
  // use splice to get all the arguments after 'f'
  var args = Array.prototype.splice.call(arguments, 1);
  f.apply(null, args);
}

run(show, 'foo', 'bar');

Two Ways to Check for Palindromes in JavaScript

function palindrome(str) {
  // Step 1. Lowercase the string and use the RegExp to remove unwanted characters from it
  var re = /[\W_]/g; // or var re = /[^A-Za-z0-9]/g;
  
  var lowRegStr = str.toLowerCase().replace(re, '');
  // str.toLowerCase() = "A man, a plan, a canal. Panama".toLowerCase() = "a man, a plan, a canal. panama"
  // str.replace(/[\W_]/g, '') = "a man, a plan, a canal. panama".replace(/[\W_]/g, '') = "amanaplanacanalpanama"
  // var lowRegStr = "amanaplanacanalpanama";
     
  // Step 2. Use the same chaining methods with built-in functions from the previous article 'Three Ways to Reverse a String in JavaScript'
  var reverseStr = lowRegStr.split('').reverse().join(''); 
  // lowRegStr.split('') = "amanaplanacanalpanama".split('') = ["a", "m", "a", "n", "a", "p", "l", "a", "n", "a", "c", "a", "n", "a", "l", "p", "a", "n", "a", "m", "a"]
  // ["a", "m", "a", "n", "a", "p", "l", "a", "n", "a", "c", "a", "n", "a", "l", "p", "a", "n", "a", "m", "a"].reverse() = ["a", "m", "a", "n", "a", "p", "l", "a", "n", "a", "c", "a", "n", "a", "l", "p", "a", "n", "a", "m", "a"]
  // ["a", "m", "a", "n", "a", "p", "l", "a", "n", "a", "c", "a", "n", "a", "l", "p", "a", "n", "a", "m", "a"].join('') = "amanaplanacanalpanama"
  // So, "amanaplanacanalpanama".split('').reverse().join('') = "amanaplanacanalpanama";
  // And, var reverseStr = "amanaplanacanalpanama";
   
  // Step 3. Check if reverseStr is strictly equals to lowRegStr and return a Boolean
  return reverseStr === lowRegStr; // "amanaplanacanalpanama" === "amanaplanacanalpanama"? => true
}
 
palindrome("A man, a plan, a canal. Panama");

继续阅读“Two Ways to Check for Palindromes in JavaScript”

diffTwoArrays

function arr_diff (a1, a2) {
    var a = [], diff = [];
    for (var i = 0; i < a1.length; i++) {
        a[a1[i]] = true;
    }
    for (var i = 0; i < a2.length; i++) {
        if (a[a2[i]]) {
            delete a[a2[i]];
        } else {
            a[a2[i]] = true;
        }
    }
    for (var k in a) {
        diff.push(k);
    }
    return diff;
};
function diffArray(arr1, arr2) {
  var newArr = [];
  // Same, same; but different. 
  var diff1 = arr1.filter(function(x) { return arr2.indexOf(x) < 0; });
  var diff2 = arr2.filter(function(x) { return arr1.indexOf(x) < 0;});
  newArr = diff1.concat(diff2);
  return newArr;
}

diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);

MySQL多种存储引擎比较及应用场景

MySQL有多种存储引擎:
MyISAM、InnoDB、MERGE、MEMORY(HEAP)、BDB(BerkeleyDB)、EXAMPLE、FEDERATED、ARCHIVE、CSV、BLACKHOLE。
MySQL支持数个存储引擎作为对不同表的类型的处理器。MySQL存储引擎包括处理事务安全表的引擎和处理非事务安全表的引擎:

◆ MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。MyISAM在所有MySQL配置里被支持,它是默认的存储引擎,除非你配置MySQL默认使用另外一个引擎。

◆ MEMORY存储引擎提供“内存中”表。MERGE存储引擎允许集合将被处理同样的MyISAM表作为一个单独的表。就像MyISAM一样,MEMORY和MERGE存储引擎处理非事务表,这两个引擎也都被默认包含在MySQL中。
注释:MEMORY存储引擎正式地被确定为HEAP引擎。

◆ InnoDB和BDB存储引擎提供事务安全表。BDB被包含在为支持它的操作系统发布的MySQL-Max二进制分发版里。InnoDB也默认被包括在所 有MySQL 5.1二进制分发版里,你可以按照喜好通过配置MySQL来允许或禁止任一引擎。

◆ EXAMPLE存储引擎是一个“存根”引擎,它不做什么。你可以用这个引擎创建表,但没有数据被存储于其中或从其中检索。这个引擎的目的是服务,在 MySQL源代码中的一个例子,它演示说明如何开始编写新存储引擎。同样,它的主要兴趣是对开发者。

◆ NDB Cluster是被MySQL Cluster用来实现分割到多台计算机上的表的存储引擎。它在MySQL-Max 5.1二进制分发版里提供。这个存储引擎当前只被Linux, Solaris, 和Mac OS X 支持。在未来的MySQL分发版中,我们想要添加其它平台对这个引擎的支持,包括Windows。

◆ ARCHIVE存储引擎被用来无索引地,非常小地覆盖存储的大量数据。

◆ CSV存储引擎把数据以逗号分隔的格式存储在文本文件中。

◆ BLACKHOLE存储引擎接受但不存储数据,并且检索总是返回一个空集。

◆ FEDERATED存储引擎把数据存在远程数据库中。在MySQL 5.1中,它只和MySQL一起工作,使用MySQL C Client API。在未来的分发版中,我们想要让它使用其它驱动器或客户端连接方法连接到另外的数据源。
当你创建一个新表的时候,你可以通过添加一个ENGINE 或TYPE 选项到CREATE TABLE语句来告诉MySQL你要创建什么类型的表:
CREATE TABLE t (i INT) ENGINE = INNODB;
CREATE TABLE t (i INT) TYPE = MEMORY;
虽然TYPE仍然在MySQL 5.1中被支持,现在ENGINE是首选的术语。

如何选择最适合你的存储引擎呢?

下述存储引擎是最常用的:
◆ MyISAM:默认的MySQL插件式存储引擎,它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。注意,通过更改STORAGE_ENGINE配置变量,能够方便地更改MySQL服务器的默认存储引擎。

◆ InnoDB:用于事务处理应用程序,具有众多特性,包括ACID事务支持。

◆ BDB:可替代InnoDB的事务引擎,支持COMMIT、ROLLBACK和其他事务特性。

◆ Memory:将所有数据保存在RAM中,在需要快速查找引用和其他类似数据的环境下,可提供极快的访问。

◆ Merge:允许MySQL DBA或开发人员将一系列等同的MyISAM表以逻辑方式组合在一起,并作为1个对象引用它们。对于诸如数据仓储等VLDB环境十分适合。

◆ Archive:为大量很少引用的历史、归档、或安全审计信息的存储和检索提供了完美的解决方案。

◆ Federated:能够将多个分离的MySQL服务器链接起来,从多个物理服务器创建一个逻辑数据库。十分适合于分布式环境或数据集市环境。

◆ Cluster/NDB:MySQL的簇式数据库引擎,尤其适合于具有高性能查找要求的应用程序,这类查找需求还要求具有最高的正常工作时间和可用性。

◆ Other:其他存储引擎包括CSV(引用由逗号隔开的用作数据库表的文件),Blackhole(用于临时禁止对数据库的应用程序输入),以及Example引擎(可为快速创建定制的插件式存储引擎提供帮助)。
请记住,对于整个服务器或方案,你并不一定要使用相同的存储引擎,你可以为方案中的每个表使用不同的存储引擎,这点很重要。

MySQL Cross Server Select Query

mysqldump could be a solution as mentioned already or you could try using the SELECT … INTO OUTFILE and then LOAD DATA INFILE … commands.

MySQL does have the federated storage engine which might be useful to you. Here’s some more documentation on it http://dev.mysql.com/doc/refman/5.0/en/federated-storage-engine.html I have to confess that I’ve not had huge success with it but it might work for you.

The third solution would be to do the work in your application. Read in the results of the SELECT query line by line and INSERT to the other server line by line. You might run into some issues with data types and null handling that way though.

卖油翁

陈康肃公尧咨(zī) 善射,当世无双 ,公亦以此自矜(jīn)。尝射于家圃(pǔ),有卖油翁释担(dàn)而立,睨(nì)之,久而不去。见其发矢(shǐ)十中八九,但微颔(hàn)之。
康肃问曰:”汝(rǔ)亦知射乎?吾射不亦精乎?”翁曰:”无他, 但手熟(shú)尔。”康肃忿(fèn)然曰:”尔安敢轻吾射!”翁曰:”以我酌(zhuó)油知之。”乃取一葫芦置于地,以钱覆其口,徐以杓(sháo)酌油沥(lì)之,自钱孔入,而钱不湿。因曰:”我亦无他,唯手熟(shú)尔。”康肃笑而遣(qiǎn)之。
此与庄生所谓解牛斫轮者何异?