负载均衡服务器(网关服务器)在操作系统内核获取网络数据包,根据负载均衡算法计算得到一台真实 Web 服务器 10.0.0.1,然后将目的 IP 地址修改为 10.0.0.1,不需要通过用户进程。真实 Web 服务器处理完成后,响应数据包回到负载均衡服务器,负载均衡服务器再将数据包原地址修改为自身的 IP 地址(114.100.80.10)发送给浏览器。
IP 负载均衡在内核完成数据分发,所以处理性能优于反向代理负载均衡。但是因为所有请求响应都要经过负载均衡服务器,集群的最大响应数据吞吐量受制于负载均衡服务器网卡带宽。
数据链路层负载均衡
数据链路层负载均衡是指在通信协议的数据链路层修改 mac 地址进行负载均衡。
这种方式又称作三角传输方式,负载均衡数据分发过程中不修改 IP 地址,只修改目的 mac 地址,通过配置真实物理服务器集群所有机器虚拟 IP 和负载均衡服务器 IP 地址一致,从而达到不修改数据包的源地址和目的地址就可以进行数据分发的目的,由于实际处理请求的真实物理服务器 IP 和数据请求目的 IP 一致,不需要通过负载均衡服务器进行地址转换,可将响应数据包直接返回给用户浏览器,避免负载均衡服务器网卡带宽成为瓶颈。这种负载方式又称作直接路由方式。
在 Linux 平台上最好的链路层负载均衡开源产品是 **LVS(Linux Virtual Server)**。
减库存在数据一致性上,主要就是保证大并发请求时库存数据不能为负数,也就是要保证数据库中的库存字段值不能为负数,一般我们有多种解决方案:一种是在应用程序中通过事务来判断,即保证减后库存不能为负数,否则就回滚;另一种办法是直接设置数据库的字段数据为无符号整数,这样减后库存字段值小于零时会直接执行 SQL 语句来报错;再有一种就是使用 CASE WHEN 判断语句,例如这样的 SQL 语句:
1
UPDATE item SET inventory = CASEWHEN inventory >= xxx THEN inventory-xxx ELSE inventory END
Window -> Preferences -> Web -> HTML Files -> Editor -> Content Assist -> Auto-Activation
保存后,我们再来输入看看,感觉真是不错呀:
插件安装
很多教科书上说到 Eclipse 的插件安装都是通过 Help -> Install New SoftWare 这种自动检索的方式,操作起来固然是方便,不过当我们不需要某种插件时不太容易找到要删除哪些内容,而且以后 Eclipse 版本升级的时候,通过这种方式安装过的插件都得再重新装一次。另外一种通过 Link 链接方式,就可以解决这些问题。
defprint_func1(): a = 17# 局部变量 print("in print_func a = ", a) defprint_func2(): print("in print_func a = ", a) print_func1() print_func2() print("a = ", a)
以上实例运行结果如下:
1 2 3
in print_func a = 17 in print_func a = 4 a = 4
关键字参数
函数也可以使用 kwarg=value 的关键字参数形式被调用.例如,以下函数:
1 2 3 4 5
defparrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'): print("-- This parrot wouldn't", action, end=' ') print("if you put", voltage, "volts through it.") print("-- Lovely plumage, the", type) print("-- It's", state, "!")
parrot()# required argument missing parrot(voltage=5.0, 'dead')# non-keyword argument after a keyword argument parrot(110, voltage=220)# duplicate value for the same argument parrot(actor='John Cleese')# unknown keyword argument
try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise finally: # 清理行为
抛出异常
Python 使用 raise 语句抛出一个指定的异常。例如:
1 2 3 4
>>> raise NameError('HiThere') Traceback (most recent call last): File "<stdin>", line 1, in ? NameError: HiThere
classError(Exception): """Base class for exceptions in this module.""" pass
classInputError(Error): """Exception raised for errors in the input. Attributes: expression -- input expression in which the error occurred message -- explanation of the error """
classTransitionError(Error): """Raised when an operation attempts a state transition that's not allowed. Attributes: previous -- state at beginning of transition next -- attempted new state message -- explanation of why the specific transition is not allowed """
>>> sys.stderr.write('Warning, log file not found starting a new one\n') Warning, log file not found starting a new one
字符串正则匹配
re 模块为高级字符串处理提供了正则表达式工具。对于复杂的匹配和处理,正则表达式提供了简洁、优化的解决方案:
1 2 3 4 5
>>> import re >>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest') ['foot', 'fell', 'fastest'] >>> re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat') 'cat in the hat'
privatevoidgrow(int minCapacity) { // overflow-conscious code intoldCapacity= elementData.length; intnewCapacity= oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
ArrayList 在每一次有效的删除操作后,都要进行数组的重组,并且删除的元素位置越靠前,数组重组的开销就越大。具体来说,ArrayList 会**调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上。
1 2 3 4 5 6 7 8 9 10 11 12 13
public E remove(int index) { rangeCheck(index);
modCount++; EoldValue= elementData(index);
intnumMoved= size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work
privatevoidwriteObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff intexpectedModCount= modCount; s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone() s.writeInt(size);
// Write out all elements in the proper order. for (int i=0; i<size; i++) { s.writeObject(elementData[i]); }
if (modCount != expectedModCount) { thrownewConcurrentModificationException(); } }
if (index < (size >> 1)) { Node<E> x = first; for (inti=0; i < index; i++) x = x.next; return x; } else { Node<E> x = last; for (inti= size - 1; i > index; i--) x = x.prev; return x; } }
privatevoidlinkFirst(E e) { final Node<E> f = first; final Node<E> newNode = newNode<>(null, e, f); first = newNode; if (f == null) last = newNode; else f.prev = newNode; size++; modCount++; }
voidlinkLast(E e) { final Node<E> l = last; final Node<E> newNode = newNode<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }
voidlinkBefore(E e, Node<E> succ) { // assert succ != null; final Node<E> pred = succ.prev; final Node<E> newNode = newNode<>(pred, e, succ); succ.prev = newNode; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; }
算法如下:
将新添加的数据包装为 Node;
如果往头部添加元素,将头指针 first 指向新的 Node,之前的 first 对象的 prev 指向新的 Node。
如果是向尾部添加元素,则将尾指针 last 指向新的 Node,之前的 last 对象的 next 指向新的 Node。
publicbooleanremove(Object o) { if (o == null) { // 遍历找到要删除的元素节点 for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) { unlink(x); returntrue; } } } else { // 遍历找到要删除的元素节点 for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) { unlink(x); returntrue; } } } returnfalse; }
E unlink(Node<E> x) { // assert x != null; finalEelement= x.item; final Node<E> next = x.next; final Node<E> prev = x.prev;
if (prev == null) { first = next; } else { prev.next = next; x.prev = null; }
if (next == null) { last = prev; } else { next.prev = prev; x.next = null; }
SELECT cust_name, cust_contact FROM customers WHERE cust_id IN (SELECT cust_id FROM orders WHERE order_num IN (SELECT order_num FROM orderitems WHERE prod_id ='RGAN01'));
WHERE 子句
在 SQL 语句中,数据根据 WHERE 子句中指定的搜索条件进行过滤。
WHERE 子句的基本格式如下:
1
SELECT ……(列名) FROM ……(表名) WHERE ……(子句条件)
WHERE 子句用于过滤记录,即缩小访问数据的范围。WHERE 后跟一个返回 true 或 false 的条件。
WHERE 可以与 SELECT,UPDATE 和 DELETE 一起使用。
SELECT 语句中的 WHERE 子句
1 2
SELECT*FROM Customers WHERE cust_name ='Kids Place';
UPDATE 语句中的 WHERE 子句
1 2 3
UPDATE Customers SET cust_name ='Jack Jones' WHERE cust_name ='Kids Place';
DELETE 语句中的 WHERE 子句
1 2
DELETEFROM Customers WHERE cust_name ='Kids Place';
可以在 WHERE 子句中使用的操作符:
比较操作符
运算符
描述
=
等于
<>
不等于。注释:在 SQL 的一些版本中,该操作符可被写成 !=
>
大于
<
小于
>=
大于等于
<=
小于等于
范围操作符
运算符
描述
BETWEEN
在某个范围内
IN
指定针对某个列的多个可能值
IN 操作符在 WHERE 子句中使用,作用是在指定的几个特定值中任选一个值。
BETWEEN 操作符在 WHERE 子句中使用,作用是选取介于某个范围内的值。
IN 示例
1 2 3
SELECT* FROM products WHERE vend_id IN ('DLL01', 'BRS01');
BETWEEN 示例
1 2 3
SELECT* FROM products WHERE prod_price BETWEEN3AND5;
逻辑操作符
运算符
描述
AND
并且(与)
OR
或者(或)
NOT
否定(非)
AND、OR、NOT 是用于对过滤条件的逻辑处理指令。
AND 优先级高于 OR,为了明确处理顺序,可以使用 ()。AND 操作符表示左右条件都要满足。
OR 操作符表示左右条件满足任意一个即可。
NOT 操作符用于否定一个条件。
AND 示例
1 2 3
SELECT prod_id, prod_name, prod_price FROM products WHERE vend_id ='DLL01'AND prod_price <=4;
OR 示例
1 2 3
SELECT prod_id, prod_name, prod_price FROM products WHERE vend_id ='DLL01'OR vend_id ='BRS01';
NOT 示例
1 2 3
SELECT* FROM products WHERE prod_price NOTBETWEEN3AND5;
通配符
运算符
描述
LIKE
搜索某种模式
%
表示任意字符出现任意次数
_
表示任意字符出现一次
[]
必须匹配指定位置的一个字符
LIKE 操作符在 WHERE 子句中使用,作用是确定字符串是否匹配模式。只有字段是文本值时才使用 LIKE。
LIKE 支持以下通配符匹配选项:
% 表示任何字符出现任意次数。
_ 表示任何字符出现一次。
[] 必须匹配指定位置的一个字符。
注意:不要滥用通配符,通配符位于开头处匹配会非常慢。
% 示例:
1 2 3
SELECT prod_id, prod_name, prod_price FROM products WHERE prod_name LIKE'%bean bag%';
_ 示例:
1 2 3
SELECT prod_id, prod_name, prod_price FROM products WHERE prod_name LIKE'__ inch teddy bear';
“左外连接”会获取左表所有记录,即使右表没有对应匹配的记录。左外连接使用 LEFT JOIN 关键字。
1 2 3
SELECT customers.cust_id, orders.order_num FROM customers LEFTJOIN orders ON customers.cust_id = orders.cust_id;
右连接(RIGHT JOIN)
“右外连接”会获取右表所有记录,即使左表没有对应匹配的记录。右外连接使用 RIGHT JOIN 关键字。
1 2 3
SELECT customers.cust_id, orders.order_num FROM customers RIGHTJOIN orders ON customers.cust_id = orders.cust_id;
组合(UNION)
UNION 运算符将两个或更多查询的结果组合起来,并生成一个结果集,其中包含来自 UNION 中参与查询的提取行。
UNION 基本规则:
所有查询的列数和列顺序必须相同。
每个查询中涉及表的列的数据类型必须相同或兼容。
通常返回的列名取自第一个查询。
默认会去除相同行,如果需要保留相同行,使用 UNION ALL。
只能包含一个 ORDER BY 子句,并且必须位于语句的最后。
应用场景:
在一个查询中从不同的表返回结构数据。
对一个表执行多个查询,按一个查询返回数据。
组合查询示例:
1 2 3 4 5 6 7
SELECT cust_name, cust_contact, cust_email FROM customers WHERE cust_state IN ('IL', 'IN', 'MI') UNION SELECT cust_name, cust_contact, cust_email FROM customers WHERE cust_name ='Fun4All';
JOIN vs UNION
JOIN 中连接表的列可能不同,但在 UNION 中,所有查询的列数和列顺序必须相同。
UNION 将查询之后的行放在一起(垂直放置),但 JOIN 将查询之后的列放在一起(水平放置),即它构成一个笛卡尔积。
函数
🔔 注意:不同数据库的函数往往各不相同,因此不可移植。本节主要以 Mysql 的函数为例。
字符串函数
函数
说明
CONCAT()
合并字符串
LEFT()、RIGHT()
左边或者右边的字符
LOWER()、UPPER()
转换为小写或者大写
LTRIM()、RTIM()
去除左边或者右边的空格
LENGTH()
长度
SOUNDEX()
转换为语音值
其中, SOUNDEX() 可以将一个字符串转换为描述其语音表示的字母数字模式。
1 2 3
SELECT* FROM mytable WHERE SOUNDEX(col1) = SOUNDEX('apple')
时间函数
日期格式:YYYY-MM-DD
时间格式:HH:MM:SS
函 数
说 明
ADDDATE()
增加一个日期(天、周等)
ADDTIME()
增加一个时间(时、分等)
CURRENT_DATE()
返回当前日期
CURRENT_TIME()
返回当前时间
DATE()
返回日期时间的日期部分
DATEDIFF()
计算两个日期之差
DATE_ADD()
高度灵活的日期运算函数
DATE_FORMAT()
返回一个格式化的日期或时间串
DAY()
返回一个日期的天数部分
DAYOFWEEK()
对于一个日期,返回对应的星期几
HOUR()
返回一个时间的小时部分
MINUTE()
返回一个时间的分钟部分
MONTH()
返回一个日期的月份部分
NOW()
返回当前日期和时间
SECOND()
返回一个时间的秒部分
TIME()
返回一个日期时间的时间部分
YEAR()
返回一个日期的年份部分
1 2
mysql>SELECT NOW(); 2018-4-1420:25:11
数学函数
常见 Mysql 数学函数:
函数
说明
ABS()
取绝对值
MOD()
取余
ROUND()
四舍五入
...
聚合函数
函 数
说 明
AVG()
返回某列的平均值
COUNT()
返回某列的行数
MAX()
返回某列的最大值
MIN()
返回某列的最小值
SUM()
返回某列值之和
AVG() 会忽略 NULL 行。
使用 DISTINCT 可以让汇总函数值汇总不同的值。
1 2
SELECTAVG(DISTINCT col1) AS avg_col FROM mytable
转换函数
函 数
说 明
示例
CAST()
转换数据类型
SELECT CAST("2017-08-29" AS DATE); -> 2017-08-29
事务
不能回退 SELECT 语句,回退 SELECT 语句也没意义;也不能回退 CREATE 和 DROP 语句。
MySQL 默认采用隐式提交策略(autocommit),每执行一条语句就把这条语句当成一个事务然后进行提交。当出现 START TRANSACTION 语句时,会关闭隐式提交;当 COMMIT 或 ROLLBACK 语句执行后,事务会自动关闭,重新恢复隐式提交。
通过 set autocommit=0 可以取消自动提交,直到 set autocommit=1 才会提交;autocommit 标记是针对每个连接而不是针对服务器的。
事务处理指令:
START TRANSACTION - 指令用于标记事务的起始点。
SAVEPOINT - 指令用于创建保留点。
ROLLBACK TO - 指令用于回滚到指定的保留点;如果没有设置保留点,则回退到 START TRANSACTION 语句处。
COMMIT - 提交事务。
RELEASE SAVEPOINT:删除某个保存点。
SET TRANSACTION:设置事务的隔离级别。
事务处理示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
-- 开始事务 START TRANSACTION;
-- 插入操作 A INSERTINTO `user` VALUES (1, 'root1', 'root1', 'xxxx@163.com');
-- 创建保留点 updateA SAVEPOINT updateA;
-- 插入操作 B INSERTINTO `user` VALUES (2, 'root2', 'root2', 'xxxx@163.com');
DROPPROCEDURE IF EXISTS `proc_adder`; DELIMITER ;; CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_adder`(IN a int, IN b int, OUT sum int) BEGIN DECLARE c int; if a isnullthenset a =0; end if;
DELIMITER $ CREATEPROCEDURE getTotal() BEGIN DECLARE total INT; -- 创建接收游标数据的变量 DECLARE sid INT; DECLARE sname VARCHAR(10); -- 创建总数变量 DECLARE sage INT; -- 创建结束标志变量 DECLARE done INTDEFAULTfalse; -- 创建游标 DECLARE cur CURSORFORSELECT id,name,age from cursor_table where age>30; -- 指定游标循环结束时的返回值 DECLARE CONTINUE HANDLER FORNOT FOUND SET done =true; SET total =0; OPEN cur; FETCH cur INTO sid, sname, sage; WHILE(NOT done) DO SET total = total +1; FETCH cur INTO sid, sname, sage; END WHILE;