Android 虚拟多开系列二——技术原理 - Binary-Stream - 博客园

mikel阅读(2101)

来源: Android 虚拟多开系列二——技术原理 – Binary-Stream – 博客园

目录
        Android虚拟多开应用有哪些?
        Android虚拟多开应用技术原理有哪几类?
        Android虚拟多开需求分析
        反虚拟多开技术
正文
一、Android虚拟多开应用列表
应用名称 版本号 开源 公司名称 下载链接
太极 维术(个人) 微信公众号:虚拟框架
VirtualApp 2017.12月停止开源更新 罗盒科技 VirtualApp
LBE平行空间 LBE LBE平行空间
360分身大师 360 360分身大师
DroidPlugin 360 DroidPlugin
小米应用分身 小米 小米手机
双开助手 卓盟 双开助手
360奇酷手机 360 360奇酷手机
克隆大师 未知 克隆大师(项目已停)
二、Android虚拟多开应用技术原理
        1)修改APK(例:克隆大师)
        2)修改Framework(Android多用户机制。例:小米应用分身、360奇酷手机、等)
        3)通过虚拟化技术实现(例:360分身大师、LBE平行空间)
        4)以插件机制运行(例:DroidPlugin)
      A. 原理简述:
         现在市场上常见的虚拟多开应用主要是基于虚拟化技术实现,而虚拟化技术主要通过 Hook 技术实现,因此我们主要考察 Hook 技术。
         Hook技术的分类,在非虫写的《Android软件安全权威指南》第十章有详细描述,推荐。现总结如下:
         按 Java 层 与 Native 层分类, Hook 技术可以分为 Java 层的 Hook 与 Native 层的 Hook。根据代码的运行环境,Java层的 Hook 可以分为 Dalvik Hook 与 ART Hook。根据 ELF 文件的特点,Native层的 Hook 可以分为基于动态库加载劫持的 LD_PRELOAD Hook、针对 .got.plt 节区的 GOT Hook 及针对汇编指令级别的 Inline Hook。
         Hook技术主要分为五类:
                 a. Dalvik Hook;
                 b. ART Hook;
                 c. LOAD_PRELAD Hook;
                 d. GOT Hook;
                 e. Inline Hook;
         已有的Hook框架:
                 a. Xposed(支持Java层的 Dalvik Hook 和 ART Hook,但不支持 Native 层的 Hook)
                 b. legend(支持Java层的 Dalvik Hook 和 ART Hook)
                 e. epic
                 c. whale(跨平台的Hook框架,支持Java层的 Dalvik Hook 和 ART Hook)
         VirtualApp 采用了 Hook技术实现了在Android平台上的沙盒环境(容器),达到可以完全控制 其内运行的App的目的。
     B. 实践
         笔者主要看了 weishu 和  lody 对该技术的研究以及实现,因此主要讲这两位开发者对该问题的研究和实现。
             1) weishu的主要产品是 taichi,其产品发展图如下所示:
            2)lody主要产品是 VirtualApp,非常优秀的一款软件,weishu的 VirtualXpose 也是基于 VirtualApp 来实现的。
                 分析该软件原理的文章非常之多,也很专业,笔者在这里仅列出两篇实践应用的文章,用以实现 基于VirtualApp来hook其他第三方应用。
                 另:笔者通过借鉴以上两篇文章实现了 基于 VirtualApp 并结合 whale hook框架 来hook其他第三方应用的目的,在之后的文章将详述实现过程。感谢。
三、Android虚拟多开需求分析
       多账号同时运行;
   运营工具;
四、反虚拟多开技术
       VirtualApp使恶意应用具有免杀能力;
       VirtualApp给检测引擎识别恶意应用带来难度,因为其母包没有恶意应用,但可动态加载子包。
       相应的解决方案文中有详细描述。

小白也能看懂的插件化DroidPlugin原理(一)-- 动态代理 - codingblock - 博客园

mikel阅读(1259)

来源: 小白也能看懂的插件化DroidPlugin原理(一)– 动态代理 – codingblock – 博客园

  前言:插件化在Android开发中的优点不言而喻,也有很多文章介绍插件化的优势,所以在此不再赘述。前一阵子在项目中用到 DroidPlugin 插件框架 ,近期准备投入生产环境时出现了一些小问题,所以决心花些时间研究了一下 DroidPlugin 插件框架的原理,以便再出现问题时也能从容应对。打开源码后发现尽是大把大把的 hook、binder、classloader 等等,很难摸清头绪,幸运的是,有很多热心的大神已经对 DroidPlugin 的原理进行了透彻的剖析,文末会有本人对参考文章的致谢。

本系列文章的代码已经上传至github,下载地址:https://github.com/lgliuwei/DroidPluginStudy 本篇文章对应的代码在 com.liuwei.proxy_hook.proxy 包内。

· 代理模式

在 DroidPlugin 中用到了大量的动态代理,所以如果我们想理解 DroidPlugin 的原理,首先我们需要知道什么是动态代理,说到动态代理,我们难免会想起静态代理,那么代理是什么呢?

代理模式的意图是通过提供一个代理( Proxy )或者占位符来控制对该对象的访问。类比我们生活中,代理也是随处可见,其中中介就是一个很好的例子,把代理看做生活中的中介,将更加易于理解,试想一下,如果我们想租房或者买房的话通过中间是不是就可以让我们非常省心。

