mysql select into的用法_zwldx的博客-CSDN博客

mikel阅读(1045)

来源: mysql select into的用法_zwldx的博客-CSDN博客

MySQL不支持Select Into语句直接备份表结构和数据,一些种方法可以代替,如下:

#MYSQL不支持:
Select * Into new_table_name from old_table_name; 这是SQL server中的用法
#替代方法:
Create table new_table_name (Select * from old_table_name);

————————————————
版权声明:本文为CSDN博主「zwldx」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zwldx/article/details/82227533

Lock wait timeout exceeded; try restarting transaction_萌神30号 ↝-CSDN博客

mikel阅读(882)

来源: Lock wait timeout exceeded; try restarting transaction_萌神30号 ↝-CSDN博客

目录

一、问题抛出

二、解决方案

三、三张表字段说明

innodb_trx

innodb_locks

innodb_lock_waits

四、终极方法

小结


一、问题抛出

在做查询语句时,MySQL 抛出了这样的异常:

  1. MySQL server error report:Array
  2. (
  3. [0] => Array
  4. (
  5. [message] => MySQL Query Error
  6. )
  7. [1] => Array
  8. (
  9. => SELECT * FROM taobao_trade WHERE order_status = 1 and orderID =‘2018061812306547’ AND is_tran_success=0 for update
  10. )
  11. [2] => Array
  12. (
  13. [error] => Lock wait timeout exceeded; try restarting transaction
  14. )
  15. [3] => Array
  16. (
  17. [errno] => 1205
  18. )
  19. )

Lock wait timeout exceeded; try restarting transaction的异常,错误提示的意思,很明显,是因为这条语句被锁住了,所以释放这个锁。

二、解决方案

我们可以通过到information_schema 中来进行查找被锁的语句。

解释: information_schema这张数据表保存了MySQL服务器所有数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权限等。再简单点,这台MySQL服务器上,到底有哪些数据库、各个数据库有哪些表,每张表的字段类型是什么,各个数据库要什么权限才能访问,等等信息都保存在information_schema表里面。

我们可以用下面三张表来查原因:

  • innodb_trx 当前运行的所有事务
  • innodb_locks 当前出现的锁
  • innodb_lock_waits 锁等待的对应关系

如果数据库中有锁的话,我们可以使用这条语句来查看:

select * from information_schema.innodb_trx

clipboard.png

图中红色语句 LOCK WAIT为占用系统资源的语句,我们需要杀掉这个锁,执行 kill 线程id号。上面这条记录的id为199120823069, trx_mysql_thread_id 为 738178711, 所以我们执行:kill 738178711杀掉这个MySQL语句的线程即可。

执行之后:

  1. kill 738178711
  2. // 查询线程
  3. // SELECT * from information_schema.processlist WHERE id = 738178711;
  4. // show full processlist;

clipboard.png

其他的记录不需要关注,因为其他的记录状态为“RUNNING” 即正在执行的事务,并没有锁。

三、三张表字段说明

innodb_trx

desc information_schema.innodb_trx;

clipboard.png

innodb_locks

desc information_schema.innodb_locks;

clipboard.png

innodb_lock_waits

desc information_schema.innodb_lock_waits

clipboard.png

四、终极方法

如果以上方法杀掉线程,但还是不能解决,则我们就可以查找执行线程用时比较久的用户,然后直接干掉。

  1. SELECT * from information_schema.`PROCESSLIST` WHERE Time > 1000 AND USER = ‘wonguser’ ORDER BY TIME desc;
  2. kill 740097562

这样把所有耗时比较久的任务干掉,就可以解决这个问题了。

小结

关于我的那个问题,我通过这个方法 select * from information_schema.innodb_trx 已经杀掉了线程,但通过表直接修改那个id对应的数据,还是会弹出Lock wait timeout exceeded; try restarting transaction这样的异常,在网上找了许多未找出具体的解决方法,后来自己灵光一现,可以找出那些好事比较久的线程,然后把那些可疑的线程杀掉,没想到这个问题就解决了,可以正常对这行数据进行操作了。

[转]MySQL select into,SQLServer select into - 阿彬 - 博客园

mikel阅读(863)

来源: [转]MySQL select into,SQLServer select into – 阿彬 – 博客园