一、静态代理

为了保证与所代理的对象功能行为的一致性,代理类一般需要实现实体类所实现的同一个接口,以下即为一个最基本的代理模式的结构。

首先先定义一个接口,供实体类和代理类实现。(如:接口 Sbuject1 )

复制代码
1 /**
2  * Created by liuwei on 17/3/1.
3  */
4 public interface Subject1 {
5     void method1();
6     void method2();
7 }
复制代码

然后创建一个 Subject1 的实现类。

复制代码
 1 /**
 2  * 实体类
 3  * Created by liuwei on 17/3/1.
 4  */
 5 public class RealSubject1 implements Subject1 {
 6     @Override
 7     public void method1() {
 8         Logger.i(RealSubject1.class, "我是RealSubject1的方法1");
 9     }
10     @Override
11     public void method2() {
12         Logger.i(RealSubject1.class, "我是RealSubject1的方法2");
13     }
14 }
复制代码

再为 RealSubject1 创建一个代理类。

复制代码
 1 /**
 2  * 静态代理类
 3  * Created by liuwei on 17/3/1.
 4  */
 5 public class ProxySubject1 implements Subject1 {
 6     private Subject1 subject1;
 7     public ProxySubject1(Subject1 subject1) {
 8         this.subject1 = subject1;
 9     }
10     @Override
11     public void method1() {
12         Logger.i(ProxySubject1.class, "我是代理,我会在执行实体方法1之前先做一些预处理的工作");
13         subject1.method1();
14     }
15     @Override
16     public void method2() {
17         Logger.i(ProxySubject1.class, "我是代理,我会在执行实体方法2之前先做一些预处理的工作");
18         subject1.method2();
19     }
20 }
复制代码

可以发现,代理模式还是很简单的,很快我们就写好一个最基本的代理结构,接下来写个测试类跑一下看看效果。

复制代码
 1 /**
 2  * Created by liuwei on 17/3/1.
 3  */
 4 public class ProxyTest {
 5     public static void main(String[] args){
 6         // static proxy
 7         ProxySubject1 proxySubject1 = new ProxySubject1(new RealSubject1());
 8         proxySubject1.method1();
 9         proxySubject1.method2();
10 }
复制代码

输出结果非常简单,这里就不再贴出来了。我们看到,在测试类中只需要调用 ProxySubject1 的对像即可对实现对 RealSubject1 的操作。同时我们也发现在初始化 ProxySubject1 时需要传入 RealSubject1 的对象,当然,我们完全可以把获取 RealSubject1 的对象封装到代理类内部,这只是代理模式根据业务需要的不同体现而已。有很多人把这一点作为区分代理模式和适配器模式的依据,这个是不对的,由于本篇的重点是为插件化的原理做铺垫,至于代理模式和适配器模式的区别日后会专门写一篇文章介绍,这里就不细说了。

其实,从这个简单的示例中也许并没有体现出代理模式的优势,而且还要多创建一个代理类,反而看起来好像更麻烦了。其实代理模式很明显的好处就是通过代理,可以控制对实体对象的访问,从而提高了安全性。而且可以在调用实体类的方法时做一些预处理和善后的工作,这样就保证了实体类可以抛开复杂的业务逻辑而只去实现一些最纯粹的功能,提高了代码的可读性和灵活性。

二、动态代理

动态代理是本文的重点,也是 DroidPlugin 插件化框架的基础。动态代理乍一听起来好像也挺高大上的,但幸运的是,它并没有我们想象中那么高深莫测,所以我们大可不必对它有任何的畏惧之感。

假设我们在上文静态代理的例子中又多了一个 RealSubject2 的类,它实现的接口是 Subject2,这时候我们如果想对 RealSubject2 进行代理需要如何做?这个简单,我们直接类比 ProxySubject1 再创建一个 ProxySubject2 即可,这样是可以的,但如果有非常多的实体类并且都实现了不同的接口,那我们岂不是需要创建很多的代理类:ProxySubject1,ProxySubject2 … ProxySubjectN!还有没有更优雅一些的方法?答案是肯定的,动态代理即可解决这个问题。(当然,这并不是动态代理唯一的优点)

动态代理是在实现阶段不需要关心代理谁,在运行阶段才指定代理对象。创建一个动态代理类很简单,JDK已经给我们提供好了动态代理接口  InvocationHandler 我们只需要实现它即可创建一个动态代理类,以下是一个简单的小例子:

复制代码
 1 /**
 2  * 动态代理
 3  * Created by liuwei on 17/3/1.
 4  * 注:动态代理的步骤:
 5  *  1、写一个InvocationHandler的实现类,并实现invoke方法,return method.invoke(...);
 6  *  2、使用Proxy类的newProxyInstance方法生成一个代理对象。例如:生成Subject1的代理对象,注意第三个参数中要将一个实体对象传入
 7  *          Proxy.newProxyInstance(
 8                          Subject1.class.getClassLoader(),
 9                          new Class[] {Subject1.class},
10                          new DynamicProxyHandler(new RealSubject1()));
11 
12  */
13 public class DynamicProxyHandler implements InvocationHandler {
14     private Object object;
15 
16     public DynamicProxyHandler(Object object) {
17         this.object = object;
18     }
19 
20     @Override
21     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
22         Logger.i(DynamicProxyHandler.class, "我正在动态代理[" + object.getClass().getSimpleName() + "]的[" + method.getName() + "]方法");
23         return method.invoke(object, args);
24     }
25 
26     /**
27      * 调用Proxy.newProxyInstance即可生成一个代理对象
28      * @param object
29      * @return
30      */
31     public static Object newProxyInstance(Object object) {
32         // 传入被代理对象的classloader,实现的接口,还有DynamicProxyHandler的对象即可。
33         return Proxy.newProxyInstance(object.getClass().getClassLoader(),
34                 object.getClass().getInterfaces(),
35                 new DynamicProxyHandler(object));
36     }
37 }
复制代码