以下的文章主要介绍的是MySQL select into 与 SQLServer select  into,我们大家都知道MySQL数据库是对Select Into 语句的直接备份表的结构与相关实际应用数据是不支持的,由于工作中的需要在网上找到一种方法可以代替, 也有其它方法可以处理,总结如下:

方法1:

MYSQL不支持:

  1. Select * Into new_table_name from old_table_name

替代方法:

  1. Create table new_table_name (Select * from old_table_name);

方法2:

1.先备份表结构和数据

导出命令 -u用户名 -p密码 -h主机IP地址 数据库名 表名1 > 导出文件.sql

  1. mysqldump -uroot -proot -h192.168.0.88 ok_db oktable2 > ok_db.sql

2.修改备份表的名字

3.登录MySQL

4.选择数据库

5.执行: Source 备份表的路径 如:Source d:\ok_db.sql 回车即可。

6.完成.

SQLServer支持 MySQL Select into语句

1.备份表直接执行如下就可以了。

  1. Select * Into new_table_name from old_table_name;

MySQL Select into outfile用于导出指定的查询数据到文件如下:

1.导出表中所有数据到C盘根目录outfile.txt中如下:

  1. Select * into outfile ‘c:\\outfile.txt’ from test;

2.导出表中指定查询条件2005-06-08号的数据到C盘根目录outfile1.txt中如下:

  1. Select * into outfile ‘c:\\outfile.txt’ from test where beginDate=‘2008-06-08’;

mysql update from 子查询_ni_hao_ya的专栏-CSDN博客

mikel阅读(1081)

来源: mysql update from 子查询_ni_hao_ya的专栏-CSDN博客

这个破问题纠结了我好久,查了mySQL的好多资料,最后还是在别人那里得到结果,特此记录一下,留个备份吧!

msSQL 子查询更新

update log set uin= b.uin
from log a,logs b
where a.accountuin = b.accountuin

mysql 不支持 update 子查询更新

找了半天资料 终于搞定了…

update `log` a inner join `logs` b on a.`accountuin` = b.`accountuin`
set a.`uin` = b.`uin` where a.`accountuin` = b.`accountuin`

这样就能 将查询出来的字段批量更新过去了…

tp 中save保存,数据没修改,保存失败的处理_一份耕耘 一份收货-CSDN博客

mikel阅读(643)

来源: tp 中save保存,数据没修改,保存失败的处理_一份耕耘 一份收货-CSDN博客