这是一个名为 DynamicProxyHandler 的动态代理类,其中 invoke 方法完成了对代理对象方法的调用,是必须实现的。接下来使用此类代理其他的实体类也非常简单,只需使用 Proxy 的newProxyInstance() 方法并传入相应的参数即可获取一个代理对象,接下来我们在测试类里面添加一下代码,代码如下:

复制代码
 1 /**
 2  * Created by liuwei on 17/3/1.
 3  */
 4 public class ProxyTest {
 5     public static void main(String[] args){
 6         // static proxy
 7         ProxySubject1 proxySubject1 = new ProxySubject1(new RealSubject1());
 8         proxySubject1.method1();
 9         proxySubject1.method2();
10 
11         // 如果想对RealSubject2代理显然不得不重新再写一个代理类。
12         ProxySubject2 proxySubject2 = new ProxySubject2(new RealSubject2());
13         proxySubject2.method1();
14         proxySubject2.method2();
15 
16         Logger.i(ProxyTest.class, "----------分割线----------\n");
17 
18         // 如果写一个代理类就能对上面两个都能代理就好了,动态代理就解决了这个问题
19         Subject1 dynamicProxyHandler1 = (Subject1) DynamicProxyHandler.newProxyInstance(new RealSubject1());
20         dynamicProxyHandler1.method1();
21         dynamicProxyHandler1.method2();
22 
23         Subject2 dynamicProxyHandler2 = (Subject2)DynamicProxyHandler.newProxyInstance(new RealSubject2());
24         dynamicProxyHandler2.method1();
25         dynamicProxyHandler2.method2();
26     }
27 }
复制代码

输出结果非常简单,这里不再给出。

三、小结

至此,相信我们对动态代理已经有一个基本的认识,其实代理模式除了上文中提到的普通代理(静态代理的一种)、动态代理之外还有很多种方式,如远程代理、虚拟代理、智能代理等等,这里就不一一介绍了。

其实插件化的原理简单来说是使用动态代理,通过反射等机制将系统中的一些方法hook掉,从而达到劫持系统方法的目的以实现对系统方法的篡改。例如通过 hook 掉 AMS 的 startActivity 方法来启动一个没有在清单文件中配置的 Activity 。下一篇文章将详细介绍 Hook 机制,以及反射在 Hook 中的实际体现。

  致谢:最后我想说的是“吃水不忘挖井人”!非常感谢术哥《Android插件化原理解析——概要》系列文章,本人正是在参考了这些内容的思路之后才有能力写下本系列文章。本人在Android的插件化领域可以说算是一个小白,写下本系列文章的目的一方面是在实践中加深自己的理解,另一方面是根据本人以小白角度对插件化原理的体会用更加简单易懂的方式传达出来,从而帮助小白也能读懂插件化!

本文链接:http://www.cnblogs.com/codingblock/p/6580364.html

作者:CodingBlock
出处:http://www.cnblogs.com/codingblock/
本文版权归作者和共博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
如果文中有什么错误,欢迎指出。以免更多的人被误导。

【SqlServer】SqlServer中的更新锁(UPDLOCK)_数据库_weixin_34049032的博客-CSDN博客

mikel阅读(685)

来源: 【SqlServer】SqlServer中的更新锁(UPDLOCK)_数据库_weixin_34049032的博客-CSDN博客

UPDLOCK.UPDLOCK 的优点是允许您读取数据(不阻塞其它事务)并在以后更新数据,同时确保自从上次读取数据后数据没有被更改。当我们用UPDLOCK来读取记录时可以对取到的记录加上更新锁,从而加上锁的记录在其它的线程中是不能更改的只能等本线程的事务结束后才能更改.
测试:
在另一个查询里:

BEGIN TRANSACTION
SELECT * FROM myTable WITH (UPDLOCK) WHERE Id in (1,2,3)
waitfor delay ’00:00:10′
update myTable set [Name]=’ZZ’ where Id in (1,2,3)
commit TRANSACTION

在另一个查询里:
SELECT * FROM myTable WHERE Id in (1,2,3)

可以马上查询到数据。
但如果要更新数据,必须等其他更新锁释放后才能执行。

update myTable set [Name]=’ZZ’ where Id in (1,2,3)

这就说明,有时候需要控制某条记录在我读取后就不许再进行更新,那么我就可以将所有要处理当前记录的查询都加上更新锁,以防止查询后被其它事务修改.将事务的影响降低到最小

如果不使用这个锁的话,在项目中很有可能出现“事务(进程 ID )与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行”错误。
例如:
表现二:
用户A读一条纪录,然后修改该条纪录
这是用户B修改该条纪录
这里用户A的事务里锁的性质由共享锁企图上升到独占锁(for update),而用户B里的独占锁由于A有共享锁存在所以必须等A释放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。
这种死锁比较隐蔽,但其实在稍大点的项目中经常发生。
解决方法:
让用户A的事务(即先读后写类型的操作),在select 时就是用Update lock
语法如下:
select * from table1 with(updlock) where ….

SqlServer中的更新锁(UPDLOCK) - 假面Wilson - 博客园

mikel阅读(847)

来源: SqlServer中的更新锁(UPDLOCK) – 假面Wilson – 博客园

UPDLOCK.UPDLOCK 的优点是允许您读取数据(不阻塞其它事务)并在以后更新数据,同时确保自从上次读取数据后数据没有被更改。当我们用UPDLOCK来读取记录时可以对取到的记录加上更新锁,从而加上锁的记录在其它的线程中是不能更改的只能等本线程的事务结束后才能更改.

示例:

测试:

在另一个查询里:

BEGIN TRANSACTION

SELECT * FROM myTable WITH (UPDLOCK) WHERE Id in (1,2,3)

waitfor delay ’00:00:10′

update myTable set [Name]=’ZZ’ where Id in (1,2,3)
commit TRANSACTION

在另一个查询里:
SELECT * FROM myTable WHERE Id in (1,2,3)

可以马上查询到数据。

但如果要更新数据,必须等其他更新锁释放后才能执行。

update myTable set [Name]=’ZZ’ where Id in (1,2,3)

这就说明,有时候需要控制某条记录在我读取后就不许再进行更新,那么我就可以将所有要处理当前记录的查询都加上更新锁,以防止查询后被其它事务修改.将事务的影响降低到最小

SQLServer实现split分割字符串到列 - 人海灬 - 博客园

mikel阅读(3544)

来源: SQLServer实现split分割字符串到列 – 人海灬 – 博客园

网上已有人实现SQLServer的split函数可将字符串分割成行,但是我们习惯了split返回数组或者列表,因此这里对其做一些改动,最终实现也许不尽如意,但是也能解决一些问题。

先贴上某大牛写的split函数(来自:Split function in SQL Server to break Comma separated strings,注意我这里将其命名为splitl):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ALTER FUNCTION dbo.splitl (
    @String VARCHAR(MAX),
    @Delimiter VARCHAR(MAX)
) RETURNS @temptable TABLE (items VARCHAR(MAX)) AS
BEGIN
    DECLARE @idx INT=1
    DECLARE @slice VARCHAR(MAX)
    IF LEN(@String) < 1 OR LEN(ISNULL(@String,'')) = 0
        RETURN
    WHILE @idx != 0
    BEGIN
        SET @idx = CHARINDEX(@Delimiter,@String)
        IF @idx != 0
            SET @slice = LEFT(@String,@idx - 1)
        ELSE
            SET @slice = @String
        IF LEN(@slice) > 0
            INSERT INTO @temptable(items) VALUES(@slice)
        SET @String = RIGHT (@String, LEN(@String) - @idx)
        IF LEN(@String) = 0
            BREAK
    END
    RETURN
END

其原理还是比较简单的,一看便知。调用该函数返回的结果是:

1
SELECT FROM dbo.splitl('a#b#C#d','#')

然而我希望得到的结果是:

1
SELECT 'a' a,'b' b,'c' c,'d' d

这就要用到SQLServer行转列的技巧,网上有很多方法可以参照。下面真正的split“过程”来了:

1
2
3
4
5
6
7
ALTER PROC [dbo].[split] @strs VARCHAR(MAX),@delimiter VARCHAR(MAXAS
SELECT items,id=IDENTITY(INT,1,1) INTO #ccc FROM dbo.splitl(@strs,@delimiter)
DECLARE @str VARCHAR(MAX)='',@SQL VARCHAR(MAX)=''
SELECT @str = @str + ',' '[' CONVERT(VARCHAR(MAX),id) + ']' FROM #ccc
SET @SQL = 'SELECT * FROM #ccc PIVOT(MAX(items) FOR id IN(' SUBSTRING(@str,2,LEN(@str)) + ')) b'
EXEC (@SQL)
DROP TABLE #ccc

该过程中使用了pivot语法,参见:使用 PIVOT 和 UNPIVOT

注意这个过程调用了splitl函数,是在其基础上开发的。我们再来看看执行结果:

1
EXEC dbo.split 'a#b#C#d','#'

发现与上面期望的效果完全一致了!

但是这只是针对一行数据做split,如果是查询结果有多行都要分割怎么办呢?

我没有找到办法,因为SQLServer查询语句中不能嵌套过程,只能调用函数,而函数返回的结果集不能是多行。

but..世上无难事,只要写过程:

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
38
39
40
-- 删除结果表
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id=object_id(N'test_result') AND OBJECTPROPERTY(id, N'IsUserTable')=1)
    DROP TABLE test_result