$model->save();
if($model->geterror!=”){//判断数据执行有错误;查看tp save流程
$this->error(‘修改失败’);
return false;
}
$model->geterror() 不为空的情况,就是数据保存失败情况,其中不包含数据未修改的情况;
—————————————————————————————-
亲测 ; $model->getError(); 如果SQL语句有问题,也不会报错的;
$model->getDbError(); 如果SQL语句有错误 ,会报错的;
save() 执行失败没有返回值;
执行成功: 有修改,返回1
没修改 ,返回0
参考:
$flag=$match->where(‘id=’.$id)->save($data);
//echo $match->getDbError();
if ($flag ||($flag === 0)){ //如果save执行错误返回空,==也是相等的;要===才能区别开来;
//$id=base64_encode($id);
$json->setErr(0,’添加完成’);//添加成功
$json->Send();
}else{
$json->setErr(10001,’添加失败’);
$json->Send();

————————————————
版权声明:本文为CSDN博主「qq_34242581」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34242581/article/details/52042505

Workerman ThinkPHP5 宝塔 安装Event拓展_九城科技-CSDN博客

mikel阅读(805)

来源: Workerman ThinkPHP5 宝塔 安装Event拓展_九城科技-CSDN博客

Workerman 结合 TP5在宝塔环境下安装Event拓展

操作系统是CentOS7

先用workerman官方给的检查环境的脚本进行检查

curl -Ss http://www.workerman.net/check.php | php

全部显示OK就代表满足workman的运行环境

宝塔默认是关掉了几个PHP的函数的

需要在PHP的配置文件里面去把workman需要用到的函数给注释掉

 

然后后续操作基本一把梭

代码如下

wget https://pecl.php.net/get/event-3.0.2.tgz

tar -zxvf event-3.0.2.tgz

cd event-3.0.2

phpize

./configure –with-php-config=/www/server/php/73/bin/php-config

make && make install

echo “extension=event.so” >> /www/server/php/73/etc/php.ini

service php-fpm-73 reload
需要注意的是/www/server/php/73 为实际PHP所存在的目录

最后启动workerman

php start.php start

这样就启动起来了
————————————————
版权声明:本文为CSDN博主「九城科技」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35422558/article/details/114822862

宝塔利用workerman实现websocket协议 用于微信小程序等应用的WSS通信 – 星空站长网

mikel阅读(1000)

来源: 宝塔利用workerman实现websocket协议 用于微信小程序等应用的WSS通信 – 星空站长网

网上下载的啦啦外卖小程序提示 WSS错误,所以研究起了这个WSS通信。

什么是Workerman

Workerman是一款开源高性能异步PHP socket框架。支持高并发,超高稳定性,被广泛的用于手机app、移动通讯,微信小程序,手游服务端、网络游戏、PHP聊天室、硬件通讯、智能家居、车联网、物联网等领域的开发。 支持TCP长连接,支持Websocket、HTTP等协议,支持自定义协议。拥有异步MySQL、异步Redis、异步Http、MQTT物联网客户端、异步消息队列等众多高性能组件。

相关阅读:Workerman简单开发实例WebSocket教程 、WebSocket在线测试

先根据相关阅读中的开发实例建立好PHP文件。Workerman的主程序和建立好的PHP文件都传到网站根目录。(注意,解压后的主程序删除掉目录后面的-master,不想删就修改建立的PHP文件指向)

运行PHP命令

curl -Ss http://www.workerman.net/check.php | php 检查是否适应 workerman 的环境。

这个时候,提示禁用了某个函数。

利用workerman实现websocket协议 用于微信小程序等应用的WSS通信

但是我在php5.6中已经删除了函数,这个时候唯一一个可能,就是你有多个PHP。

使用 php –ini 查看当前PHP调用的是哪个版本PHP的配置文件。

这里可以看到是调用的7.4的。

利用workerman实现websocket协议 用于微信小程序等应用的WSS通信

那我们进入7.4的配置文件(php.ini)文件将

搜索一下disable_functions

在这行前面加分号(;),或者把提示没有的函数拿掉即可

然后再次运行 curl -Ss http://www.workerman.net/check.php | php 检查是否合适 workerman 的环境。

全显示OK,并且没有报错。

进入PHP执行目录

cd /www/server/php/56/bin

php /home/wwwroot/domin.com/ws_test.php start

利用workerman实现websocket协议 用于微信小程序等应用的WSS通信

以上显示是成功开启服务

为了这个链接,研究了整整3天时间。

在网上看到的用代理方式,是没有用的。因为之前的一些程序没有写在教程中。

layer.js源码分析_极客神殿-CSDN博客_layer源码解析

mikel阅读(601)

来源: layer.js源码分析_极客神殿-CSDN博客_layer源码解析

最近在看layer.js源码,从中得到了一些启发,对于一个框架的设计也有了一定的看法,现在对于这个框架的设计以及其他的问题来说明一下。

layer.js是一个专注于弹出层的框架,这个框架本身可以实现5种弹出层类型,其他的就不多说了,可以去看看它的官网,下面说一下它的主要组织形式:

  1. 首先,这个框架本身就是一个IIFE(立即执行函数表达式),保证了局部环境,避免了全局变量污染的问题
  2. 框架内部主要是三个对象构成,分别是Class构造函数、layer对象、ready对象
  3. 通过window来暴露对外api

以前看过一点JQuery的源码,layer.js的框架结构和JQuery是相同形式,框架内部主要是这三个对象来组成,对于这三个对象上具体的方法以及属性我列举了下,如下图所示:

这里写图片描述

整个框架的结构组织以及脉络还是很清晰的,框架整体的代码量大概1300多行左右,我对这个框架运行的具体流程做了个较为详细的流程,具体流程如下:
这里写图片描述


/*!

 @Title: Layui
 @Description:经典模块化前端框架
 @Site: www.layui.com
 @Author: 贤心
 @License:MIT

 */

;!function(win) {

    "use strict";

    var Lay = function() {
        this.v = '1.0.9_rls'; //版本号
    };

    Lay.fn = Lay.prototype;

    var doc = document,
        config = Lay.fn.cache = {},

        // 获取本js所在目录
        getPath = function() {
            var js = doc.scripts,
                jsPath = js[js.length - 1].src;
            return jsPath.substring(0, jsPath.lastIndexOf('/') + 1);
        }(),

        // 异常提示
        error = function(msg) {
            win.console && console.error && console.error('Layui hint: ' + msg);
        },

        // 检测opera环境
        isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]',

        // 内置模块
        modules = {
            layer : 'modules/layer', //弹层
            laydate : 'modules/laydate', //日期
            laypage : 'modules/laypage', //分页
            laytpl : 'modules/laytpl', //模板引擎
            layim : 'modules/layim', //web通讯
            layedit : 'modules/layedit', //富文本编辑器
            form : 'modules/form', //表单集
            upload : 'modules/upload', //上传
            tree : 'modules/tree', //树结构
            table : 'modules/table', //富表格
            element : 'modules/element', //常用元素操作
            util : 'modules/util', //工具块
            flow : 'modules/flow', //流加载
            carousel : 'modules/carousel', //轮播
            code : 'modules/code', //代码修饰器
            jquery : 'modules/jquery', //DOM库(第三方)  
            mobile : 'modules/mobile', //移动大模块 | 若当前为开发目录,则为移动模块入口,否则为移动模块集合
            'layui.all' : 'dest/layui.all' //PC模块合并版
        };

    config.modules = {}; //记录模块物理路径
    config.status = {}; // 记录已注册的模块集。
    config.timeout = 10; //符合规范的模块请求最长等待秒数
    config.event = {}; //记录模块自定义事件

    // 定义模块
    Lay.fn.define = function(deps, callback) {
        var that = this,
            type = typeof deps === 'function',
            mods = function() {
                // 参数callback,可选,用于回调。
                // 回调参数function,用于回调时,注册模块。
                typeof callback === 'function' && callback(function(app, exports) {
                    // 回调参数function的参数app,必要,代表模块名。
                    // 回调参数function的参数exports,必要,代表模块的接口方法。
                    layui[app] = exports;
                    // config.status,记录已注册的模块集。
                    config.status[app] = true;
                });
                return this;
            };

        // 参数deps,代表依赖的模块集,可选。
        type && (
            callback = deps,
            deps = []
        );

        // 相当于layui['layui.all'] || layui['layui.mobile']
        // 模块名layui.all,代表所有模块。
        // 模块名layui.mobile,代表手机版的所有模块。
        // 如果已经加载所有模块,则直接执行回调。
        if (layui['layui.all'] || (!layui['layui.all'] && layui['layui.mobile'])) {
            return mods.call(that);
        }

        // 方法layui.use,动态加载所依赖的模块集deps。
        that.use(deps, mods);
        return that;
    };

    // 动态加载模块集
    Lay.fn.use = function(apps, callback, exports) {
        var that = this,
        // config.dir,内置文件的基目录,默认值为layui.js的所在目录,需以斜杠结束。
            dir = config.dir = config.dir ? config.dir : getPath;
        var head = doc.getElementsByTagName('head')[0];

        // 参数apps,必要,可以是字符串或数组。
        apps = typeof apps === 'string' ? [ apps ] : apps;

        // 参数apps中存在jquery时,如果页面已加载jQuery1.7+库,则直接使用该库。
        if (window.jQuery && jQuery.fn.on) {
            that.each(apps, function(index, item) {
                if (item === 'jquery') {
                    apps.splice(index, 1);
                }
            });
            layui.jquery = jQuery;
        }

        var item = apps[0],
            timeout = 0;
        // 参数exports,可选。
        exports = exports || [];

        // config.host,格式为“//.../”,默认值为config.dir中的主机,或当前页面的主机。
        config.host = config.host || (dir.match(/\/\/([\s\S]+?)\//)/* 匹配“//.../” */ || [ '//' + location.host + '/' ])[0];

        // apps.length === 0 || (layui['layui.all'] || layui['layui.mobile']) && modules[item]
        // 参数apps,允许为空集。
        // 如果需要加载的模块集为空集,则执行回调。
        // 模块名layui.all,代表所有模块。
        // 模块名layui.mobile,代表手机版的所有模块。
        // modules,代表layui的内置模块集。
        // 如果已经加载所有模块,并且当前模块是layui的内置模块,则当前模块不需要加载。
        if (apps.length === 0
            || (layui['layui.all'] && modules[item])
            || (!layui['layui.all'] && layui['layui.mobile'] && modules[item])
        ) {
            return onCallback(), that;
        }

        // 用于监听文件加载完毕
        function onScriptLoad(e, url) {
            var readyRegExp = navigator.platform === 'PLaySTATION 3' ? /^complete$/ : /^(complete|loaded)$/
            if (e.type === 'load' || (readyRegExp.test((e.currentTarget || e.srcElement).readyState))) {
                config.modules[item] = url;
                head.removeChild(node);
                // 轮询查看当前模块是否已注册,每0.025秒轮询一次,共论询config.timeout秒。
                // config.timeout,文件加载超时,默认值为10秒。
                (function poll() {
                    if (++timeout > config.timeout * 1000 / 4) {
                        return error(item + ' is not a valid module');
                    };
                    config.status[item] ? onCallback() : setTimeout(poll, 4);
                }());
            }
        }

        var node = doc.createElement('script'),
        // config.base,代表扩展模块的JS文件目录,默认值为空串,需要以斜杠结束。
        // modules,代表layui的内置模块集。
        // layui.modules[name],代表模块name的相对路径(不包括后缀.js),默认值为name。
        //         如果当前模块是内置模块,则相对路径相对于config.dir + "lay/"。
        //        如果当前模块是扩展模块,则相对路径相对于config.base。
            url = (modules[item] ? (dir + 'lay/') : (config.base || '')) + (that.modules[item] || item) + '.js';
        node.async = true;
        node.charset = 'utf-8';
        node.src = url + function() {
            // config.version=true时,使用config.v作为版本号,否则自己作为版本号,默认值不启用版本号。
            // config.v,代表版本号,默认值为当前时间。
            // config.version=true,config.v不设置时,使流览器不会加载缓存文件,而是重新加载。
            var version = config.version === true ? (config.v || (new Date()).getTime()) : (config.version || '');
            return version ? ('?v=' + version) : '';
        }();

        // config.modules[name],代表已加载,或正在加载中的模块name的相对路径(不包括后缀.js)。
        if (!config.modules[item]) {
            head.appendChild(node);
            if (node.attachEvent && !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && !isOpera) {
                node.attachEvent('onreadystatechange', function(e) {
                    onScriptLoad(e, url);
                });
            } else {
                node.addEventListener('load', function(e) {
                    onScriptLoad(e, url);
                }, false);
            }
        } else {
            // 轮询查看是否加载完毕,每0.025秒轮询一次,共论询config.timeout秒。
            // config.timeout,文件加载超时,默认值为10秒。
            (function poll() {
                if (++timeout > config.timeout * 1000 / 4) {
                    return error(item + ' is not a valid module');
                };
                // config.status,记录已注册的模块集。
                (typeof config.modules[item] === 'string' && config.status[item]) ? onCallback() : setTimeout(poll, 4);
            }());
        }
        config.modules[item] = url;

        //回调
        function onCallback() {
            // 参数exports,记录模块的接口。
            exports.push(layui[item]);
            // 加载下一个模块,如果没有下一个,则执行回调。
            apps.length > 1 ? that.use(apps.slice(1), callback, exports) : (typeof callback === 'function' && callback.apply(layui, exports));
        }

        return that;

    };

    // 获取节点的style属性值
    Lay.fn.getStyle = function(node, name) {
        var style = node.currentStyle ? node.currentStyle : win.getComputedStyle(node, null);
        return style[style.getPropertyValue ? 'getPropertyValue' : 'getAttribute'](name);
    };

    // 动态加载CSS
    Lay.fn.link = function(href, fn, cssname) {
        var that = this,
            link = doc.createElement('link');
        var head = doc.getElementsByTagName('head')[0];

        // 参数fn,可选。
        if (typeof fn === 'string')
            cssname = fn;

        // 参数cssname,用于标识CSS文件的ID,默认值为href。
        var app = (cssname || href).replace(/\.|\//g, '');
        var id = link.id = 'layuicss-' + app,
            timeout = 0;

        link.rel = 'stylesheet';
        // config.debug=true时,使流览器不会加载缓存文件。
        link.href = href + (config.debug ? '?v=' + new Date().getTime() : '');
        link.media = 'all';

        // 参数cssname,同一ID的CSS文件的只许加载一次。
        if (!doc.getElementById(id)) {
            head.appendChild(link);
        }

        // 参数fn,用于监听CSS加载完毕。
        if (typeof fn !== 'function') return;
        // 轮询查看是否加载完毕,每0.1秒轮询一次,共论询config.timeout秒。
        (function poll() {
            if (++timeout > config.timeout * 1000 / 100) {
                return error(href + ' timeout');
            };
            parseInt(that.getStyle(doc.getElementById(id), 'width')) === 1989 ? function() {
                fn();
            }() : setTimeout(poll, 100);
        }());
    };

    // css内部加载器
    Lay.fn.addcss = function(firename, fn, cssname) {
        // 全局配置dir,用于内置文件的基目录,默认值为layui.js所在的目录,需要以斜杠结束。
        layui.link(config.dir + 'css/' + firename, fn, cssname);
    };

    // 图片预加载
    Lay.fn.img = function(url, callback, error) {
        var img = new Image();
        img.src = url;
        if (img.complete) {
            return callback(img);
        }
        img.onload = function() {
            img.onload = null;
            callback(img);
        };
        img.onerror = function(e) {
            img.onerror = null;
            error(e);
        };
    };

    // 全局配置
    Lay.fn.config = function(options) {
        options = options || {};
        for (var key in options) {
            config[key] = options[key];
        }
        return this;
    };

    // layui.modules[name],代表模块name的相对路径(不包括后缀.js),默认值为name。
    Lay.fn.modules = function() {
        var clone = {};
        for (var o in modules) {
            clone[o] = modules[o];
        }
        return clone;
    }();

    // 设置模块的相对路径(不含后缀.js)
    Lay.fn.extend = function(options) {
        var that = this;

        options = options || {};
        for (var o in options) {
            // layui[name],如果存在,则表示模块name已注册。
            // layui.modules[name],代表模块name的相对路径(不包括后缀.js),默认值为name。
            // 已注册或已设置相对路径的模块集,不允许再设置相对路径。显然,内置模块的相对路径不允许更改。
            if (that[o] || that.modules[o]) {
                error('\u6A21\u5757\u540D ' + o + ' \u5DF2\u88AB\u5360\u7528');
            } else {
                that.modules[o] = options[o];
            }
        }
        return that;
    };

    // 路由
    Lay.fn.router = function(hash) {
        var hashs = (hash || location.hash).replace(/^#/, '').split('/') || [];
        var item,
            param = {
                dir : []
            };
        for (var i = 0; i < hashs.length; i++) {
            item = hashs[i].split('=');
            /^\w+=/.test(hashs[i]) ? function() {
                if (item[0] !== 'dir') {
                    param[item[0]] = item[1];
                }
            }() : param.dir.push(hashs[i]);
            item = null;
        }
        return param;
    };

    // 本地存储
    Lay.fn.data = function(table, settings) {
        table = table || 'layui';

        if (!win.JSON || !win.JSON.parse) return;

        //如果settings为null,则删除表
        if (settings === null) {
            return delete localStorage[table];
        }

        settings = typeof settings === 'object'
            ? settings
            : {
                key : settings
            };

        try {
            var data = JSON.parse(localStorage[table]);
        } catch (e) {
            var data = {};
        }

        if (settings.value)
            data[settings.key] = settings.value;
        if (settings.remove)
            delete data[settings.key];
        localStorage[table] = JSON.stringify(data);

        return settings.key ? data[settings.key] : data;
    };

    // 设备信息
    Lay.fn.device = function(key) {
        var agent = navigator.userAgent.toLowerCase();

        //获取版本号
        var getVersion = function(label) {
            var exp = new RegExp(label + '/([^\\s\\_\\-]+)');
            label = (agent.match(exp) || [])[1];
            return label || false;
        };

        var result = {
            os : function() { //底层操作系统
                if (/windows/.test(agent)) {
                    return 'windows';
                } else if (/linux/.test(agent)) {
                    return 'linux';
                } else if (/iphone|ipod|ipad|ios/.test(agent)) {
                    return 'ios';
                }
            }(),
            ie : function() { //ie版本
                return (!!win.ActiveXObject || "ActiveXObject" in win) ? (
                    (agent.match(/msie\s(\d+)/) || [])[1] || '11' //由于ie11并没有msie的标识
                    ) : false;
            }(),
            weixin : getVersion('micromessenger') //是否微信
        };

        //任意的key
        if (key && !result[key]) {
            result[key] = getVersion(key);
        }

        //移动设备
        result.android = /android/.test(agent);
        result.ios = result.os === 'ios';

        return result;
    };

    // 提示
    Lay.fn.hint = function() {
        return {
            error : error
        }
    };

    // 遍历
    Lay.fn.each = function(obj, fn) {
        var that = this,
            key;
        if (typeof fn !== 'function') return that;
        obj = obj || [];
        if (obj.constructor === Object) {
            for (key in obj) {
                if (fn.call(obj[key], key, obj[key])) break;
            }
        } else {
            for (key = 0; key < obj.length; key++) {
                if (fn.call(obj[key], key, obj[key])) break;
            }
        }
        return that;
    };

    // 阻止事件冒泡
    Lay.fn.stope = function(e) {
        e = e || win.event;
        e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true;
    };

    // 自定义模块事件
    Lay.fn.onevent = function(modName, events, callback) {
        if (typeof modName !== 'string'
            || typeof callback !== 'function') return this;
        config.event[modName + '.' + events] = [ callback ];

        //不再对多次事件监听做支持
        /*
        config.event[modName + '.' + events] 
          ? config.event[modName + '.' + events].push(callback) 
        : config.event[modName + '.' + events] = [callback];
        */

        return this;
    };

    // 执行自定义模块事件
    Lay.fn.event = function(modName, events, params) {
        var that = this,
            result = null,
            filter = events.match(/\(.*\)$/) || []; //提取事件过滤器
        var set = (events = modName + '.' + events).replace(filter, ''); //获取事件本体名
        var callback = function(_, item) {
            var res = item && item.call(that, params);
            res === false && result === null && (result = false);
        };
        layui.each(config.event[set], callback);
        filter[0] && layui.each(config.event[events], callback); //执行过滤器中的事件
        return result;
    };

    win.layui = new Lay();

}(window);

使用 Redis 实现一个轻量级的搜索引擎,牛逼啊! - 入她程序员 - 博客园

mikel阅读(710)

来源: 使用 Redis 实现一个轻量级的搜索引擎,牛逼啊! – 入她程序员 – 博客园

场景

大家如果是做后端开发的,想必都实现过列表查询的接口,当然有的查询条件很简单,一条 SQL 就搞定了,但有的查询条件极其复杂,再加上库表中设计的各种不合理,导致查询接口特别难写,然后加班什么的就不用说了(不知各位有没有这种感受呢~)。
下面以一个例子开始,这是某购物网站的搜索条件,如果让你实现这样的一个搜索接口,你会如何实现?(当然你说借助搜索引擎,像 Elasticsearch 之类的,你完全可以实现。但我这里想说的是,如果要你自己实现呢?)

从上图中可以看出,搜索总共分为6大类,每大类中又分了各个子类。这中间,各大类条件之间是取的交集,各子类中有单选、多选、以及自定义的情况,最终输出符合条件的结果集。
好了,既然需求很明确了,我们就开始来实现。关注公众号Java技术栈回复面试,可以获取整理的 Redis 系列面试题及全部答案。
实现1

率先登场是小A同学,他是写 SQL 方面的“专家”。小A信心满满的说:“不就是一个查询接口吗?看着条件很多,但凭着我丰富的 SQL 经验,这点还是难不倒我的。”
于是乎就写出了下面这段代码(这里以 MYSQL 为例):
select … from table_1
left join table_2
left join table_3
left join (select … from table_x where …) tmp_1

where …
order by …
limit m,n
代码在测试环境跑了一把,结果好像都匹配上了,于是准备上预发。这一上预发,问题就开始暴露出来。
预发为了尽可能的逼真线上环境,所以数据量自然而然要比测试大的多。所以这么一个复杂的 SQL,它的执行效率可想而知。测试同学果断把小A的代码给打了回来。
实现2

总结了小A失败的教训,小B开始对SQL进行了优化,先是通过了explain关键字进行SQL性能分析,对该加索引的地方都加上了索引。同时将一条复杂SQL拆分成了多条SQL,计算结果在程序内存中进行计算。这篇Explain 最完整总结,推荐看下。
伪代码如下:
$result_1 = query(‘select … from table_1 where …’);
$result_2 = query(‘select … from table_2 where …’);
$result_3 = query(‘select … from table_3 where …’);

$result = array_intersect($result_1, $result_2, $result_3, …);
这种方案从性能上明显比第一种要好很多,可是在功能验收的时候,产品经理还是觉得查询速度不够快。MySQL 实现一个简单版搜索引擎,这篇推荐看下。
小B自己也知道,每次查询都会向数据库查询多次,而且有些历史原因,部分条件是做不到单表查询的,所以查询等待的时间是避免不了的。
实现3

小C从上面的方案中看到了优化的空间。他发现小B在思路上是没问题的,将复杂条件拆分,计算各个子维度的结果集,最后将所有的子结果集进行一个汇总合并,得到最终想要的结果。
于是他突发奇想,能否事先将各个子维度的结果集给缓存起来,这要查询的时候直接去取想要的子集,而不用每次去查库计算。
这里小C采用 Redis 来存储缓存数据,用它的主要原因是,它提供了多种数据结构,并且在 Redis 中进行集合的交并集操作是一件很容易的事情。
具体方案,如图所示:

这里每个条件都事先将计算好的结果集ID存入对应的key中,选用的数据结构是集合(Set)。查询操作包括:
子类单选:直接根据条件 key,获取对应结果集;
子类多选:根据多个条件 Key,进行并集操作,获取对应结果集;
最终结果:将获取的所有子类结果集进行交集操作,得到最终结果;
这其实就是所谓的反向索引。
这里会发现,漏了一个价格的条件。从需求中可知,价格条件是个区间,并且是无穷举的。所以上述的这种穷举条件的 Key-Value 方式是做不到的。这里我们采用 Redis 的另一种数据结构进行实现,有序集合(Sorted Set):

将所有商品加入 Key 为价格的有序集合中,值为商品ID,每个值对应的分数为商品价格的数值。这样在 Redis 的有序集合中就可以通过ZRANGEBYSCORE命令,根据分数(价格)区间,获取相应结果集。
至此,方案三的优化已全部结束,将数据的查询与计算通过缓存的手段,进行了分离。在每次查找时,只需要简单的查找 Redis 几次就能得出结果。查询速度上符合了验收的要求。
扩展

分页
这里你或许发现了一个严重的功能缺陷,列表查询怎么能没有分页。是的,我们马上来看 Redis 是如何实现分页的。关注公众号Java技术栈回复面试,可以获取整理的 Redis 系列面试题及全部答案。
分页主要涉及排序,这里简单起见,就以创建时间为例。
如图所示:

图中蓝色部分是以创建时间为分值的商品有序集合,蓝色下方的结果集即为条件计算而得的结果,通过ZINTERSTORE命令,赋结果集权重为0,商品时间结果为1,取交集而得的结果集赋予创建时间分值的新有序集合。对新结果集的操作即能得到分页所需的各个数据:
页面总数为:ZCOUNT命令
当前页内容:ZRANGE命令
若以倒序排列:ZREVRANGE命令
数据更新
关于索引数据更新的问题,有两种方式来进行。一种是通过商品数据的修改,来即时触发更新操作,一种是通过定时脚本来进行批量更新。
这里要注意的是,关于索引内容的更新,如果暴力的删除 Key,再重新设置 Key。因为 Redis 中两个操作不会是原子性进行的,所以中间可能存在空白间隙,建议采用仅移除集合中失效元素,添加新元素的方式进行。
性能优化
Redis 是内存级操作,所以单次的查询会很快。但是如果我们的实现中会进行多次的 Redis 操作,Redis 的多次连接时间可能是不必要时间消耗。通过使用MULTI命令,开启一个事务,将 Redis 的多次操作放在一个事务中,最后通过EXEC来进行原子性执行(注意:这里所谓的事务,只是将多个操作在一次连接中执行,如果执行过程中遇到失败,是不会回滚的)。
总结

这里只是一个采用 Redis 优化查询搜索的一个简单 Demo,和现有的开源搜索引擎相比,它更轻量,学习成本页相应低些。其次,它的一些思想与开源搜索引擎是类似的,如果再加上词语解析,也可以实现类似全文检索的功能。
总结了一些2020年的面试题,这份面试题的包含的模块分为19个模块,分别是: Java 基础、容器、多线程、反射、对象拷贝、Java Web 、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、MySQL、Redis、JVM 。

获取资料以上资料:关注公众号:有故事的程序员,获取学习资料。
记得点个关注+评论哦~