-- 建立数据表
CREATE TABLE #tmp (
    id INT NOT NULL IDENTITY(0,1),
    str VARCHAR(MAX)
)
INSERT INTO #tmp SELECT 'a#b#c#d' UNION SELECT 'f#g#h'
-- 生成结果表
DECLARE @maxc INT=(SELECT MAX(LEN(str)-LEN(REPLACE(str,'#','')))+1 FROM #tmp)
DECLARE @sql0 VARCHAR(MAX)='CREATE TABLE test_result ('
DECLARE @x INT=0
WHILE @x<@maxc BEGIN
    SET @sql0 = @sql0 + 'a' + CONVERT(VARCHAR(MAX),@x) + ' VARCHAR(MAX),'
    SET @x=@x+1
END
SET @sql0 = SUBSTRING(@sql0,0,LEN(@sql0)) + ')'
EXEC (@sql0)
-- 遍历数据表
DECLARE @i INT=0
WHILE @i<(SELECT COUNT(1) FROM #tmp) BEGIN
    DECLARE @strs VARCHAR(MAX)=(SELECT str FROM #tmp WHERE id=@i)
    DECLARE @cols INT=(SELECT LEN(@strs)-LEN(REPLACE(@strs,'#','')))+1
    DECLARE @y INT=0
    DECLARE @sql1 VARCHAR(MAX)='INSERT INTO test_result('
    WHILE @y<@cols BEGIN
        SET @sql1 = @sql1 + 'a' + CONVERT(VARCHAR(MAX),@y) + ','
        SET @y=@y+1
    END
-- -- 分割字符串
    SET @sql1 = SUBSTRING(@sql1,0,LEN(@sql1)) + ') EXEC split "' + @strs + '","#"'
    EXEC (@sql1)
    SET @i=@i+1
END
SELECT * FROM test_result

暂时就到此为止八~sqlserver毕竟不够完美,这样的函数系统提供能够最好,自己实现的话遇到太多瓶颈,比如函数不支持动态语句,不能将查询结果传入过程等等。

至于实际应用,将上面这个栗子建立临时数据表的部分替换成要查询的真实表列即可,最后结果如下所示:

解决浏览器中点击【Backspace】回退问题_网络_默默前行,勿喜、勿悲、一切随缘!-CSDN博客

mikel阅读(1279)

来源: 解决浏览器中点击【Backspace】回退问题_网络_默默前行,勿喜、勿悲、一切随缘!-CSDN博客

问题:

工作中遇到在浏览器空白处,或者不可编辑的input框上,点击【Backspace】按键,出现浏览器页面回退的问题,经过测试,发现谷歌浏览器默认屏蔽了这个回退的功能,但IE、360浏览器、火狐浏览器都没有,这个功能会导致,特别是后台系统,session丢失,退回到登录页面,严重影响用户体验。
比如,用户在进行表单的信息填写,不经意在浏览器空白处点击了【Backspace】按键,退到了登录界面,想想这是个什么样的体验。

解决方法:

通过js监听backspace按键的按下事件:

1、如果标签不是input或者textarea则阻止Backspace
2、input标签除了(TEXT、TEXTAREA、PASSWORD)这些类型,全部阻止Backspace
3、input或者textarea输入框如果不可编辑则阻止Backspace

代码如下:

function banBackSpace(e) {
var ev = e || window.event;
//各种浏览器下获取事件对象
var obj = ev.relatedTarget || ev.srcElement || ev.target || ev.currentTarget;
//按下Backspace键
if (ev.keyCode == 8) {
var tagName = obj.nodeName //标签名称
//如果标签不是input或者textarea则阻止Backspace
if (tagName != ‘INPUT’ && tagName != ‘TEXTAREA’) {
return stopIt(ev);
}
var tagType = obj.type.toUpperCase();//标签类型
//input标签除了下面几种类型,全部阻止Backspace
if (tagName == ‘INPUT’ && (tagType != ‘TEXT’ && tagType != ‘TEXTAREA’ && tagType != ‘PASSWORD’)) {
return stopIt(ev);
}
//input或者textarea输入框如果不可编辑则阻止Backspace
if ((tagName == ‘INPUT’ || tagName == ‘TEXTAREA’) && (obj.readOnly == true || obj.disabled == true)) {
return stopIt(ev);
}
}
}
function stopIt(ev) {
if (ev.preventDefault) {
//preventDefault()方法阻止元素发生默认的行为
ev.preventDefault();
}
if (ev.returnValue) {
//IE浏览器下用window.event.returnValue = false;实现阻止元素发生默认的行为
ev.returnValue = false;
}
return false;
}

$(function() {
//实现对字符码的截获,keypress中屏蔽了这些功能按键
document.onkeypress = banBackSpace;
//对功能按键的获取
document.onkeydown = banBackSpace;
})
上述代码可以放到公共的js中,此处的代码参照此篇博客:
http://www.cnblogs.com/lujiulong/p/6019638.html

在此说明几点:
1、  IE:有window.event对象
Firefox:没有window.event对象。可以通过给函数的参数传递event对象。如οnmοusemοve=doMouseMove(event)
统一的解决方法:var event = event || window.event;

2、  IE:even对象有srcElement属性,但是没有target属性
Firefox:even对象有target属性,但是没有srcElement属性
解决方法:var obj = event.relatedTarget || event.srcElement || event.target || event.currentTarget;
————————————————
版权声明:本文为CSDN博主「晓梦_知行」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/csdn_ds/article/details/73337929

SQL Server 中WITH (NOLOCK)浅析 - 潇湘隐者 - 博客园

mikel阅读(622)

来源: SQL Server 中WITH (NOLOCK)浅析 – 潇湘隐者 – 博客园

概念介绍

  

开发人员喜欢在SQL脚本中使用WITH(NOLOCK), WITH(NOLOCK)其实是表提示(table_hint)中的一种。它等同于 READUNCOMMITTED 。 具体的功能作用如下所示(摘自MSDN):

   1: 指定允许脏读。不发布共享锁来阻止其他事务修改当前事务读取的数据,其他事务设置的排他锁不会阻碍当前事务读取锁定数据。允许脏读可能产生较多的并发操作,但其代价是读取以后会被其他事务回滚的数据修改。这可能会使您的事务出错,向用户显示从未提交过的数据,或者导致用户两次看到记录(或根本看不到记录)。有关脏读、不可重复读和幻读的详细信息,请参阅并发影响

   2: READUNCOMMITTED 和 NOLOCK 提示仅适用于数据锁。所有查询(包括那些带有 READUNCOMMITTED 和 NOLOCK 提示的查询)都会在编译和执行过程中获取 Sch-S(架构稳定性)锁。因此,当并发事务持有表的 Sch-M(架构修改)锁时,将阻塞查询。例如,数据定义语言 (DDL) 操作在修改表的架构信息之前获取 Sch-M 锁。所有并发查询(包括那些使用 READUNCOMMITTED 或 NOLOCK 提示运行的查询)都会在尝试获取 Sch-S 锁时被阻塞。相反,持有 Sch-S 锁的查询将阻塞尝试获取 Sch-M 锁的并发事务。有关锁行为的详细信息,请参阅锁兼容性(数据库引擎)

   3:  不能为通过插入、更新或删除操作修改过的表指定 READUNCOMMITTED 和 NOLOCK。SQL Server 查询优化器忽略 FROM 子句中应用于 UPDATE 或 DELETE 语句的目标表的 READUNCOMMITTED 和 NOLOCK 提示。

功能与缺陷

 

    使用WIHT(NOLOCK)有利也有弊,所以在决定使用之前,你一定需要了解清楚WITH(NOLOCK)的功能和缺陷,看其是否适合你的业务需求,不要觉得它能提升性能,稀里糊涂的就使用它。

 

    1:使用WITH(NOLOCK)时查询不受其它排他锁阻塞

    打开会话窗口1,执行下面脚本,不提交也不回滚事务,模拟事务真在执行过程当中

BEGIN TRAN

       UPDATE TEST SET NAME='Timmy' WHERE OBJECT_ID =1;

       --ROLLBACK

   

   打开会话窗口2,执行下面脚本,你会发现执行结果一直查询不出来(其实才两条记录)。当前会话被阻塞了

SELECT * FROM TEST;

    打开会话窗口3,执行下面脚本,查看阻塞情况,你会发现在会话2被会话1给阻塞了,会话2的等待类型为LCK_M_S:“当某任务正在等待获取共享锁时出现”



  SELECT wt.blocking_session_id                    AS BlockingSessesionId
        ,sp.program_name                           AS ProgramName
        ,COALESCE(sp.LOGINAME, sp.nt_username)     AS HostName
        ,ec1.client_net_address                    AS ClientIpAddress
        ,db.name                                   AS DatabaseName
        ,wt.wait_type                              AS WaitType
        ,ec1.connect_time                          AS BlockingStartTime
        ,wt.WAIT_DURATION_MS/1000                  AS WaitDuration
        ,ec1.session_id                            AS BlockedSessionId
        ,h1.TEXT                                   AS BlockedSQLText
        ,h2.TEXT                                   AS BlockingSQLText
  FROM sys.dm_tran_locks AS tl
  INNER JOIN sys.databases db
    ON db.database_id = tl.resource_database_id
  INNER JOIN sys.dm_os_waiting_tasks AS wt
    ON tl.lock_owner_address = wt.resource_address
  INNER JOIN sys.dm_exec_connections ec1
    ON ec1.session_id = tl.request_session_id
  INNER JOIN sys.dm_exec_connections ec2
    ON ec2.session_id = wt.blocking_session_id
  LEFT OUTER JOIN master.dbo.sysprocesses sp
    ON SP.spid = wt.blocking_session_id
  CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1
  CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2

 

 

clipboard

 

此时查看会话1(会话1的会话ID为53,执行脚本1前,可以用SELECT  @@spid查看会话ID)的锁信息情况,你会发现表TEST(ObjId=1893581784)持有的锁信息如下所示

 

clipboard[1]

   

打开会话窗口4,执行下面脚本.你会发现查询结果很快就出来,会话4并不会被会话1阻塞。

    SELECT * FROM TEST WITH(NOLOCK)

从上面模拟的这个小例子可以看出,正是由于加上WITH(NOLOCK)提示后,会话1中事务设置的排他锁不会阻碍当前事务读取锁定数据,所以会话4不会被阻塞,从而提升并发时查询性能。

 

2:WITH(NOLOCK) 不发布共享锁来阻止其他事务修改当前事务读取的数据,这个就不举例子了。

本质上WITH(NOLOCK)是通过减少锁和不受排它锁影响来减少阻塞,从而提高并发时的性能。所谓凡事有利也有弊,WITH(NOLOCK)在提升性能的同时,也会产生脏读现象。

如下所示,表TEST有两条记录,我准备更新OBJECT_ID=1的记录,此时事务既没有提交也没有回滚

clipboard[2]

BEGIN TRAN

UPDATE TEST SET NAME='Timmy' WHERE OBJECT_ID =1;

--ROLLBACK

此时另外一个会话使用WITH(NOLOCK)查到的记录为未提交的记录值

clipboard[3]

假如由于某种原因,该事务回滚了,那么我们读取到的OBJECT_ID=1的记录就是一条脏数据。

脏读又称无效数据的读出,是指在数据库访问中,事务T1将某一值修改,然后事务T2读取该值,此后T1因为某种原因撤销对该值的修改,这就导致了T2所读取到的数据是无效的。

 

WITH(NOLOCK)使用场景

 

什么时候可以使用WITH(NOLOCK)? 什么时候不能使用WITH(NOLOCK),这个要视你系统业务情况,综合考虑性能情况与业务要求来决定是否使用WITH(NOLOCK), 例如涉及到金融或会计成本之类的系统,出现脏读那是要产生严重问题的。关键业务系统也要慎重考虑。大体来说一般有下面一些场景可以使用WITH(NOLOCK)

   1: 基础数据表,这些表的数据很少变更。

   2:历史数据表,这些表的数据很少变更。

   3:业务允许脏读情况出现涉及的表。

   4:数据量超大的表,出于性能考虑,而允许脏读。

另外一点就是不要滥用WITH(NOLOCK),我发现有个奇怪现象,很多开发知道WITH(NOLOCK),但是有不了解脏读,习惯性的使用WITH(NOLOCK)。

 

WITH(NOLOCK)与 NOLOCK区别

 

为了搞清楚WITH(NOLOCK)与NOLOCK的区别,我查了大量的资料,我们先看看下面三个SQL语句有啥区别

    SELECT * FROM TEST NOLOCK

    SELECT * FROM TEST (NOLOCK);

    SELECT * FROM TEST WITH(NOLOCK);

上面的问题概括起来也就是说NOLOCK、(NOLOCK)、 WITH(NOLOCK)的区别:

1: NOLOCK这样的写法,其实NOLOCK其实只是别名的作用,而没有任何实质作用。所以不要粗心将(NOLOCK)写成NOLOCK

2:(NOLOCK)与WITH(NOLOCK)其实功能上是一样的。(NOLOCK)只是WITH(NOLOCK)的别名,但是在SQL Server 2008及以后版本中,(NOLOCK)不推荐使用了,”不借助 WITH 关键字指定表提示”的写法已经过时了。 具体参见MSDN http://msdn.microsoft.com/zh-cn/library/ms143729%28SQL.100%29.aspx

    2.1  至于网上说WITH(NOLOCK)在SQL SERVER 2000不生效,我验证后发现完全是个谬论。

    2.2  在使用链接服务器的SQL当中,(NOLOCK)不会生效,WITH(NOLOCK)才会生效。如下所示

clipboard[4]

    消息 4122,级别 16,状态 1,第 1 行

    Remote table-valued function calls are not allowed.

 

3.语法上有些许出入,如下所示

这种语法会报错
SELECT  * FROM   sys.indexes  WITH(NOLOCK) AS i
-Msg 156, Level 15, State 1, Line 1
-Incorrect syntax near the keyword 'AS'.

这种语法正常
SELECT  * FROM   sys.indexes  (NOLOCK) AS i

可以全部改写为下面语法

SELECT  * FROM   sys.indexes   i WITH(NOLOCK)


SELECT  * FROM   sys.indexes   i (NOLOCK)

 

WITH(NOLOCK)会不会产生锁

    很多人误以为使用了WITH(NOLOCK)后,数据库库不会产生任何锁。实质上,使用了WITH(NOLOCK)后,数据库依然对该表对象生成Sch-S(架构稳定性)锁以及DB类型的共享锁, 如下所示,可以在一个会话中查询一个大表,然后在另外一个会话中查看锁信息(也可以使用SQL Profile查看会话锁信息)

    不使用WTIH(NOLOCK)

clipboard[5]

  使用WITH(NOLOCK)

clipboard[6]

  从上可以看出使用WITH(NOLOCK)后,数据库并不是不生成相关锁。  对比可以发现使用WITH(NOLOCK)后,数据库只会生成DB类型的共享锁、以及TAB类型的架构稳定性锁.

另外,使用WITH(NOLOCK)并不是说就不会被其它会话阻塞,依然可能会产生Schema Change Blocking

会话1:执行下面SQL语句,暂时不提交,模拟事务正在执行

BEGIN TRAN

  ALTER TABLE TEST ADD Grade VARCHAR(10) ;

会话2:执行下面语句,你会发现会话被阻塞,截图如下所示。

SELECT * FROM TEST WITH(NOLOCK)

image

(1条消息)mysql-bin.000001文件的来源及处理方法_数据库_yushaolong1234的博客-CSDN博客

mikel阅读(550)

来源: (1条消息)mysql-bin.000001文件的来源及处理方法_数据库_yushaolong1234的博客-CSDN博客

用ports安装了MySQL以后,过一段时间发现/var空间不足了,查一下,会发现是mySQL-bin.000001、mySQL-bin.000002等文件占用了空间,那么这些文件是干吗的?这是数据库的操作日志,例如UPDATE一个表,或者DELETE一些数据,即使该语句没有匹配的数据,这个命令也会存储到日志文件中,还包括每个语句执行的时间,也会记录进去的。

这样做主要有以下两个目的:
1:数据恢复
如果你的数据库出问题了,而你之前有过备份,那么可以看日志文件,找出是哪个命令导致你的数据库出问题了,想办法挽回损失。
2:主从服务器之间同步数据
主服务器上所有的操作都在记录日志中,从服务器可以根据该日志来进行,以确保两个同步。

处理方法分两种情况:
1:只有一个mysql服务器,那么可以简单的注释掉这个选项就行了。
vi /etc/my.cnf把里面的log-bin这一行注释掉,重启mysql服务即可。
2:如果你的环境是主从服务器,那么就需要做以下操作了。
A:在每个从属服务器上,使用SHOW SLAVE STATUS来检查它正在读取哪个日志。
B:使用SHOW MASTER LOGS获得主服务器上的一系列日志。
C:在所有的从属服务器中判定最早的日志,这个是目标日志,如果所有的从属服务器是更新的,就是清单上的最后一个日志。
D:清理所有的日志,但是不包括目标日志,因为从服务器还要跟它同步。
清理日志方法为:
PURGE MASTER LOGS TO ‘mysql-bin.010’;
PURGE MASTER LOGS BEFORE ‘2008-12-19 21:00:00’;

如果你确定从服务器已经同步过了,跟主服务器一样了,那么可以直接RESET MASTER将这些文件删除。

 

======================================

 

之前发现自己10G的服务器空间大小,用了几天就剩下5G了,自己上传的文件才仅仅几百M而已,到底是什么东西占用了这么大空间呢?今天有时间彻底来查了一下:

 

看下上面的目录web根目录是放在/home 里面的,所有文件加起来才不到300M,而服务器上已经占用了近5G空间,恐怖吧,最后经我一步一步查询得知,原来是这个文件夹占了非常多的空间资源:

 

原来如此,是mysql文件夹下的var目录占用空间最大,那里面是啥 内容呢?我们来看下:

 

发现了如此多的 mysql-bin.0000X文件,这是什么东西呢?原来这是mysql的操作日志文件.我才几十M的数据库,操作日志居然快3G大小了.

如何删除mysql-bin.0000X 日志文件呢?

红色表示输入的命令.

[root@jiucool var]# /usr/local/mysql/bin/mysql -u root -p
Enter password:  (输入密码)
Welcome to the MySQL monitor.  Commands end with ; or /g.
Your MySQL connection id is 264001
Server version: 5.1.35-log Source distribution

Type ‘help;’ or ‘/h’ for help. Type ‘/c’ to clear the current input statement.

mysql> reset master; (清除日志文件)
Query OK, 0 rows affected (8.51 sec)

mysql>

好了,我们再来查看下mysql文件夹占用多少空间?

[root@jiucool var]# du -h –max-depth=1 /usr/local/mysql/
37M     /usr/local/mysql/var
70M     /usr/local/mysql/mysql-test
15M     /usr/local/mysql/lib
448K    /usr/local/mysql/include
2.9M    /usr/local/mysql/share
7.6M    /usr/local/mysql/libexec
17M     /usr/local/mysql/bin
11M     /usr/local/mysql/docs
2.9M    /usr/local/mysql/sql-bench
163M    /usr/local/mysql/

好了,看一下,整个mysql 目录才占用163M大小!OK,没问题,既然mysql-bin.0000X日志文件占用这么大空间,存在的意义又不是特别大,那么我们就不让它生成吧.

[root@jiucool var]# find / -name my.cnf

找到了my.cnf 即mysql配置文件,我们将log-bin=mysql-bin 这条注释掉即可.

# Replication Master Server (default)
# binary logging is required for replication
#log-bin=mysql-bin

重启下mysql吧.

OK,至此,操作完成. 以后再不会因为就几十M的数据库大小生成N个G的日志文件啦.

这些个日志文件太恐怖了,我搬到这新VPS来才二十天左右,还不到一个月日志文件居然就近3个G大小,如果一两个月我不清除日志文件这还得了!

Sql Server 判断表是否存在方法总结 - willingtolove - 博客园

mikel阅读(808)

来源: Sql Server 判断表是否存在方法总结 – willingtolove – 博客园

 

正文

#使用场景:

1、在创建表之前,需要先判断该表是否已经存在;

2、在删除表之前,需要先判断该表是否已经存在;

#方法总结:

1、判断实体表是否存在的方法:

1)、方法一:

if Exists(select top 1 * from sysObjects where Id=OBJECT_ID(N'UserInfos') and xtype='U')
    print '表UserInfos 存在'
else 
    print '表UserInfos 不存在'

2)、方法二:

if OBJECT_ID(N'UserInfos',N'U') is not null
    print '表UserInfos 存在!'
else 
    print '表UserInfos 不存在!'

2、判断临时表是否存在的方法:

1)、方法一:

if exists (select * from tempdb.dbo.sysobjects where id = object_id(N'tempdb..#TempUsers') and type='U')
    print '临时表#TempUsers 存在!'
else 
    print '临时表#TempUsers 不存在!'

2)、方法二:

if OBJECT_ID(N'tempdb..#TempUsers',N'U') is not null
    print '临时表#TempUsers 存在!'
else 
    print '临时表#TempUsers 不存在!'